上一篇 下一篇 分享链接 返回 返回顶部

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

发布人:Minchunlin 发布时间:2026-09-28 11:00 阅读量:8
生产环境中香港服务器搭配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
返回502error.log、应用服务状态、8080监听状态Nginx无法连接应用,或应用刚刚退出
返回504upstream_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返回的实际路径,且不要同时建立多个重复任务。无论使用何种调度器,都应满足:

  1. 定期执行certbot renew;
  2. 续期成功后执行上述Nginx部署钩子;
  3. 将任务输出和失败状态纳入运维检查;
  4. 定期保留一次--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日志、上游耗时和资源使用情况调整超时、进程数与磁盘保留策略。

目录结构
全文