把站点挪到 8443 之后踩的几个坑

这台机器上 80 和 443 都被前置的东西占着,站点本身只好退到回环的 127.0.0.1:8443 上,对外由 443 转发过来。搬完之后页面能开, 但日志、缓存和协议版本全都不对,前后改了四轮才顺。

一、listen ... http2 的旧写法

nginx 1.25.1 起把 HTTP/2 从 listen 参数里拆了出来,改成独立的 http2 指令。原本的写法虽然还能跑,但启动时会扔一条 the "listen ... http2" directive is deprecated 的警告, 而且和新的 http2 on; 混用时行为不好猜。

server {
    listen 127.0.0.1:8443 ssl;
    http2 on;                    # 1.25.1+ 的正确写法
    server_name example.com;
}

顺手把 nginx -t 的输出也看了一眼,警告一并消失。

二、ssl 参数不会自动继承

我以为在 http 块里写了 ssl_protocols,所有 server 就都跟着走。实际上 ssl_protocols 确实可以在 http 块里全局设置, 但一旦某个 server 块里又写了一遍,就只对那个 server 生效——而被其他配置 include 进来的模板经常各自带一份,最后哪个生效全看顺序。

排查办法是把合并后的配置打出来看:

sudo nginx -T | grep -nE "ssl_protocols|ssl_ciphers|listen "

-T 会输出所有 include 展开后的完整配置,比到处翻文件靠谱。 我这边就是发现某个 snippet 里还留着 TLSv1.1。

三、反向代理丢了 Host

前置转发到 8443 时,默认带过去的 Host 是上游的地址,站点拿到的 是 127.0.0.1:8443,于是 server_name 匹配不上, 落到了默认 server,页面内容对但所有绝对链接都错。

location / {
    proxy_pass http://127.0.0.1:8443;
    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;
}

$host 和 $http_host 的区别在于前者会去掉端口并按 server_name 归一化,静态站点用 $host 就够了。

四、日志里的 IP 全成了 127.0.0.1

这个不算 bug,是架构决定的:请求确实是回环来的。想拿到真实 IP 有两条路, 要么在链路上开 PROXY protocol,要么让前置把 X-Real-IP 传下来再配 real_ip_header:

set_real_ip_from 127.0.0.1;
real_ip_header   X-Real-IP;
real_ip_recursive on;

开 PROXY protocol 要两边同时改,改错一边就是整站 502,我最后选了 X-Real-IP,够用。

验证

站点在回环上,浏览器没法直接测,用 --resolve 把域名硬指到 127.0.0.1,再配合 -k 忽略证书告警:

curl -sk --resolve example.com:8443:127.0.0.1 \
     -o /dev/null -w "%{http_code} %{time_total}s\n" \
     https://example.com:8443/

再补一条握手的检查,确认证书链完整、协议是 TLS 1.3:

openssl s_client -connect 127.0.0.1:8443 \
                 -servername example.com -brief </dev/null

四个坑里真正会让人误判的是第二个:配置看着都对,就是行为不对, 因为生效的是另一个文件里的那份。nginx -T 应该成为习惯。

nginx TLS 排错