原创

Nginx配置反向代理静态资源与负载均衡详解

Nginx 配置反向代理、静态资源与负载均衡的核心用法

Nginx 最常见的三类能力,就是反向代理、静态资源服务和负载均衡。很多项目线上部署时,访问链路几乎都绕不开这三项:浏览器请求先到 Nginx,由 Nginx 决定请求是直接返回静态文件,还是转发给后端应用,再或者分发到多台服务实例。

如果只会“把配置抄上去跑起来”,通常很快就会遇到问题:接口路径转发错了、静态资源 404、真实客户端 IP 丢失、负载均衡不生效、WebSocket 连接失败、缓存头配置混乱。要真正理解 Nginx 的配置,关键是先搞清楚它处理请求的顺序和各模块的职责。


一、先理解 Nginx 在请求链路中的位置

一个典型的 Web 请求链路如下:

浏览器 / App
    ↓
  Nginx
    ├── 直接返回静态资源
    ├── 反向代理到 Java / Go / Python / Node 服务
    └── 按负载均衡策略分发到多台后端机器

这意味着 Nginx 不仅是“一个 Web 服务器”,更是一个入口网关。它通常承担这些职责:

  • 接收客户端请求
  • 根据域名、端口、路径匹配 serverlocation
  • 静态资源直接由本地磁盘返回
  • 动态请求通过 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:传递客户端真实 IP
  • X-Forwarded-For:记录完整代理链路上的客户端 IP
  • X-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 从磁盘读取并返回,不需要经过应用服务,所以效率很高。


七、rootalias 的区别

这也是高频易错点。

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 的逻辑是:

  1. 先找请求对应的文件
  2. 找不到再找目录
  3. 都找不到时返回 /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_failsfail_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;
}

也可以放在 serverlocation 中做更细粒度控制。


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
  • 核对 locationproxy_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 理解成一个统一入口调度器:

  • 静态文件由它高效返回
  • 动态请求由它转发给应用
  • 多节点流量由它分配
  • 请求头、缓存、压缩、超时、日志都在这一层集中治理

只要把 serverlocationrootaliasproxy_passupstream 这几个核心点真正搞清楚,Nginx 配置的大部分问题都能定位和解决。

正文到此结束
评论插件初始化中...
Loading...