Next.js 应用上线,Nginx 是绕不开的一层:反向代理、HTTPS、缓存、压缩都在这里。本文给出一套经过生产验证的完整配置,按"反代 → 压缩 → 缓存 → HTTPS"四步走。
一、基础反向代理:把流量交给 Node
Next.js(next start)监听 127.0.0.1:3000,Nginx 占 80/443 对外:
server {
listen 80;
server_name www.example.cn;
# 真实客户端 IP 透传(日志与风控依赖)
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;
# 静态构建产物:Nginx 直接接手,不经过 Node
location /_next/static/ {
proxy_pass http://127.0.0.1:3000;
add_header Cache-Control "public, max-age=31536000, immutable";
}
# 其余全部反代(页面 / API / 图片优化)
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade; # WebSocket 兼容
proxy_set_header Connection "upgrade";
proxy_read_timeout 60s;
}
}
两个高频事故点:
- 忘了
X-Forwarded-Proto:HTTPS 下 Next 误判协议,生成的回调/分享链接变成 http,微信场景直接被拦 - 没配 Upgrade 头:后续加 WebSocket(实时通知、协同)时连接握手失败
二、Gzip/Brotli:文本资源先砍 70%
gzip on;
gzip_comp_level 6;
gzip_min_length 1024;
gzip_types text/plain text/css application/json application/javascript
application/xml image/svg+xml;
gzip_vary on; # 告知 CDN 缓存压缩与未压缩两个版本
有条件上 Brotli(ngx_brotli 模块),同样级别压缩率比 gzip 再高 15%~20%:
brotli on;
brotli_comp_level 5;
brotli_types text/plain text/css application/json application/javascript;
注意:/_next/static/ 里的资源在构建时已预压缩(.gz/br 后缀文件),配合 gzip_static on; 可以直接吐压缩文件,零 CPU 消耗。
三、缓存策略:immutable 的正确打开方式
Next.js 构建产物文件名带内容哈希,内容变则文件名变,因此可以放心"永久缓存":
| 资源 | 策略 |
|---|---|
/_next/static/* | max-age=31536000, immutable |
/_next/image?* | max-age=86400(图片优化结果,内容可能变) |
| HTML 页面 | no-cache(协商缓存,保证内容新鲜) |
| API 响应 | 默认 no-store,列表类接口按业务加短缓存 |
再加一层 proxy cache 给高频页面(首页、新闻列表)减负:
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=pagecache:10m inactive=60s;
location /news {
proxy_cache pagecache;
proxy_cache_valid 200 60s; # 60 秒微缓存,高峰期效果显著
proxy_cache_use_stale error timeout updating;
proxy_pass http://127.0.0.1:3000;
}
四、HTTPS:Certbot 三条命令
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d www.example.cn -d example.cn
sudo certbot renew --dry-run # 验证自动续期(certbot 自带定时任务)
HTTP 强制跳转 HTTPS,并补上安全响应头:
server {
listen 80;
server_name www.example.cn example.cn;
return 301 https://www.example.cn$request_uri;
}
server {
listen 443 ssl http2;
server_name www.example.cn;
ssl_certificate /etc/letsencrypt/live/www.example.cn/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/www.example.cn/privkey.pem;
add_header Strict-Transport-Security "max-age=31536000" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
}
五、进程守护:systemd 替代 PM2 的写法
# /etc/systemd/system/aiyun-web.service
[Unit]
Description=Aiyun Website (Next.js)
After=network.target mysql.service
[Service]
Type=simple
User=www
WorkingDirectory=/srv/www/aiyun
Environment=NODE_ENV=production
Environment=PORT=3000
ExecStart=/usr/bin/node node_modules/.bin/next start
Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now aiyun-web
六、上线检查清单
curl -I验证 gzip/br 生效、静态资源 immutable 头正确- HTTPS 全链路(含图片域名),HTTP 301 到 HTTPS
- 故意重启 Node 进程,确认 Nginx 502 页面友好且 systemd 自动拉起
- nginx -t 语法检查后再 reload,绝不直接 restart 生产
- 日志切割(logrotate)配置到位,避免磁盘写爆
小结
Nginx 层的价值在于:把静态、压缩、缓存、TLS这些"每请求都要做但 Node 不擅长的事"接管走,让 Node 只做业务。配置不复杂,但每一行都值得写进团队的标准模板。