生产环境中香港服务器搭配CN2线路,Nginx反向代理与证书续期如何配置

香港服务器搭配CN2线路时,Nginx、应用进程和证书续期应当分开管理:Nginx负责公网入口、TLS终止和反向代理,应用只监听本机回环地址,systemd负责进程守护,Certbot或其他ACME客户端负责证书更新。CN2属于网络接入与传输路径选择,不是Nginx配置项,也不会改变证书申请和续期流程。
下面以使用systemd的Debian/Ubuntu类Linux为例,域名使用example.com,应用监听127.0.0.1:8080。实际操作前,请将域名、邮箱、应用启动命令和目录替换为真实值。
一、部署前先确认边界
生产环境不要直接把应用端口暴露到公网。推荐的连接关系如下:
客户端
│
├─ TCP 80:ACME验证、HTTP跳转
└─ TCP 443:HTTPS请求
│
▼
Nginx
│
└─ 127.0.0.1:8080
│
▼
应用进程
在开始配置前,至少确认以下条件:
- 域名的
A记录已经指向香港服务器公网IPv4地址。 - 如果配置了
AAAA记录,服务器已经正确提供IPv6访问;否则应先移除无效的AAAA记录。 - 防火墙或安全策略只放行实际需要的端口。公网入口通常需要TCP 80和443,SSH等管理端口应按照现有运维策略限制来源。
- 应用已经部署到服务器,并且支持以前台方式启动。
- 应用的健康检查地址、日志位置和启动命令已经明确。
- 已经准备可恢复的配置备份。证书私钥属于敏感文件,备份时应使用权限受控并经过加密的存储位置。
- 当前服务器使用systemd管理服务。可先核验:
cat /etc/os-release
ps -p 1 -o comm=
systemctl is-system-running || true
ss -lntp
如果ps显示的不是systemd,不要直接套用下面的服务单元配置,应先确认当前系统的进程管理方式。
域名解析和公网地址也要提前核对:
sudo apt-get update
sudo apt-get install -y dnsutils curl openssl
dig +short A example.com
dig +short AAAA example.com
curl -4 -I http://example.com
curl -6 -I http://example.com
没有IPv6服务时,curl -6失败并不一定说明IPv4配置有问题;但如果DNS仍然发布了不可用的AAAA记录,部分客户端可能优先尝试IPv6,最终表现为访问不稳定。
二、先让应用由systemd守护
Nginx只负责接收和转发请求,不能替代应用进程管理。生产环境中,应用应由systemd启动、停止和重启,避免通过SSH窗口直接运行进程。
下面的示例假设:
- 应用用户为
app; - 应用目录为
/opt/myapp; - 启动脚本为
/opt/myapp/bin/start; - 启动脚本以前台方式运行;
- 应用支持
--host和--port参数。
创建用户和目录会改变系统权限,仅适用于新部署或已经完成应用目录备份的环境。不要在不确认目录归属的情况下直接执行chown。
if ! id app >/dev/null 2>&1; then
sudo useradd --system \
--home-dir /opt/myapp \
--shell /usr/sbin/nologin \
app
fi
sudo install -d -o app -g app -m 0750 /opt/myapp
sudo install -d -o root -g app -m 0750 /etc/myapp
如果应用需要环境变量,可以单独保存到/etc/myapp/myapp.env,不要把数据库密码、密钥等内容直接写入systemd单元:
sudo touch /etc/myapp/myapp.env
sudo chown root:app /etc/myapp/myapp.env
sudo chmod 0640 /etc/myapp/myapp.env
创建服务文件:
# /etc/systemd/system/myapp.service
[Unit]
Description=My Application
Wants=network-online.target
After=network-online.target
[Service]
Type=simple
User=app
Group=app
WorkingDirectory=/opt/myapp
EnvironmentFile=-/etc/myapp/myapp.env
ExecStart=/opt/myapp/bin/start --host 127.0.0.1 --port 8080
Restart=on-failure
RestartSec=5
KillSignal=SIGTERM
TimeoutStopSec=30
NoNewPrivileges=true
PrivateTmp=true
LimitNOFILE=65535
[Install]
WantedBy=multi-user.target
ExecStart必须替换为实际启动命令。如果启动脚本会自行转入后台,systemd可能无法正确判断进程状态,应改为以前台方式运行。
加载并启动服务:
sudo systemctl daemon-reload
sudo systemctl enable --now myapp.service
sudo systemctl status myapp.service --no-pager
sudo journalctl -u myapp.service -n 100 --no-pager
确认应用只监听本机地址:
sudo ss -lntp | grep ':8080'
curl -i http://127.0.0.1:8080/
如果应用提供健康检查接口,优先使用实际的健康检查路径,例如:
curl -i http://127.0.0.1:8080/health
理想结果是监听地址为127.0.0.1:8080,而不是0.0.0.0:8080。后者会让应用端口可能直接暴露到公网,是否允许这样做必须有明确的安全理由。
三、安装Nginx并先完成HTTP验证
在Debian/Ubuntu类系统中,可先核验软件包,再安装Nginx和Certbot:
apt-cache policy nginx certbot
sudo apt-get install -y nginx certbot
nginx -v
certbot --version
创建ACME验证目录:
sudo install -d -m 0755 /var/www/acme
第一次申请证书前,先只配置HTTP站点。这样Certbot可以通过HTTP访问验证文件,不需要让Nginx在证书文件尚不存在时加载HTTPS配置。
# /etc/nginx/sites-available/example.com
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
location ^~ /.well-known/acme-challenge/ {
root /var/www/acme;
default_type text/plain;
try_files $uri =404;
}
location / {
return 301 https://$host$request_uri;
}
}
如果同名站点文件已经存在,先将原文件复制到受控备份目录,不要直接覆盖。确认没有同名软链接后再启用:
sudo test ! -e /etc/nginx/sites-enabled/example.com && \
sudo ln -s /etc/nginx/sites-available/example.com \
/etc/nginx/sites-enabled/example.com
sudo nginx -t
sudo systemctl enable --now nginx
sudo systemctl reload nginx
先验证ACME路径是否能被访问:
printf 'acme-check\n' | sudo tee /var/www/acme/health.txt >/dev/null
curl -i http://example.com/.well-known/acme-challenge/health.txt
如果返回200并且响应正文为acme-check,说明域名已经到达正确的Nginx站点。若返回其他结果,应先处理DNS、站点匹配、权限或防火墙问题,不要急着申请证书。
四、申请证书并切换到HTTPS反向代理
确认域名解析和HTTP验证无误后,使用webroot方式申请证书。命令中的邮箱应替换为真实的运维邮箱,证书域名只填写已经解析到该服务器的名称。
sudo certbot certonly \
--webroot \
--webroot-path /var/www/acme \
--domain example.com \
--domain www.example.com \
--email ops@example.com \
--agree-tos \
--no-eff-email
证书申请成功后,先确认文件存在:
sudo certbot certificates
sudo ls -l /etc/letsencrypt/live/example.com/
然后将站点配置调整为HTTP跳转和HTTPS反向代理。下面的配置使用本机回环地址连接应用,避免请求再次绕行公网地址。
# /etc/nginx/sites-available/example.com
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
location ^~ /.well-known/acme-challenge/ {
root /var/www/acme;
default_type text/plain;
try_files $uri =404;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name example.com www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
access_log /var/log/nginx/example.access.log app_timing;
error_log /var/log/nginx/example.error.log warn;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
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;
proxy_set_header X-Request-ID $request_id;
proxy_connect_timeout 5s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
}
配置中的几个关键点需要特别注意:
proxy_pass使用127.0.0.1:8080,必须与应用实际监听地址和端口一致。X-Forwarded-Proto用于告诉应用原始请求是HTTP还是HTTPS,应用生成绝对URL、回调地址或安全Cookie时通常会依赖它。X-Forwarded-For保留请求经过Nginx前的客户端地址。应用侧应只信任来自本机Nginx的转发头,不能无条件信任客户端自行提交的同名请求头。proxy_read_timeout不是性能指标,而是单个请求等待上游响应的时间边界。长轮询、文件处理等业务需要根据实际接口调整。- 如果应用使用WebSocket或其他长连接,应按照应用协议补充升级头和更长的读取超时,不能仅凭普通HTTP配置推断长连接一定可用。
修改后必须先检查语法,再平滑加载:
sudo nginx -t
sudo systemctl reload nginx
nginx -t失败时不要执行reload。此时当前运行中的Nginx通常仍使用旧配置,可以根据错误行号修正文件或执行回滚。
五、配置访问日志和错误日志
只看页面是否打开,无法判断请求是卡在Nginx、应用还是网络入口。建议记录请求状态、上游状态和各阶段耗时,但不要把密码、令牌等敏感信息写入URL或日志。
log_format必须位于Nginx的http配置上下文中。Debian/Ubuntu类系统通常会加载/etc/nginx/conf.d/*.conf,先核验:
sudo nginx -T 2>/dev/null | grep -E 'conf.d|sites-enabled'
如果确认该目录会被加载,可创建:
# /etc/nginx/conf.d/request-timing.conf
log_format app_timing
'$remote_addr - $remote_user [$time_local] '
'"$request" status=$status bytes=$body_bytes_sent '
'request_time=$request_time '
'upstream_status=$upstream_status '
'upstream_connect_time=$upstream_connect_time '
'upstream_header_time=$upstream_header_time '
'upstream_response_time=$upstream_response_time '
'request_id=$request_id '
'referer="$http_referer" user_agent="$http_user_agent"';
检查并重新加载:
sudo nginx -t
sudo systemctl reload nginx
查看实时日志:
sudo tail -f /var/log/nginx/example.access.log
sudo tail -f /var/log/nginx/example.error.log
sudo journalctl -u myapp.service -f
日志判断可以按以下方式进行:
| 现象 | 优先检查位置 | 通常意味着什么 |
|---|---|---|
| Nginx访问日志完全没有记录 | DNS、防火墙、公网地址、监听端口 | 请求可能尚未到达Nginx |
| 返回502 | error.log、应用服务状态、8080监听状态 | Nginx无法连接应用,或应用刚刚退出 |
| 返回504 | upstream_response_time、应用日志、慢接口 | 应用没有在读取超时内返回 |
| 返回404且应用日志无记录 | Nginx站点和location匹配 | 请求可能被错误站点或Nginx规则处理 |
| TLS握手失败 | 证书路径、域名、IPv4/IPv6监听 | 证书或监听协议配置不匹配 |
日志还需要纳入现有的logrotate策略,否则持续访问可能耗尽磁盘。先查看是否已经覆盖自定义日志:
grep -R "/var/log/nginx" /etc/logrotate.conf /etc/logrotate.d 2>/dev/null
sudo logrotate -d /etc/logrotate.conf
df -h
如果自定义日志不在轮转范围内,应按系统现有的Nginx用户、日志组和保留策略补充配置,不要直接套用其他发行版的create权限。配置完成后先使用logrotate -d进行演练,再执行正式轮转。
六、证书自动续期和续期后的Nginx加载
证书续期最容易遗漏的不是申请动作,而是续期后Nginx没有重新加载新证书。Certbot通常会使用系统定时器或发行版提供的调度任务,但必须先确认:
command -v certbot
systemctl list-timers --all | grep -i certbot || true
sudo certbot certificates
先进行模拟续期:
sudo certbot renew --dry-run
模拟成功只能说明验证流程基本可用,还要确认续期后Nginx能够通过语法检查并平滑加载。可以创建部署钩子:
sudo install -d -m 0755 /etc/letsencrypt/renewal-hooks/deploy
sudo tee /etc/letsencrypt/renewal-hooks/deploy/nginx-reload.sh >/dev/null <<'EOF'
#!/bin/sh
set -eu
PATH=/usr/sbin:/usr/bin:/sbin:/bin
export PATH
nginx -t
systemctl reload nginx
EOF
sudo chmod 0755 /etc/letsencrypt/renewal-hooks/deploy/nginx-reload.sh
sudo /etc/letsencrypt/renewal-hooks/deploy/nginx-reload.sh
该钩子只在续期流程调用部署钩子时执行。它先运行nginx -t,语法检查失败就不会执行reload,从而避免把错误配置加载到运行中的Nginx。
如果系统没有任何Certbot定时任务,应按照当前发行版的调度方式建立一个续期任务。任务命令应使用command -v certbot返回的实际路径,且不要同时建立多个重复任务。无论使用何种调度器,都应满足:
- 定期执行
certbot renew; - 续期成功后执行上述Nginx部署钩子;
- 将任务输出和失败状态纳入运维检查;
- 定期保留一次
--dry-run验证记录。
查看证书实际有效期:
sudo openssl x509 \
-in /etc/letsencrypt/live/example.com/fullchain.pem \
-noout \
-subject \
-issuer \
-dates
不要把/etc/letsencrypt/live中的证书文件复制到站点目录后长期使用。Certbot通常通过软链接更新证书,Nginx应直接引用live目录中的路径。
七、资源边界不要靠猜测设置
香港服务器搭配CN2线路后,网络可达性和应用处理能力仍然是两个独立问题。线路不能替代应用进程、内存、文件描述符和磁盘日志管理。
建议至少核对以下资源:
nproc
free -h
df -h
df -i
ulimit -n
sudo systemctl show myapp.service -p LimitNOFILE
sudo ss -s
配置资源边界时,优先遵循以下原则:
- 应用进程数量根据CPU、内存和应用模型调整,不要单纯按照CPU核心数机械增加。
LimitNOFILE需要同时考虑Nginx连接数、应用连接数、日志文件和上游连接,设置后要观察实际使用量。proxy_connect_timeout应短于业务整体超时,用于快速发现应用端口不可用;proxy_read_timeout应按照接口类型设置,过小会误杀正常慢请求,过大则会长期占用连接。- 上传接口需要明确
client_max_body_size,该值应与应用限制、磁盘空间和业务需求一致,不能直接使用示例值。 - 日志目录和应用临时目录必须纳入磁盘监控。磁盘写满后,可能同时导致日志丢失、证书续期失败和应用无法写入临时文件。
- 不要未经压力测试就设置过低的
MemoryMax或连接上限。硬限制触发后,systemd可能终止应用,表现为间歇性502。
生产环境的边界配置应以监控数据为依据。至少观察应用重启次数、Nginx的5xx比例、upstream_response_time、内存使用量、磁盘使用率和文件描述符使用情况。
八、备份配置并准备回滚
配置修改前,先备份Nginx、证书、systemd服务文件和应用环境变量。证书目录包含私钥,备份文件不能放在可被Nginx直接访问的目录中,也不应只保存在同一块系统盘。
以下命令适用于已经存在这些目录和文件的环境:
sudo install -d -m 0700 /var/backups/myapp
sudo tar \
--create \
--gzip \
--preserve-permissions \
--file="/var/backups/myapp/config-$(date +%F-%H%M%S).tar.gz" \
/etc/nginx \
/etc/letsencrypt \
/etc/systemd/system/myapp.service \
/etc/myapp
备份完成后,应将备份复制到权限受控的异机位置,并进行加密。至少验证压缩包可以读取:
sudo tar -tzf /var/backups/myapp/config-*.tar.gz | head
Nginx配置回滚的低风险方式是恢复旧文件、检查语法、再reload:
sudo cp -a \
/etc/nginx/sites-available/example.com \
/etc/nginx/sites-available/example.com.before-rollback
sudo cp -a \
/var/backups/myapp/example.com.known-good \
/etc/nginx/sites-available/example.com
sudo nginx -t
sudo systemctl reload nginx
上面的备份文件名只是示例,实际回滚前应先确认它确实是可用版本。不要在没有备份的情况下直接删除/etc/letsencrypt,也不要通过删除live和archive目录来处理证书问题。证书回滚应优先恢复站点配置和正确的证书引用,再执行语法检查与reload。
如果只是新证书加载失败,当前Nginx工作进程可能仍在提供旧证书。此时先查看:
sudo nginx -t
sudo systemctl status nginx --no-pager
sudo journalctl -u nginx -n 100 --no-pager
修复路径、权限或证书链问题后,再次执行reload。只有确认新配置能够通过nginx -t,才应继续变更。
九、上线后的连续验证
完成配置后,按从本机到公网、从低风险到高风险的顺序检查。
1. 验证应用和Nginx进程
sudo systemctl is-active myapp.service
sudo systemctl is-active nginx
sudo ss -lntp | grep -E ':80|:443|:8080'
curl -i http://127.0.0.1:8080/
2. 验证HTTP跳转
curl -I http://example.com/
预期是返回301或其他明确的重定向响应,并指向HTTPS地址。若HTTP请求超时,先检查公网80端口和Nginx监听;若返回其他站点内容,检查server_name和站点启用状态。
3. 验证HTTPS证书和代理
curl -I https://example.com/
curl -sS -D- -o /dev/null https://example.com/
查看证书域名和有效期:
openssl s_client \
-connect example.com:443 \
-servername example.com \
/dev/null |
openssl x509 -noout -subject -issuer -dates
4. 验证IPv4和IPv6的一致性
curl -4 -I https://example.com/
curl -6 -I https://example.com/
如果IPv4正常而IPv6失败,检查服务器是否监听[::]:443、IPv6防火墙是否放行,以及AAAA记录是否确实指向当前服务器。没有准备好IPv6服务时,移除错误的AAAA记录通常比保留无效入口更容易排查。
5. 验证日志链路
访问一次实际业务页面后,同时查看:
sudo tail -n 20 /var/log/nginx/example.access.log
sudo tail -n 20 /var/log/nginx/example.error.log
sudo journalctl -u myapp.service -n 50 --no-pager
如果Nginx有访问记录但应用没有对应日志,重点检查proxy_pass、应用监听地址和应用日志配置。如果两边都没有记录,则回到DNS、防火墙、公网监听和域名匹配检查。
生产部署中最容易遗漏的是三项复核:证书续期后是否真的reload了Nginx、应用是否仍然只监听回环地址、日志和证书备份是否能够在另一台环境中恢复。完成这三项检查后,再根据5xx日志、上游耗时和资源使用情况调整超时、进程数与磁盘保留策略。