Nginx 生产落地案例
生产落地案例
案例一:教育平台后台从裸端口迁到 HTTPS 统一入口
业务背景:后台管理系统、API 服务、静态前端分别跑在三台机器上,端口各不相同(3000 / 8080 / 静态文件),此前是把端口直接开在公网上,用 IP 加端口访问。问题很直接:证书没处放、后端端口暴露、跨域配置散在各个服务里、扩容得改前端地址。
怎么用:在三台机器前加一台 Nginx 做统一入口,按域名拆 server block:admin.example.com 走后台、api.example.com 走接口、www.example.com 直接发静态文件。所有后端只监听内网地址,公网只开 80 和 443。
关键配置:用 upstream 把 API 的两个实例纳管并开 keepalive;用 map $http_upgrade $connection_upgrade 统一处理 WebSocket;证书用 acme.sh 签发,续期钩子里加 nginx -s reload;每个 location 都补上 X-Forwarded-Proto,让后端能正确判断是否 HTTPS 并生成正确的跳转链接。
upstream api_backend {
least_conn;
server 10.0.0.11:8080 max_fails=3 fail_timeout=15s;
server 10.0.0.12:8080 max_fails=3 fail_timeout=15s;
keepalive 32;
}
server {
listen 443 ssl;
server_name api.example.com;
ssl_certificate /etc/nginx/certs/api.fullchain.pem;
ssl_certificate_key /etc/nginx/certs/api.key;
ssl_protocols TLSv1.2 TLSv1.3;
client_max_body_size 50m; # 后台要传课件和图片
location / {
proxy_pass http://api_backend;
proxy_http_version 1.1;
proxy_set_header Connection ""; # 配合 upstream keepalive,必须清空
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_connect_timeout 5s;
proxy_read_timeout 60s;
}
}
效果指标:证书统一在一处续期,后端端口不再暴露;跨域配置从三个服务里删掉,前端全部改成同域请求;静态资源从应用进程剥离后,应用机器的 CPU 占用在发版或热点访问时明显平缓(具体数值因机器规格而异,不做绝对值承诺)。
踩过的坑:
proxy_pass结尾斜杠写错导致接口 404。原来写的是location /api/ { proxy_pass http://api_backend/; },结果/api/users被转成/users,后端路由全是/api/前缀所以 404。去掉结尾斜杠后正常。记住:代理到「根」用带斜杠,代理到「同前缀」不要带斜杠。- 上传课件时报 413。Nginx 的
client_max_body_size默认只有 1m,同时后端框架自己也有 body 上限,两边都调才生效。 - 后台长任务轮询报 504。默认
proxy_read_timeout是 60 秒,某个导出接口处理要几分钟。最后改成把导出做成异步任务加轮询,而不是无脑把超时调到十分钟——超时值调大只是把问题推迟,会积累大量挂起连接。 - 配置里两个 server block 的
server_name重复,Nginx 只在启动时给一条 warning,结果某天新配置一直不生效,排查半天才发现流量被前一个 block 接走了。现在每次改完都用nginx -T过一遍。
案例二:用 proxy_cache 给公开内容接口挡掉重复回源
业务背景:资料库的列表页和文章详情页是典型的读多写少——内容编辑一天改几次,但页面被大量匿名用户反复访问。每来一个请求后端就要查一次数据库、渲染一次模板,数据库连接数在访问高峰时明显吃紧。
怎么用:不动应用代码,在 Nginx 层给公开内容的 GET 接口加代理缓存。带登录态或个性化参数的请求完全绕过缓存,直接回源。
proxy_cache_path /var/cache/nginx/content levels=1:2 keys_zone=content_cache:30m
max_size=5g inactive=2h use_temp_path=off;
map $http_authorization $skip_cache {
default 1; # 带 Authorization 的一律不缓存
"" 0;
}
server {
listen 443 ssl;
server_name library.example.com;
ssl_certificate /etc/nginx/certs/library.fullchain.pem;
ssl_certificate_key /etc/nginx/certs/library.key;
location /api/content/ {
proxy_pass http://content_backend;
proxy_cache content_cache;
proxy_cache_key "$scheme$request_method$host$request_uri";
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 30s;
proxy_cache_methods GET HEAD;
# 个性化 / 带鉴权 / 强制刷新 的请求直接穿透
proxy_cache_bypass $skip_cache $arg_nocache;
proxy_no_cache $skip_cache $arg_nocache;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
proxy_cache_background_update on;
proxy_cache_lock on; # 同一 key 只放一个请求回源,防缓存击穿
proxy_cache_revalidate on;
add_header X-Cache-Status $upstream_cache_status;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# 后台编辑接口绝不缓存,且要跳过缓存直接读后端
location /api/admin/ {
proxy_pass http://content_backend;
proxy_no_cache 1;
proxy_cache_bypass 1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
效果指标:内容接口的绝大部分请求由缓存直接返回,不再查数据库;后端只需承受缓存过期时的那一次回源。X-Cache-Status 头让命中率变得可观测——用 curl -I 或日志统计 HIT / MISS 的比例即可。
踩过的坑:
- 一开始忘了区分鉴权请求,缓存把某个登录用户的响应发给了其他用户。这类问题属于严重的数据泄露,所以
proxy_cache_bypass一定要在有Authorization头或Cookie时生效。凡是响应随用户变化的接口,宁可缓存整个/api/前缀都关掉,也不要赌。 - 内容更新后页面还是旧的。这是因为缓存是按 TTL 过期的,编辑发布后不会自动失效。解决办法是在发布流程里加一步,调用带
nocache参数的内部接口触发回源,或直接按 key 清理缓存目录并 reload。纯靠缩短 TTL 会牺牲命中率。 - 缓存目录写满磁盘。上线前就把
max_size和inactive定好,并给/var/cache/nginx单独挂盘或加监控。默认不设上限会一直涨。 - 缓存目录权限问题导致 Nginx 起不来。
proxy_cache_path指定的目录需要 Nginx 的 worker 用户可写,容器化部署时尤其容易忘。

