Nginx配置反向代理静态资源与负载均衡详解
Nginx 配置反向代理、静态资源与负载均衡的核心用法
Nginx 最常见的三类能力,就是反向代理、静态资源服务和负载均衡。很多项目线上部署时,访问链路几乎都绕不开这三项:浏览器请求先到 Nginx,由 Nginx 决定请求是直接返回静态文件,还是转发给后端应用,再或者分发到多台服务实例。
如果只会“把配置抄上去跑起来”,通常很快就会遇到问题:接口路径转发错了、静态资源 404、真实客户端 IP 丢失、负载均衡不生效、WebSocket 连接失败、缓存头配置混乱。要真正理解 Nginx 的配置,关键是先搞清楚它处理请求的顺序和各模块的职责。
一、先理解 Nginx 在请求链路中的位置
一个典型的 Web 请求链路如下:
浏览器 / App
↓
Nginx
├── 直接返回静态资源
├── 反向代理到 Java / Go / Python / Node 服务
└── 按负载均衡策略分发到多台后端机器
这意味着 Nginx 不仅是“一个 Web 服务器”,更是一个入口网关。它通常承担这些职责:
- 接收客户端请求
- 根据域名、端口、路径匹配
server和location - 静态资源直接由本地磁盘返回
- 动态请求通过
proxy_pass转发到上游服务 - 多个上游节点之间做负载均衡
- 统一处理 HTTPS、缓存、压缩、跨域、限流等能力
二、Nginx 配置文件的基本结构
一个典型的 Nginx 主配置结构如下:
worker_processes auto;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name example.com;
location / {
root /usr/share/nginx/html;
index index.html;
}
}
}
重点看三层结构:
1. http
定义 HTTP 服务范围内的公共配置,例如日志格式、压缩、上游服务组、缓存策略等。
2. server
表示一个虚拟主机。通常按域名、端口划分,一个 server 对应一组站点入口配置。
3. location
表示具体路径匹配规则。请求进入某个 server 后,会继续按照 location 规则处理。
这三层关系可以简单理解为:
http
└── server
└── location
三、反向代理到底是什么
反向代理的本质是:客户端访问的是 Nginx,但 Nginx 再把请求转发给真正的后端服务,客户端并不知道后端服务的真实地址。
例如:
- 浏览器访问:
http://example.com/api/users - Nginx 收到请求后,转发给:
http://127.0.0.1:8080/api/users
客户端始终认为自己在访问 example.com,而不是直接访问 8080 端口。
反向代理的价值
- 隐藏后端服务地址
- 统一入口,便于管理域名和证书
- 支持负载均衡
- 可以做请求转发、限流、缓存、鉴权
- 解耦前端与后端部署方式
四、Nginx 反向代理的基础配置
1. 最基本的代理写法
server {
listen 80;
server_name example.com;
location /api/ {
proxy_pass http://127.0.0.1:8080;
}
}
含义很直接:
- 当请求路径以
/api/开头时 - 转发给
127.0.0.1:8080
例如:
http://example.com/api/user/list- 会被代理到:
http://127.0.0.1:8080/api/user/list
2. 建议补充的代理头
实际项目里,不建议只写一个 proxy_pass。更常见的配置如下:
server {
listen 80;
server_name example.com;
location /api/ {
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;
}
}
这些头的作用分别是:
Host:把原始请求的主机名传给后端X-Real-IP:传递客户端真实 IPX-Forwarded-For:记录完整代理链路上的客户端 IPX-Forwarded-Proto:告诉后端原始请求是http还是https
后端服务如果需要记录真实来源 IP、生成跳转地址、判断协议类型,这几个头通常都必须传。
五、proxy_pass 结尾带不带 / 的区别
这是 Nginx 反向代理最容易配错的地方之一。
看两个配置:
写法一:不带 /
location /api/ {
proxy_pass http://127.0.0.1:8080;
}
请求:
/api/user/list
转发后仍然是:
/api/user/list
也就是把完整路径拼到后端地址后面。
写法二:带 /
location /api/ {
proxy_pass http://127.0.0.1:8080/;
}
请求:
/api/user/list
转发后会去掉匹配到的 /api/ 前缀,变成:
/user/list
结论
这是规则核心:
-
proxy_pass http://host:port;保留原始请求 URI -
proxy_pass http://host:port/;用/替换当前匹配到的 location 前缀
这个差异直接决定你的后端收到的路径是什么。很多“接口 404”“路径多一层或少一层”的问题,本质就是这里配置错了。
六、静态资源服务怎么配
Nginx 非常擅长处理静态资源,例如:
- HTML
- CSS
- JS
- 图片
- 字体文件
- 下载文件
- 前端构建产物
因为静态资源直接由 Nginx 从磁盘读取并返回,不需要经过应用服务,所以效率很高。
七、root 与 alias 的区别
这也是高频易错点。
1. root
location /static/ {
root /data/www;
}
请求:
/static/app.js
实际查找文件路径:
/data/www/static/app.js
也就是说,root 是把请求路径整体拼接到指定目录后面。
2. alias
location /static/ {
alias /data/www/static/;
}
请求:
/static/app.js
实际查找文件路径:
/data/www/static/app.js
这里 alias 的语义是:用指定目录直接替换匹配的路径前缀。
3. 什么时候用哪个
root:适合目录结构和 URL 路径天然对应的场景alias:适合 URL 前缀与物理目录不一致的场景
4. 最容易出错的地方
alias 在目录映射场景中,通常要带尾部 /:
location /static/ {
alias /data/www/static/;
}
不要写成:
location /static/ {
alias /data/www/static;
}
虽然部分情况下也能工作,但路径拼接容易产生歧义,尤其在复杂配置中更容易踩坑。
八、静态资源服务的典型配置
1. 基础静态目录映射
server {
listen 80;
server_name static.example.com;
location / {
root /data/www/html;
index index.html;
}
location /assets/ {
alias /data/www/assets/;
}
}
含义:
/下访问首页文件在/data/www/html/assets/路径映射到/data/www/assets/
2. 为静态资源设置缓存头
location /assets/ {
alias /data/www/assets/;
expires 30d;
add_header Cache-Control "public";
}
这类配置适合:
- 带 hash 的前端构建文件
- 长期不变的图片、字体、JS、CSS 文件
作用是减少浏览器重复请求,提高加载速度。
3. 单页应用前端路由支持
Vue、React 等前端项目部署后,刷新页面常见 404,本质是因为路由由前端接管,但 Nginx 会按文件路径查找。
常见解决方案:
server {
listen 80;
server_name app.example.com;
root /data/www/app;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
}
try_files 的逻辑是:
- 先找请求对应的文件
- 找不到再找目录
- 都找不到时返回
/index.html
这正是前端单页应用需要的行为。
九、负载均衡的基本原理
当后端不止一台服务实例时,Nginx 可以把请求分发到多个节点,避免单机压力过大,同时提升可用性。
例如有三台应用服务器:
- 192.168.1.10:8080
- 192.168.1.11:8080
- 192.168.1.12:8080
此时客户端不需要知道具体访问哪台机器,只访问 Nginx 即可,Nginx 决定把请求发给谁。
十、使用 upstream 定义后端服务组
http {
upstream backend_server {
server 192.168.1.10:8080;
server 192.168.1.11:8080;
server 192.168.1.12:8080;
}
server {
listen 80;
server_name example.com;
location /api/ {
proxy_pass http://backend_server;
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;
}
}
}
这里:
upstream backend_server定义了一个上游服务组proxy_pass http://backend_server;表示请求转发到这个服务组- Nginx 会自动按照默认策略分发请求
十一、Nginx 常见负载均衡策略
1. 轮询(默认)
upstream backend_server {
server 192.168.1.10:8080;
server 192.168.1.11:8080;
server 192.168.1.12:8080;
}
默认就是轮询。请求会依次分配到不同节点。
适合场景:
- 各节点性能接近
- 请求耗时差异不大
- 不要求会话粘性
2. weight 权重
upstream backend_server {
server 192.168.1.10:8080 weight=5;
server 192.168.1.11:8080 weight=3;
server 192.168.1.12:8080 weight=2;
}
权重越大,被分配到的请求越多。
适合场景:
- 机器配置不同
- 某些节点处理能力更强
- 想逐步给新节点导流
3. ip_hash
upstream backend_server {
ip_hash;
server 192.168.1.10:8080;
server 192.168.1.11:8080;
server 192.168.1.12:8080;
}
按客户端 IP 做哈希分配,同一客户端通常会落到同一台后端。
适合场景:
- 需要简单会话保持
- 后端 session 存本地内存,没有做共享
缺点也很明显:
- 节点扩缩容时映射容易变化
- NAT 场景下多个用户可能被映射到同一节点
- 分布未必均匀
4. least_conn
upstream backend_server {
least_conn;
server 192.168.1.10:8080;
server 192.168.1.11:8080;
server 192.168.1.12:8080;
}
优先把请求分发给当前连接数较少的节点。
适合场景:
- 请求处理时间差异较大
- 长连接较多
- 想让负载更贴近实际连接压力
十二、服务节点状态控制参数
1. backup
upstream backend_server {
server 192.168.1.10:8080;
server 192.168.1.11:8080;
server 192.168.1.12:8080 backup;
}
backup 节点只有在主节点都不可用时才会启用。
2. down
upstream backend_server {
server 192.168.1.10:8080;
server 192.168.1.11:8080 down;
server 192.168.1.12:8080;
}
down 表示当前节点临时下线,不参与负载均衡。
适合灰度切换、问题节点隔离。
3. max_fails 和 fail_timeout
upstream backend_server {
server 192.168.1.10:8080 max_fails=3 fail_timeout=30s;
server 192.168.1.11:8080 max_fails=3 fail_timeout=30s;
}
含义通常理解为:
- 在
30s内失败达到3次 - 该节点会被暂时判定为不可用
这类参数可以降低异常节点持续接流量的概率。
十三、一个同时包含反向代理、静态资源、负载均衡的完整示例
worker_processes auto;
events {
worker_connections 2048;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
client_max_body_size 50m;
upstream app_cluster {
least_conn;
server 192.168.1.10:8080 weight=3 max_fails=3 fail_timeout=30s;
server 192.168.1.11:8080 weight=3 max_fails=3 fail_timeout=30s;
server 192.168.1.12:8080 weight=2 max_fails=3 fail_timeout=30s backup;
}
server {
listen 80;
server_name www.example.com;
root /data/www/frontend;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
location /assets/ {
alias /data/www/frontend/assets/;
expires 30d;
add_header Cache-Control "public";
}
location /api/ {
proxy_pass http://app_cluster;
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_send_timeout 60s;
proxy_read_timeout 60s;
}
}
}
这个配置覆盖了三类场景:
- 前端页面和静态资源由 Nginx 直接提供
/api/请求代理到后端服务集群- 后端多个实例之间按负载均衡策略分发
这就是大多数中小型项目部署的典型结构。
十四、反向代理时必须关注的几个细节
1. 后端是否需要真实客户端 IP
如果后端有这些需求:
- 风控
- 日志审计
- 地域分析
- 限流
- 登录安全校验
那么必须正确传递:
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
否则后端看到的可能永远是 Nginx 所在机器 IP。
2. 是否需要支持 WebSocket
如果有 WebSocket、SSE 或长连接类场景,需要补充:
location /ws/ {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
否则握手可能失败。
3. 上传文件大小限制
默认情况下,Nginx 对请求体大小有限制。上传大文件时若配置不足,会直接报错。
http {
client_max_body_size 100m;
}
也可以放在 server 或 location 中做更细粒度控制。
4. 超时配置不能忽略
反向代理场景里,经常因为后端处理慢而出现 504、连接中断等问题。
常用超时参数:
location /api/ {
proxy_pass http://backend_server;
proxy_connect_timeout 5s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
说明:
proxy_connect_timeout:连接后端超时时间proxy_send_timeout:向后端发送请求超时时间proxy_read_timeout:读取后端响应超时时间
十五、静态资源场景中的性能优化
1. 开启 sendfile
sendfile on;
让 Nginx 更高效地传输文件,减少用户态和内核态之间的数据拷贝开销。
2. 开启压缩
gzip on;
gzip_types text/plain text/css application/javascript application/json application/xml;
gzip_min_length 1k;
gzip_comp_level 5;
适合压缩文本类资源:
- HTML
- CSS
- JS
- JSON
- XML
不适合已经压缩过的资源,例如 JPEG、PNG、MP4。
3. 缓存静态资源
对带版本号或 hash 的资源设置长缓存最有效:
location /assets/ {
alias /data/www/assets/;
expires 30d;
add_header Cache-Control "public, max-age=2592000";
}
十六、生产环境中更实用的配置建议
1. 动静分离
典型做法是:
/assets/、/static/由 Nginx 直接处理/api/转发给后端服务- 页面入口
index.html根据项目类型决定是否缓存
这样可以显著降低应用服务器压力。
2. Session 不要依赖单机
虽然 ip_hash 可以做一定程度会话保持,但生产环境更推荐:
- Session 存 Redis
- 使用 JWT
- 或统一认证中心
原因很简单:会话粘性不能从根本上解决扩容、迁移、故障切换问题。
3. 负载均衡不是高可用的全部
Nginx 能分发流量,但不等于天然具备完整高可用能力。还需要关注:
- 后端服务健康检查
- 应用无状态化
- 数据库高可用
- Redis 高可用
- 监控告警
- 日志追踪
否则某个节点即便被摘除,系统整体依然可能不可用。
4. 日志必须保留
反向代理和静态资源问题,很多都只能通过日志定位。
常见日志配置:
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log warn;
}
排查问题时优先看:
- 访问日志中的请求路径、状态码、响应大小
- 错误日志中的文件不存在、上游超时、连接拒绝等信息
十七、常见错误与排查思路
1. 接口 404
常见原因:
location没匹配到proxy_pass末尾/用错- 后端服务实际接口前缀和转发路径不一致
排查重点:
- 看 Nginx access log 中实际请求路径
- 看后端收到的请求 URI
- 核对
location和proxy_pass的拼接规则
2. 静态资源 404
常见原因:
root/alias理解错误- 物理目录不对
- 文件权限不足
- 前端构建路径和部署路径不一致
排查重点:
- 确认请求 URL
- 推导 Nginx 实际映射到的磁盘路径
- 在服务器上直接检查文件是否存在
3. 负载均衡看起来没生效
常见原因:
- 浏览器连接复用导致观察不明显
- 接口请求量太少
- 使用了
ip_hash - 某些节点已被标记失败
- 后端返回内容一致,看不出差异
排查方式:
- 在后端响应头或响应体打印节点标识
- 查看 access log 和上游日志
- 检查
upstream配置和节点状态
4. 客户端真实 IP 丢失
常见原因:
- 没有设置
X-Real-IP - 后端未正确解析
X-Forwarded-For - 多层代理场景未配置真实 IP 信任链
十八、按场景整理三套常用配置模板
1. 纯静态站点
server {
listen 80;
server_name static.example.com;
root /data/www/site;
index index.html;
location / {
try_files $uri $uri/ =404;
}
location /assets/ {
alias /data/www/site/assets/;
expires 30d;
add_header Cache-Control "public";
}
}
2. 前后端分离项目
upstream backend_server {
server 127.0.0.1:8080;
}
server {
listen 80;
server_name app.example.com;
root /data/www/frontend;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
location /api/ {
proxy_pass http://backend_server;
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;
}
}
3. 多实例后端集群
upstream backend_cluster {
least_conn;
server 10.0.0.11:8080 weight=5;
server 10.0.0.12:8080 weight=5;
server 10.0.0.13:8080 weight=3;
}
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://backend_cluster;
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_send_timeout 30s;
proxy_read_timeout 30s;
}
}
十九、版本与能力说明
1. 反向代理、静态资源、基础负载均衡
这些属于 Nginx 开源版长期具备的核心能力,主流版本均支持。
2. 负载均衡高级特性
部分更高级的能力,例如更完整的主动健康检查、商业级监控管理能力等,通常在 Nginx Plus 中更完整。开源版也能通过被动健康检查、超时与失败参数覆盖大量常见场景,但能力边界要区分清楚。
3. WebSocket 支持
主流现代版本均可支持,但必须正确设置升级头和 HTTP 版本。
二十、总结
把 Nginx 用好,不是记住几条配置,而是理解三个核心问题:
1. 请求最终是本地返回,还是转发给后端
这决定你用的是静态资源配置还是反向代理配置。
2. 路径在转发过程中有没有被改写
这决定你要不要在 proxy_pass 后面加 /,也决定后端收到的 URI 是否正确。
3. 多个后端节点如何分流
这决定你选轮询、权重、least_conn 还是 ip_hash。
真正落地时,可以把 Nginx 理解成一个统一入口调度器:
- 静态文件由它高效返回
- 动态请求由它转发给应用
- 多节点流量由它分配
- 请求头、缓存、压缩、超时、日志都在这一层集中治理
只要把 server、location、root、alias、proxy_pass、upstream 这几个核心点真正搞清楚,Nginx 配置的大部分问题都能定位和解决。