和往常一样,系统为 debian,让我们开始吧。

1. Nginx 安装和配置

Nginx官网文档
https://nginx.org/en/docs/

Nginx(发音为"engine-x")是一个高性能的开源 Web 服务器,也可以用作反向代理服务器、负载均衡器和 HTTP 缓存。

1.1 安装

(1)安装Nginx

# debian
sudo apt update
sudo apt install nginx

# centos
sudo yum update
sudo yum install nginx

关于Nginx的一些命令和配置文件的位置介绍

在安装过程中可以看到 配置文件的位置在 /etc/nginx
当然不同的安装方式也可能不一样,可以通过 whereis nginx来搜索

1.2 配置文件

通过包管理工具安装的Nginx一般在 /etc/nginx

其中有一个 nginx.conf 文件,它就是Nginx的配置文件,但是为了方便管理,所以我一般不直接修改该文件,而是在 /etc/nginx/conf.d/ 下新建文件。

在默认的 nginx.confhttp模块下,有这样一条命令 include /etc/nginx/conf.d/*.conf; 它表示包含了 /etc/nginx/conf.d/下所有文件,所以我们在/etc/nginx/conf.d/直接添加新文件即可,如果没有该命令需要手动把文件的路径添加到 nginx.conf

关于 Nginx配置文件模块该如何书写,以下做一些简单介绍。

1.3 配置⽂件的结构

Nginx的配置⽂件是由⼀系列的指令组成的,每个指令都是由⼀个指令名和⼀个或者多个参数组成的。

指令和参数之间使⽤空格来分隔,指令以分号 ; 结尾,参数可以使⽤单引号或者双引号来包裹。

配置⽂件分为以下⼏个部分:

# 全局块
worker_processes 1;

events {
	# events块
}

http {
	# http块
	server {
		# server块
		location / {
		# location块
		}
	}
}

例如:

server {  
    listen 80;  
    server_name seektao.cc;
    return 301 https://www.seektao.cc$request_uri;  
}  
  
server {  
    listen 80;  
    server_name www.seektao.cc;  
    return 301 https://$host$request_uri;  
}  

server {  
    listen 443 ssl http2;  
    server_name www.seektao.cc;
}

这个Nginx配置文件包含了三个server块,每个块定义了不同的虚拟主机配置。

  1. 第一个server块:
    • listen 80;: 监听80端口,表示这个server块用于处理HTTP请求。
    • server_name seektao.cc;: 指定服务器的域名为seektao.cc
    • return 301 https://www.seektao.cc$request_uri;: 当有HTTP请求访问seektao.cc时,返回301重定向到https://www.seektao.cc$request_uri,即将HTTP请求重定向到HTTPS协议的www.seektao.cc
  2. 第二个server块:
    • listen 80;: 同样监听80端口,处理HTTP请求。
    • server_name www.seektao.cc;: 指定服务器的域名为www.seektao.cc
    • return 301 https://$host$request_uri;: 当有HTTP请求访问www.seektao.cc时,返回301重定向到https://$host$request_uri,即将HTTP请求重定向到HTTPS协议的www.seektao.cc
  3. 第三个server块:
    • listen 443 ssl http2;: 监听443端口,启用SSL和HTTP/2协议。
    • server_name www.seektao.cc;: 指定服务器的域名为www.seektao.cc

这个配置文件的作用是:

  • 当有HTTP请求访问seektao.ccwww.seektao.cc时,会将请求重定向到相应的HTTPS地址。
  • 对于HTTPS请求,只有www.seektao.cc会被处理,而其他域名或路径则没有配置处理规则,可能会返回默认的Nginx页面或404错误。

这只是一个简单的配置示例,实际生产环境中还需要根据具体需求进行进一步配置,例如配置SSL证书、代理、缓存等功能。

1.3.1 全局块

全局块是配置⽂件的第⼀个块,也是配置⽂件的主体部分,主要⽤来设置⼀些影响Nginx服务器整体运⾏的配置指令,主要包括配置运⾏Nginx服务器的⽤户(组)、允许⽣成的workerprocess数、进程PID存放路径、⽇志存放路径和类型以及配置⽂件引⼊等。

# 指定运⾏Nginx服务器的⽤户,只能在全局块配置
# 将user指令注释掉,或者配置成nobody的话所有⽤户都可以运⾏
# user [user] [group]
# user nobody nobody;
user nginx;

# 指定⽣成的worker进程的数量,也可使⽤⾃动模式,只能在全局块配置
worker_processes 1;

# 错误⽇志存放路径和类型
error_log /var/log/nginx/error.log warn;

# 进程PID存放路径
pid /var/run/nginx.pid;

1.3.2 events块

events {
	# 指定使⽤哪种⽹络IO模型,只能在events块中进⾏配置
	# use epoll
	# 每个worker process允许的最⼤连接数
	worker_connections 1024;
}

1.3.3 http块

http块是配置⽂件的主要部分,包括http全局块和server块。

http {
    # nginx 可以使用include指令引入其他配置文件
    include /etc/nginx/mime.types;
    
    # 默认类型,如果请求的URL没有包含文件类型,会使用默认类型
    default_type application/octet-stream; # 默认类型
    
    # 开启高效文件传输模式
    sendfile on;
    
    # 连接超时时间
    keepalive_timeout 65;
    
    # access_log 日志存放路径和类型
    # 格式为:access_log <path> [format [buffer=size] [gzip[=level]] [flush=time] [if=condition]];
    access_log /var/log/nginx/access.log main;
    
    # 定义日志格式
    log_format main '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$http_x_forwarded_for"';
    
    # 设置sendfile最大传输片段大小,默认为0,表示不限制
    # sendfile_max_chunk 1m;
    
    # 每个连接的请求次数
    # keepalive_requests 100;
    
    # keepalive超时时间
    keepalive_timeout 65;
    
    # 开启gzip压缩
    # gzip on;
    
    # 开启gzip压缩的最小文件大小
    # gzip_min_length 1k;
    
    # gzip压缩级别,1-9,级别越高压缩率越高,但是消耗CPU资源也越多
    # gzip_comp_level 2;
    
    # gzip压缩文件类型
    # gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;
    
    # upstream指令用于定义一组服务器,一般用来配置反向代理和负载均衡
    upstream www.example.com {
        # ip_hash指令用于设置负载均衡的方式,ip_hash表示使用客户端的IP进行hash,这样可以保证同一个客户端的请求每次都会分配到同一个服务器,解决了session共享的问题
        ip_hash;
        
        # weight 用于设置权重,权重越高被分配到的几率越大
        server 192.168.50.11:80 weight=3;
        server 192.168.50.12:80;
        server 192.168.50.13:80;
    }
    
    server {
        # 参考server块的配置
    }
}

1.3.4 server块

server块是配置虚拟主机的,⼀个http块可以包含多个server块,每个server块就是⼀个虚拟主机。

server {
    # 监听IP和端口
    # listen的格式为:
    # listen [ip]:port [default_server] [ssl] [http2] [spdy] [proxy_protocol] [setfib=number] [fastopen=number] [backlog=number];
    # listen指令非常灵活,可以指定多个IP和端口,也可以使用通配符
    # 下面是几个实际的例子:
    # listen 127.0.0.1:80; # 监听来自127.0.0.1的80端口的请求
    # listen 80; # 监听来自所有IP的80端口的请求
    # listen *:80; # 监听来自所有IP的80端口的请求,同上
    # listen 127.0.0.1; # 监听来自来自127.0.0.1的80端口,默认端口为80
    listen 80;
    
    # server_name 用来指定虚拟主机的域名,可以使用精确匹配、通配符匹配和正则匹配等方式
    # server_name example.org www.example.org; # 精确匹配
    # server_name *.example.org; # 通配符匹配
    # server_name ~^www\d+\.example\.net$; # 正则匹配
    server_name localhost;
    
    # location块用来配置请求的路由,一个server块可以包含多个location块,每个location块就是一个请求路由
    # location块的格式是:
    # location [=|~|~*|^~] /uri/ { ... }
    # = 表示精确匹配,只有完全匹配上才能生效
    # ~ 表示区分大小写的正则匹配
    # ~* 表示不区分大小写的正则匹配
    # ^~ 表示普通字符匹配,如果匹配成功,则不再匹配其他location
    # /uri/ 表示请求的URI,可以是字符串,也可以是正则表达式
    # { ... } 表示location块的配置内容
    location / {
        # root指令用于指定请求的根目录,可以是绝对路径,也可以是相对路径
        root /usr/share/nginx/html; # 根目录
        
        # index指令用于指定默认文件,如果请求的是目录,则会在目录下查找默认文件
        index index.html index.htm; # 默认文件
    }
    
    # 下面是一些location的示例:
    location = / { # 精确匹配请求
        root /usr/share/nginx/html;
        index index.html index.htm;
    }
    
    location ^~ /images/ { # 匹配以/images/开头的请求
        root /usr/share/nginx/html;
    }
    
    location ~* \.(gif|jpg|jpeg)$ { # 匹配以gif、jpg或者jpeg结尾的请求
        root /usr/share/nginx/html;
    }
    
    location !~ \.(gif|jpg|jpeg)$ { # 不匹配以gif、jpg或者jpeg结尾的请求
        root /usr/share/nginx/html;
    }
    
    location !~* \.(gif|jpg|jpeg)$ { # 不匹配以gif、jpg或者jpeg结尾的请求
        root /usr/share/nginx/html;
    }
    
    # error_page 用于指定错误页面,可以指定多个,按照优先级从高到低依次查找
    error_page 500 502 503 504 /50x.html; # 错误页面
    location = /50x.html {
        root /usr/share/nginx/html;
    }
}

1.4 Nginx常用命令

systemctl start nginx 	# 启动Nginx (默认已经启动)
systemctl stop nginx	# 关闭Nginx
systemctl status nginx 	# 查看Nginx状态

sudo nginx -t 			# 检查Nginx配置文件语法是否正确
sudo nginx -s reload	# 重新加载Nginx配置

sudo nginx 				# 启动Nginx
sudo nginx -c filename 	# 指定配置⽂件
nginx -V 				# 查看Nginx的版本和编译参数等信息
sudo nginx -s quit 		# 优雅停⽌Nginx
sudo nginx -s stop 		# 快速停⽌Nginx
sudo nginx -s reopen 	# 重新打开⽇志⽂件

到此位置,Nginx就结束啦,马上开始我们的SSL证书之旅吧。

2. Certbot 申请SSL证书

Certbot 是一个由 Electronic Frontier Foundation (EFF) 开发的免费、开源的工具,用于自动化在 Web 服务器上部署 SSL/TLS 证书。SSL/TLS 证书是用于加密网站与用户之间传输的数据,确保数据传输的安全性和隐私性。

Certbot 支持大多数常见的 Web 服务器,包括 Apache、Nginx、IIS 等。

2.1 安装 certbot

# debian
sudo apt update
# 添加软件源
sudo apt install software-properties-common
sudo add-apt-repository ppa:certbot/certbot
# 安装certbot
sudo apt install certbot




# centos 没用,不太清楚,应该类似于debian
sudo yum update

此处省略添加软件源,请自己记得添加。。。

sudo yum install certbot

这里注意:一定要添加软件源,安装最新版本的 certbot,否则后面提示安装dns插件有问题

安装完成,查看版本 certbot --version,目前是 2.9.0

2.2 申请域名证书

使用certbot 来申请 Let’s Encrypt免费CA的SSL证书

官网教程
https://eff-certbot.readthedocs.io/en/latest/using.html#getting-certificates-and-choosing-plugins

# 比如,实际我没用,我使用的是下面的dns插件来获取
certbot certonly --webroot -w /path/to/your/website -d yourdomain.com

使用Web根目录/path/to/your/website中的文件验证您拥有yourdomain.com的控制权,并为该域名获取SSL证书。

2.3 DNS获取SSL泛域名证书(推荐)

DNS获取证书的好处在于可以申请泛域名证书,也就是像 *.seektao.cc也就是一次申请,到处使用,方便至极。

2.3.1 certbot-dns-dnspod

certbot-dns-dnspod 是dnspod的插件,默认的certbot是没有的,需要手动安装

Github地址

https://github.com/tengattack/certbot-dns-dnspod

# pip
sudo pip install git+https://github.com/tengattack/certbot-dns-dnspod.git



# snap 本次没有采用
sudo snap install certbot-dns-dnspod
sudo snap set certbot trust-plugin-with-root=ok
sudo snap connect certbot:plugin certbot-dns-dnspod

安装完成之后新建一个 .ini文件,比如 /etc/certbot/dnspod.ini

sudo mkdir /etc/certbot
sudo touch /etc/certbot/dnspod.ini

编辑 dnspod.ini,填入下面的内容,点此跳转到dnspod api 申请页面,注意申请的是dnspod token

dns_dnspod_api_id = 12345
dns_dnspod_api_token = 1234567890abcdef1234567890abcdef
sudo chmod 600 /etc/certbot/dnspod.ini

申请,替换为自己的域名

sudo certbot certonly -a dns-dnspod \
    --dns-dnspod-credentials /etc/certbot/dnspod.ini \
    -d seektao.cc \
    -d "*.seektao.cc"


接着就是:
输入邮箱,
是否同意注册acme(选Y),
是否同意发送邮件。。。看自己吧

接着等待,等待。。就完成了

通过日志可以看到证书文件保存在 `/etc/letsencrypt/live

# 其中 
[cert name]/privkey.pem:证书的私钥。
[cert name]/fullchain.pem:在大多数服务器软件中使用的证书文件。
[cert name]/chain.pem:在Nginx >=1.3.7 中用于 OCSP stapling。
[cert name]/cert.pem:会破坏许多服务器配置,不应在未进一步阅读文档的情况下使用。

我们用到 privkey.pem,fullchain.pem即可。

此节关于 dnspod 申请ssl证书就是如此。

2.4 自动续期

自动续期添加一个定时任务即可

# 设置定时任务
sudo crontab -e

# 每月1号的午夜执行 certbot renew 命令来续订证书
0 0 1 * * /usr/local/bin/certbot renew

需要注意自己的certbot执行路径是否正确

which certbot 可以看到certbot执行路径

关于续期的更多文档可以观看

https://eff-certbot.readthedocs.io/en/latest/using.html#setting-up-automated-renewal

3. Nginx一键生成模板

3.1 需求

这个需求在于我使用Nginx作为反代服务器,每新增一个服务,就要手动去新增一个nginx文件,而且我主要使用的是二级域名加端口的模式,配合使用 泛域名证书来编写这个脚本就很方便了。

现在想要实现输入 脚本文件 域名 端口 就会在 /etc/nginx/conf.d/下新增一个以域名开头的配置文件,例如:

$ sudo ./generate_nginx_simple_template.sh test1.seektao.cc 5980
Do you want to reload Nginx configuration? (y/n) y
Reloading Nginx configuration...

$ ll /etc/nginx/conf.d/test1.conf 
-rw-r--r-- 1 root root 965 Mar 25 13:01 /etc/nginx/conf.d/test1.conf

模板的内容如下:

$ cat /etc/nginx/conf.d/test1.conf 
server {  
    listen 80;  
    server_name test1.seektao.cc;  
    return 301 $scheme://test1.seektao.cc$request_uri;  
}  
  
server {  
    listen 443 ssl http2;  
    server_name test1.seektao.cc;  
  
    # SSL 证书配置  
    ssl_certificate /etc/letsencrypt/live/seektao.cc/fullchain.pem;  
    ssl_certificate_key /etc/letsencrypt/live/seektao.cc/privkey.pem;  
  
    # 安全设置  
    ssl_protocols TLSv1.2 TLSv1.3;  
    ssl_ciphers HIGH:!aNULL:!MD5;  
    ssl_prefer_server_ciphers on;  
  
    # 反向代理  
    location / {  
        proxy_pass http://127.0.0.1:5980/;  
        proxy_set_header Host $host;  
        proxy_set_header X-Real-IP $remote_addr;  
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;  
        proxy_set_header X-Forwarded-Proto $scheme;  
    }  
  
    # 日志配置  
    access_log /var/log/nginx/test1.seektao.cc.access.log;  
    error_log /var/log/nginx/test1.seektao.cc.error.log;  
} 

3.2 脚本的配置

使用这个脚本请先申请泛域名证书,或者你自己再修改配置文件的证书位置。

vim generate_nginx_simple_template.sh

#!/bin/bash  
  
# 检查参数数量  
if [ "$#" -ne 2 ]; then  
    echo "Usage: $0 <domain> <application_port>"  
    exit 1  
fi  
  
DOMAIN=$1  
APPLICATION_PORT=$2  
PORT_HTTP=80  
PORT_HTTPS=443  
CERT_DOMAIN=$(echo "$DOMAIN" | awk -F. '{print $(NF-1)"."$NF}')  
CONF_FILE_PREFIX=$(echo "$DOMAIN" | awk -F. '{print $1}')  
CONF_FILE="/etc/nginx/conf.d/${CONF_FILE_PREFIX}.conf"  
  
# 生成 Nginx 配置内容  
cat <<EOF > $CONF_FILE
server {  
    listen $PORT_HTTP;  
    server_name $DOMAIN;  
    return 301 \$scheme://$DOMAIN\$request_uri;  
}  
  
server {  
    listen $PORT_HTTPS ssl http2;  
    server_name $DOMAIN;  
  
    # SSL 证书配置  
    ssl_certificate /etc/letsencrypt/live/$CERT_DOMAIN/fullchain.pem;  
    ssl_certificate_key /etc/letsencrypt/live/$CERT_DOMAIN/privkey.pem;  
  
    # 安全设置  
    ssl_protocols TLSv1.2 TLSv1.3;  
    ssl_ciphers HIGH:!aNULL:!MD5;  
    ssl_prefer_server_ciphers on;  
  
    # 反向代理  
    location / {  
        proxy_pass http://127.0.0.1:$APPLICATION_PORT/;  
        proxy_set_header Host \$host;  
        proxy_set_header X-Real-IP \$remote_addr;  
        proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;  
        proxy_set_header X-Forwarded-Proto \$scheme;  
    }  
  
    # 日志配置  
    access_log /var/log/nginx/$DOMAIN.access.log;  
    error_log /var/log/nginx/$DOMAIN.error.log;  
}  
EOF
  
# 检查临时文件是否成功创建  
if [ ! -f "${CONF_FILE}" ]; then  
    echo "Error: Failed to create temporary configuration file."  
    exit 1  
fi  
  
# 提示用户是否需要修改配置文件  
# read -p "Do you want to modify the configuration file before saving? (y/n) " choice  
  
# 根据用户选择执行相应操作  
# case "$choice" in  
#     y|Y )  
#         # 使用文本编辑器让用户修改配置文件  
#         echo "Editing configuration file..."  
#         vim "${CONF_FILE}" # 或者使用你喜欢的其他文本编辑器,如 nano  
#         ;;  
#     n|N )  
#         # 直接将临时文件移动到目标位置  
#         echo "Saving configuration file without modification..."  
#         mv "${CONF_FILE}" "${CONF_FILE}"  
#         ;;  
#     * )  
#         echo "Invalid choice. Exiting..."  
#         exit 1  
#         ;;  
# esac  
  
# 检查 SSL 证书路径是否存在,如果不存在,提示用户手动申请  
if [ ! -e "/etc/letsencrypt/live/$CERT_DOMAIN/fullchain.pem" ]; then  
    echo "Warning: SSL certificate for $CERT_DOMAIN does not exist."  
    echo "Please use certbot or another tool to manually obtain a certificate for $CERT_DOMAIN."  
fi  
  
# 提示用户是否需要重新加载 Nginx 配置  
read -p "Do you want to reload Nginx configuration? (y/n) " reload_choice  
  
case "$reload_choice" in  
    y|Y )  
        # 重新加载 Nginx 配置  
        echo "Reloading Nginx configuration..."  
        sudo nginx -s reload  
        ;;  
    n|N )  
        echo "Nginx configuration will not be reloaded."  
        ;;  
    * )  
        echo "Invalid choice. Exiting..."  
        exit 1  
        ;;  
esac

至此完成~~

4. 后话

在编写这篇教程之前,我一直都是用的是 Nginx Proxy Manager (简称NPM)图形化管理界面,觉得Nginx配置看不懂,太复杂,对Nginx也不太懂。

教程的前一天,我想使用Nginx Proxy Manager为反代的服务添加 location片段,却会导致添加了location的服务挂掉,不论是图形化添加还是修改配置文件,我还以为是我的配置姿势不对,添加的location内容有错,后面找了很久的资料,却没能找到解决办法,最后还是在 NPM 的Github issues很多人出现了这个问题,而且没有一个合适的解决方案,NPM的bug。唉,一不做,二不休,不如直接用Nginx,还少去了NPM占据了一部分内存。

安装Nginx的过程还是很愉快的。但是对于ssl证书我又了解甚少了,对此又花费了很多精力去了解 certbot,结果apt安装的certbot没有dnspod插件??我一个大问号,又卸载了apt方式安装的certbot,去安装snap,通过snap安装certbot,我发现 snap也有问题??但是找到了snap上GitHub上的 dnspod插件,也支持snap,然后又卸载 certbot,snap,重新通过 apt 安装 certbot,然后下载dnspod,结果又在申请证书的时候出错了。原来我为了简便,dnspod.ini 用的是 NPM之前的配置。后来改了就好了。

现在只差反代了,上面的脚本也是为了我在 NPM已经反代的服务而写的,毕竟一个一个复制然后改端口也挺麻烦的。

全文完。

制作不易,如果对您有一丢丢帮助,您可以点赞收藏转发,一键三连,🤞🤞,谢谢您咧~~

如需转载,请注明出处。

创作时间:2024-03-25 13:40

创作者:muyiacc