Skip to content
Charles Shao
Go back

Nginx 配置实践:结构、反向代理、限流与生产清单

views

前面几篇讲了负载怎么分、缓存怎么用、冷热怎么拆、CDN 怎么就近——落地时,很多团队的第一站仍是 Nginx。装好之后真正要啃的,是 nginx.conf 怎么写对、怎么改稳。

本篇围绕配置结构讲清全局块、events、http/server/location,并给出反向代理、负载均衡、HTTPS、限流等常用示例与校验命令。

TL;DR

Table of contents

Open Table of contents

1. Nginx 能做什么

Nginx 的核心能力包括:高性能 HTTP 服务、反向代理、负载均衡、动静分离处理,以及邮件代理服务等。

安装完成后,nginx.conf 是配置的核心入口,正确理解其结构与各参数含义,是安全修改和调优 Nginx 的前提。

Nginx 核心能力示意:HTTP/反向代理、负载均衡、动静分离、邮件代理

HTTP/反向代理、负载均衡、动静分离是日常最常用的能力。

2. nginx.conf 结构

nginx.conf 是 Nginx 的主配置文件。也可以在 conf.d 目录下添加多个独立的配置文件(如按域名拆分),Nginx 会将它们一并加载。生产环境有多个域名需要代理时,通常在 conf.d 下为每个域名维护独立配置。

配置文件的整体层级结构为:全局块 → events 块 → http 块;http 块内可嵌套多个 server 块,每个 server 块可嵌套多个 location 块。

nginx.conf 结构示意:全局块、events 块、http/server/location 嵌套层级

配置从外到内:全局 → events → http → server → location。

2.1 全局块

全局块配置影响整个 Nginx 进程的运行参数:

user nginx;
worker_processes auto;
# error_log  logs/error.log;
# error_log  logs/error.log  notice;
# error_log  logs/error.log  info;
# pid        logs/nginx.pid;

2.2 events 块

events 块设置 Nginx 的工作模式与连接上限:

events {
    worker_connections 65535;
}

2.3 http 块

http 块是整个配置的核心,大部分 HTTP 相关指令(MIME 类型、日志、超时、虚拟主机等)都在此处定义:

http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile      on;          # 启用高效文件传输模式
    keepalive_timeout 65;

    include /etc/nginx/conf.d/*.conf;

    server {
        listen      80;
        server_name localhost;
        # access_log  logs/host.access.log  main;

        location / {
            root  html;
            index index.html index.htm;
        }
    }
}

主要参数说明

# 精确匹配
server_name www.example.com;
# 左侧通配
server_name *.example.com;
# 右侧通配
server_name www.example.*;
# 正则匹配
server_name ~^www\.example\.*$;

2.4 location 块

location 块用于配置不同 URL 路径的处理规则,支持反向代理、重定向、静态文件服务等:

location / {
    root  html;
    index index.html index.htm;
}
location = /50x.html {
    root html;
}

location 匹配规则(优先级从高到低):

=    # 精确匹配,优先级最高
^~   # 前缀匹配,匹配成功后停止搜索其他规则
~    # 正则匹配,区分大小写
~*   # 正则匹配,不区分大小写
/    # 通用前缀匹配,兜底规则

location 匹配优先级对照

优先级前缀匹配类型示例说明
1(最高)=精确匹配location = /login仅匹配 /login 本身,命中即停止搜索
2^~前缀匹配(优先)location ^~ /static/匹配 /static/ 前缀后立即停止,不再尝试正则
3~正则匹配(区分大小写)location ~ \.php$按配置顺序依次尝试,第一个匹配成功即生效
3~*正则匹配(不区分大小写)location ~* \.(jpg|png)$~ 优先级相同,按声明顺序匹配
4(最低)无前缀 /普通前缀匹配location /最长前缀匹配,常作兜底规则

易错点~/~* 之间没有优先级区分,命中顺序取决于配置文件中的先后声明顺序,而非正则的“精确程度”——把更具体的正则写在前面,否则可能被范围更宽的规则提前拦截。

location 配置示例:

server {
    listen      80;
    server_name www.example.com;

    # 精确匹配 /web02
    location = /web02 {
        root  /usr/local/nginx/html;
        index index.html index.htm;
    }

    # 精确匹配 /web01
    location = /web01 {
        root /usr/local/nginx/html;
    }

    # 正则匹配图片资源(区分大小写)
    location ~ \.(jpeg|jpg|png)$ {
        root /usr/local/nginx/html;
    }
}

3. 常用配置示例

3.1 反向代理

反向代理通过 proxy_pass 指令将请求转发到后端服务器。转发时通常需要透传客户端信息(真实 IP、Host 等):

upstream app {
    server 10.10.10.100:8090;
    server 10.10.10.101:8090;
}

server {
    listen      8090;
    server_name www.example.com;

    location / {
        proxy_pass         http://app;
        proxy_set_header   Host              $host;
        proxy_set_header   X-Real-IP         $remote_addr;
        proxy_set_header   X-Forwarded-For   $proxy_add_x_forwarded_for;
    }
}

反向代理与动静分离示意:Client 经 Nginx 转发到后端,静态资源本地/CDN 返回、动态请求 proxy_pass

静态本地/CDN 返回,动态经 proxy_pass 打到后端。

3.2 负载均衡

算法与 L4/L7 选型见负载均衡;这里只给 Nginx upstream 常用写法。

负载均衡通过 upstream 定义后端节点池,proxy_pass 将流量打入池中。Nginx 支持三种主要策略(参见负载均衡):

一句话upstream 定义后端池,proxy_pass 把流量打进去——权重、ip_hashleast_conn 都在这一层选。

加权轮询(默认)

upstream web_weighted {
    server 10.10.10.100 weight=1;
    server 10.10.10.101 weight=2;
    server 10.10.10.102 down;   # 临时下线
}

IP 哈希(会话粘滞)

upstream web_iphash {
    ip_hash;
    server backend1.example.com;
    server backend2.example.com;
    server backend3.example.com;
}

最少连接

upstream web_leastconn {
    least_conn;
    server backend1.example.com;
    server backend2.example.com;
    server backend3.example.com;
}

使用方式统一通过 proxy_pass 引用:

server {
    listen 80;
    location / {
        proxy_pass http://web_weighted;
    }
}

3.3 HTTPS

获取域名的 SSL 证书和私钥后,在 server 块中配置如下:

server {
    listen      443 ssl;
    server_name www.example.com;

    ssl_certificate        /path/to/your_certificate.pem;
    ssl_certificate_key    /path/to/your_private.key;
    ssl_session_cache      shared:SSL:10m;
    ssl_session_timeout    1d;
    ssl_session_tickets    off;

    # 只保留现代协议,禁用已弃用的 TLS 1.0/1.1
    ssl_protocols          TLSv1.2 TLSv1.3;
    # TLS 1.3 的套件由 OpenSSL 内置管理;下面这行仅约束 TLS 1.2 的套件
    ssl_ciphers            ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
    # TLS 1.3 下客户端优先,1.2 下服务端优先即可,无需强制服务端顺序
    ssl_prefer_server_ciphers off;

    location / {
        root  html;
        index index.html index.htm;
    }
}

为什么不再照抄 HIGH:!aNULL:!MD5HIGH 是一个随 OpenSSL 版本变化的宽泛别名,可能纳入 CBC 等已不推荐的套件,且无法表达”只走 ECDHE 前向保密 + AEAD”的现代要求。生产建议直接锁定 TLSv1.2 TLSv1.3 与上面这类明确的 ECDHE-AEAD 套件列表(可用 Mozilla SSL Configuration Generator 按 nginx 版本生成 intermediate 配置),并配合 OCSP Stapling、HSTS 一起上线。

生产环境建议同时配置 HTTP → HTTPS 的强制跳转,以及 HTTP/2(新版本用 http2 on; 指令,旧版本用 listen 443 ssl http2)以提升并发性能。

3.4 限流

Nginx 通过 ngx_http_limit_req_module 模块限制请求速率,保护后端免受突发过载或恶意请求冲击(各算法原理详见服务限流;负载均衡层面的流量控制背景可参见负载均衡)。

措辞纠正limit_req 的实现更接近漏桶(leaky bucket)——请求以 rate 指定的固定速率被”漏出”放行,burst 相当于桶的容量(可排队的额外请求数),超出即拒绝。它不是严格意义上的令牌桶:令牌桶允许把攒下的令牌一次性用于瞬时突发,而 limit_req 即使在 burst 之内也仍按固定间隔放行(除非加 nodelay)。因此下文中的 rate 应理解为”漏出/放行速率”,而非”令牌补充速率”。

http {
    # 定义限流区:按客户端 IP 限速,共享内存 10MB,速率 10 req/s
    limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;

    server {
        listen 80;

        location / {
            # burst=20:允许最多 20 个排队请求;nodelay:超出 burst 立即返回 503
            limit_req zone=mylimit burst=20 nodelay;
            proxy_pass http://backend;
        }
    }
}

默认超限返回 503,可用 limit_req_status 429; 改为更语义化的 429 Too Many Requests。

3.5 动静分离

动静分离的核心思路:静态资源由 Nginx 直接服务(避免经过 JVM/Python 等运行时),动态请求才走 proxy_pass 打到后端,既节省后端资源,又可对静态文件单独设置缓存头。

upstream app {
    server 127.0.0.1:8080;
}

server {
    listen 80;
    server_name www.example.com;

    # 静态资源:Nginx 直出,设置长效缓存
    location ~* \.(html|css|js|jpg|jpeg|png|gif|ico|svg|woff2?)$ {
        root   /var/www/static;
        expires 7d;
        add_header Cache-Control "public, max-age=604800, immutable";
    }

    # API 请求:转发到后端
    location /api/ {
        proxy_pass http://app;
    }

    # 兜底:其余路径(如 SPA history 模式)也走后端
    location / {
        proxy_pass http://app;
    }
}

生产上静态目录通常通过 CI/CD 同步或挂载 CDN;动静分离也可在 CDN 层实现(静态回源 OSS,动态回源 LB),两层不互斥。

3.6 Proxy 头与超时

proxy_pass 转发时,若不显式透传头,后端收到的 Host 会变成 upstream 地址,REMOTE_ADDR 会变成 Nginx 本机 IP。以下是生产环境的标准写法:

location / {
    proxy_pass http://app;

    # 透传原始 Host,避免后端虚拟主机匹配错误
    proxy_set_header Host              $host;
    # 客户端真实 IP
    proxy_set_header X-Real-IP         $remote_addr;
    # 完整代理链(多层代理时追加)
    proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
    # 原始协议(HTTP/HTTPS),后端用于生成正确的跳转链接
    proxy_set_header X-Forwarded-Proto $scheme;

    # 三段超时缺一不可(单位:秒)
    proxy_connect_timeout  5s;    # 与后端建立 TCP 连接的超时
    proxy_read_timeout     60s;   # 等待后端响应数据的超时(含业务处理时间)
    proxy_send_timeout     60s;   # 向后端发送请求体的超时
}

超时设置过小会导致正常慢请求被截断;设置过大则故障节点会长时间占用连接。生产建议结合业务 P99 RT 设定,并与服务端超时保持一致(参见依赖韧性)。

proxy 超时参数对照

参数含义默认值生产建议值设置过小的风险设置过大的风险
proxy_connect_timeout与 upstream 建立 TCP 连接的超时60s3–5s后端瞬时抖动即被误判为不可用故障节点长时间占用等待队列,拖慢整体转发
proxy_read_timeout等待 upstream 返回响应数据的超时(含业务处理耗时)60s按 P99 RT × 1.5–2 倍设定正常慢请求(大查询、慢接口)被提前截断返回 504故障节点的连接迟迟不释放,占满 worker 连接数
proxy_send_timeout向 upstream 发送请求体的超时(两次写操作间的最大间隔)60sproxy_read_timeout 保持同量级大文件上传等场景可能被中途打断与 read 超时同理,故障连接占用时间变长

对齐原则:三段超时应与客户端超时、服务端框架超时(如 Spring MVC 的 server.tomcat.connection-timeout)形成一致的梯度——上游超时应略长于下游,否则会出现「Nginx 已经超时返回 504,但后端还在继续跑」的资源浪费。

3.7 Upstream 健康检查与摘除

Nginx 开源版内置被动健康检查:连续失败达阈值后自动摘除节点,待 fail_timeout 窗口过后再尝试恢复。

upstream app {
    # 60s 内连续失败 3 次则摘除,60s 后重新探测
    server 10.10.10.100:8080 max_fails=3 fail_timeout=60s;
    server 10.10.10.101:8080 max_fails=3 fail_timeout=60s;

    # backup:主节点全部不可用时才启用
    server 10.10.10.102:8080 backup;

    # down:手动标记永久下线(不参与调度、也不尝试恢复)
    # server 10.10.10.103:8080 down;
}

摘除与优雅排水down 适合发版前手动摘节点;发版完成后去掉 downnginx -s reload 即可重新上线。零停机发版建议结合上游 LB 或服务注册中心做优雅排水(详见负载均衡)。

开源版没有内置主动健康检查:上面的 max_fails / fail_timeout被动探测——只有真实请求打到故障节点失败后才会摘除,也就是说至少要”牺牲”几个真实请求才发现节点挂了。主动健康检查(周期性探活、故障节点不承接任何真实流量)需要 Nginx Plus 的 health_check 指令,或开源社区的 nginx_upstream_check_module(需重新编译打补丁)。竞价这类对错误率极敏感的链路,建议在上游 LB / 服务网格层做主动探活,而不是只依赖 Nginx 开源版的被动摘除。

3.8 常见踩坑

1. proxy_pass 末尾斜杠影响 URI 改写

# 不带斜杠:/api/v1/foo → 后端收到 /api/v1/foo(路径原样透传)
proxy_pass http://app;

# 带斜杠:/api/v1/foo → 后端收到 /v1/foo(去掉 location 前缀 /api)
proxy_pass http://app/;

规则:proxy_pass 末尾带 /(或带路径)时,Nginx 会将 location 匹配前缀替换掉。行为不一致是生产中最常见的 404 来源之一。

2. worker_connections 要与 ulimit -n 对齐

worker_connections 是 Nginx 的软上限,OS 文件描述符上限(ulimit -n)是硬上限——若后者更低,实际可建连接数会被 OS 截断。生产建议在 /etc/security/limits.conf 或 systemd unit 中将 nofile 设置为不低于 worker_processes × worker_connections,并通过 ulimit -n/proc/<pid>/limits 验证生效。

3. reload 与 restart 的区别

nginx -s reload 让 master 用新配置 fork 新 worker,旧 worker 优雅退出——已有连接不中断restart 会先 stop 再 start,存量长连接被强制关闭。生产上应始终优先 reload,reload 前务必执行 nginx -t 确认语法无误。

4. proxy_buffering 与 SSE / 流式响应

Nginx 默认 proxy_buffering on,会将后端响应缓冲完整后再发给客户端,导致 SSE、流式 AI 生成等场景客户端长时间收不到数据。解决方式:

location /stream/ {
    proxy_pass http://app;
    proxy_buffering        off;
    proxy_cache            off;
    proxy_read_timeout     3600s;   # 长连接需要更大的读超时
    chunked_transfer_encoding on;
}

4. 常用运维命令

# 校验配置文件语法(修改后必做)
nginx -t

# 热重载配置(不中断已有连接)
nginx -s reload

# 优雅关闭(等待当前请求处理完毕)
nginx -s quit

# 立即停止
nginx -s stop

若 Nginx 已注册为 systemd 服务:

systemctl start   nginx   # 启动
systemctl stop    nginx   # 停止
systemctl restart nginx   # 重启
systemctl reload  nginx   # 热重载

5. 生产检查清单

上线或变更 Nginx 配置前,逐项确认。

通用清单

小结

Nginx 在高性能系统中扮演流量入口的角色:静态资源直出减轻后端压力,proxy_pass + upstream 实现反向代理与负载均衡,limit_req 在边缘做第一道限流,SSL termination 统一卸载 TLS 开销。本系列前几篇讨论的负载均衡算法、缓存策略、限流机制,最终都要落到 Nginx(或其他网关)的配置上才能生效;其后的异步与削峰连接与 I/O 模型性能测试则分别回答「洪峰怎么削」「连接怎么撑」「容量怎么验」。

参考


views
Share this post on:

Previous Post
异步与削峰:用消息队列把洪峰挡在核心路径外
Next Post
CDN:就近访问、回源、缓存策略与防盗链