用 Nginx 反代一个静态站点:从配置到排查
单机放多个站点,最省事的做法是让 Nginx 直接读文件;需要跑应用时,再用反向代理把它接在后面。这篇记录我常用的一套配置,以及 502、504 出现时的排查顺序。
先分清两件事
静态站点和反向代理解决的是不同问题,配置之前先确认自己属于哪一种:
- 静态托管:页面是构建好的 HTML、CSS、JS 和图片,Nginx 用
root指向目录即可。没有进程常驻,重启机器不影响访问。 - 反向代理:后端是 Node、Python 或 Go 写的服务,监听
127.0.0.1:3000之类的本地端口,Nginx 负责 TLS、压缩、缓存和访问控制。
两者可以共存:同一个域名下,/ 走静态文件,/api/ 转给后端。
一个够用的 server 块
下面这份配置覆盖了静态站点最常见的需求:HTTP/2、404 页面、静态资源长缓存。证书路径按实际替换。
server {
listen 443 ssl;
http2 on;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
root /var/www/example.com;
index index.html;
location / {
try_files $uri $uri/ $uri.html =404;
}
location ~* \.(css|js|svg|png|jpg|webp|woff2)$ {
expires 30d;
add_header Cache-Control "public, immutable";
access_log off;
}
error_page 404 /404.html;
location = /404.html {
internal;
}
}
try_files $uri $uri/ $uri.html =404 的意思是:先找同名文件,再找同名目录,最后尝试补一个 .html 后缀,都没有就返回 404。这样 /about 可以直接对应到 about.html,不必把每个链接都写成带后缀的形式。
把请求转给后端
后端只监听本地回环地址,外网访问统一经过 Nginx。除了 proxy_pass,几个请求头也要一并带上,否则后端拿到的客户端 IP 全是 127.0.0.1。
location /api/ {
proxy_pass http://127.0.0.1:3000;
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_read_timeout 30s;
}
如果后端用了 WebSocket,还需要补上 Upgrade 与 Connection 两个头,并把 proxy_read_timeout 调大,否则连接会在空闲一段时间后被切断。
出问题时按这个顺序查
先把范围缩小到“Nginx 到后端”这一段,再决定改哪里。
- 502 Bad Gateway:后端没起、端口写错,或者进程崩了。先在服务器上执行
curl -I http://127.0.0.1:3000,再用ss -lntp确认端口确实在监听。 - 504 Gateway Timeout:连接建立了,但后端在超时时间内没有返回。看后端日志里最慢的那条请求,再决定是调大
proxy_read_timeout还是优化查询。 - 404(静态):多半是
root指错,或者 Nginx 运行用户没有目录的读权限。用namei -l /var/www/example.com/index.html逐级检查权限。 - 413 Request Entity Too Large:上传被默认的 1 MB 限制挡住了,按需调整
client_max_body_size。
改完先验证再重载
每次改动后先跑 nginx -t,通过之后再 reload。直接 restart 会把现有连接全部断掉,reload 则是平滑加载新配置。
sudo nginx -t
sudo systemctl reload nginx
curl -I https://example.com/
curl -I https://example.com/api/health
习惯上我会把旧配置复制一份带日期的备份,例如 site.conf.20261028.bak。回滚只需要改回文件名再 reload,比凭记忆撤销快得多。
小结
静态站点优先静态托管,只有确实需要计算时才引入后端。配置尽量少,出错的位置也就少;剩下的工作交给日志和 curl,比反复猜测有效。