LabHub
学习 学习路径 课程

Tomcat 与 nginx 运维

代理配置那六行,以及没有这六行会发生什么

在 LabHub 中继续学习

一句话总结

代理配置事故往往来自六行请求头转发配置,或 proxy_pass 中一个斜杠。两者即使遗漏,页面平时也能正常显示,因此通常很晚才被发现。

概念图: 运维便利与统一策略 · 策略放在应用中,修改后必须重新部署;放在代理中,只需 reload。 · Host $host · X-Forwarded-For

为什么在前面放 Web 服务器

Tomcat 也会处理 HTTP,为什么还要在前面放 nginx?在 SI 现场,真正理由不是性能,而是运维便利与统一策略

最后一点尤其重要。策略放在应用中,修改后必须重新部署;放在代理中,只需 reload。 这就是运维团队偏爱代理的原因。

代理配置的六行

location / {
    proxy_pass http://127.0.0.1:8080;

    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-Forwarded-Host  $host;
    proxy_set_header X-Forwarded-Port  $server_port;

    proxy_connect_timeout 3s;
    proxy_send_timeout   30s;
    proxy_read_timeout   60s;
}

只有理解每行缺失时会发生什么,才不是死记硬背。

还有一条重要安全原则:这些请求头必须在最前端代理被覆盖。 因为客户端可以自行发送 X-Forwarded-For: 10.0.0.1。使用 proxy_set_header 覆盖后,客户端值会被忽略,但追加型变量除外。

proxy_pass 的斜杠——最常见的 nginx 缺陷

location /api/ {
    proxy_pass http://backend;      # 슬래시 없음 → /api/users/1 이 그대로 전달
}

location /api/ {
    proxy_pass http://backend/;     # 슬래시 있음 → /api 부분이 잘리고 /users/1 로 전달
}

只要 URI 部分存在(包括斜杠),与 location 匹配的部分就会被删除。 后端期望 /api context,却加了斜杠,就会大量返回 404;反过来,后端期望根路径却未加斜杠,可能得到 /api/api/users

每年都有人为这一个字符浪费半天。混淆时,同时查看 access log 中的 $upstream_addr 与后端日志中的请求路径,即可立即判断。

请求大小——413 的真相

nginx 的 client_max_body_size 默认值为 1MB。文件上传系统不调高该值,上传 3MB 附件时会出现 413 Request Entity Too Large。而这个错误由 nginx 自己返回,应用日志不会留下任何记录。 开发人员会因“服务器里完全没有日志”而排查数日。

client_max_body_size 20m;
client_body_buffer_size 128k;
large_client_header_buffers 4 16k;

large_client_header_buffers 用于较大的请求头。接入 SSO 后 cookie 会增大,超过默认缓冲区就会返回 400 Bad Request,而日志同样不够明确。

压缩与静态文件

gzip on;
gzip_comp_level 6;
gzip_min_length 1000;
gzip_types text/plain text/css application/json application/javascript text/xml;
gzip_vary on;

静态文件最好完全不经过代理。

location /static/ {
    alias /app/static/;
    expires 7d;
    access_log off;
}

混淆 rootalias 也是常见问题。对于 location /static/root /app; 对应 /app/static/파일alias /app/static/; 也对应 /app/static/파일。看起来相同,但若 location /s/ 搭配 alias /app/static/;,则 /s/a.js/app/static/a.jslocation 路径与真实目录名不同时使用 alias。

阻止访问管理页面

Tomcat manager 或 actuator 暴露到外网,确实会导致安全事故。在代理层阻止最可靠。

location ~ ^/(manager|host-manager)/ { return 403; }
location /actuator/ {
    allow 10.0.0.0/8;
    deny  all;
    proxy_pass http://127.0.0.1:8080;
}

这里的要点是:应用配置也能限制访问,但在代理层限制,无需部署应用即可修改策略。 即使周五下午收到安全检查整改要求,也只需 reload。

access log 应记录什么

默认 combined 格式不足以分析故障,至少要加入以下四项。

log_format labhub '$remote_addr - $remote_user [$time_local] '
                  '"$request" $status $body_bytes_sent '
                  'ua="$upstream_addr" us=$upstream_status '
                  'urt=$upstream_response_time rt=$request_time';

如果 $request_time 很大,而 $upstream_response_time 很小,问题不在后端,而在客户端网络或响应传输。仅凭这两个值的差异,就能多次纠正“后端很慢”的误判。

在实际项目中

缺少六行请求头时,出现的症状看似互不相关:访问日志中的客户端 IP 全都变成同一个代理 IP(缺少 X-Real-IPX-Forwarded-For);登录后的重定向跳到内部地址或 http(缺少 HostX-Forwarded-Proto);基于 IP 的访问控制和审计日志整体失去意义。

共同点是平时完全没有问题。因此,往往上线数周后,直到安全审计或故障分析才发现,而届时已经无法靠既有日志追踪任何事情。

proxy_pass 的斜杠更加安静。末尾有斜杠,就删除 location 路径后转发;没有则保留。开发环境使用根路径时差异不明显,到了带 context 路径的生产环境才突然出现 404。