Skip to content
Published on

nginx 配置完全指南:从架构到生产优化的 15 个核心主题

分享
Authors

1. nginx 架构与配置结构

1.1 事件驱动架构:Master-Worker 模型

nginx 采用了与 Apache httpd 的进程/线程模型根本不同的事件驱动(Event-Driven)架构。这一设计哲学正是 nginx 能在单台服务器上处理数十万个并发连接的核心原因。

┌─────────────────────────────────────────────────────────┐
Master Process- 读取并校验配置文件                                    │
- 创建/管理 Worker 进程 (fork)- 端口绑定 (80, 443)- 信号处理 (reload, stop, reopen)└─────────┬───────────┬───────────┬───────────┬───────────┘
          │           │           │           │
    ┌─────▼─────┐ ┌───▼───┐ ┌───▼───┐ ┌───▼───┐
Worker 0  │ │Worker 1│ │Worker 2│ │Worker 3Event Loop │ │  ...   │ │  ...   │ │  ...    │ epoll/kq   │ │        │ │        │ │        │
    │ 数千 conn  │ │        │ │        │ │        │
    └───────────┘ └────────┘ └────────┘ └────────┘

Master Process 以 root 权限运行,负责解析配置文件、绑定端口和管理 Worker 进程。它通过 fork() 系统调用创建 Worker,重载配置时不会中断已有连接,而是先启动新的 Worker,再对旧 Worker 执行 graceful shutdown。

Worker Process 是实际处理客户端请求的核心单元。每个 Worker 运行独立的事件循环,利用操作系统的 I/O 多路复用机制(Linux 上是 epoll,FreeBSD/macOS 上是 kqueue)在不阻塞的情况下同时处理数千个连接。相比为每个连接分配一个线程的方式,上下文切换和内存开销大幅降低。

1.2 nginx.conf 的配置上下文结构

nginx 配置遵循分层的上下文(Context)结构。子上下文会继承父级的配置,若在子级重新声明相同指令则会覆盖父级。

# ============================================
# Main Context (全局配置)
# ============================================
user nginx;                          # 运行 Worker 进程的用户
worker_processes auto;               # 按 CPU 核心数创建 Worker
error_log /var/log/nginx/error.log warn;
pid /run/nginx.pid;

# ============================================
# Events Context (连接处理配置)
# ============================================
events {
    worker_connections 1024;         # 每个 Worker 的最大并发连接数
    multi_accept on;                 # 一次接受多个连接
    use epoll;                       # Linux 上使用 epoll (默认值)
}

# ============================================
# HTTP Context (HTTP 协议配置)
# ============================================
http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    # ========================================
    # Server Context (虚拟主机)
    # ========================================
    server {
        listen 80;
        server_name example.com;

        # ====================================
        # Location Context (URL 路径匹配)
        # ====================================
        location / {
            root /var/www/html;
            index index.html;
        }

        location /api/ {
            proxy_pass http://backend;
        }
    }
}

# ============================================
# Stream Context (TCP/UDP 代理, L4)
# ============================================
stream {
    server {
        listen 3306;
        proxy_pass mysql_backend;
    }
}

上下文层级小结:

上下文位置作用
Main最顶层全局配置 (user, worker, pid, error_log)
EventsMain 内部连接处理机制 (worker_connections)
HTTPMain 内部HTTP 协议相关的全部配置
ServerHTTP 内部虚拟主机(按域名配置)
LocationServer 内部按 URL 路径的请求处理规则
UpstreamHTTP 内部后端服务器组(负载均衡)
StreamMain 内部TCP/UDP L4 代理

1.3 配置文件结构化的最佳实践

在生产环境中,不要把所有配置塞进单个 nginx.conf,而应模块化管理。

/etc/nginx/
├── nginx.conf                    # 主配置 (用 include 拆分)
├── conf.d/                       # 公共配置片段
│   ├── ssl-params.conf           # SSL/TLS 公共参数
│   ├── proxy-params.conf         # 反向代理公共请求头
│   ├── security-headers.conf     # 安全响应头
│   └── gzip.conf                 # 压缩配置
├── sites-available/              # 按站点划分的配置文件
│   ├── example.com.conf
│   ├── api.example.com.conf
│   └── admin.example.com.conf
├── sites-enabled/                # 已启用的站点 (符号链接)
│   ├── example.com.conf -> ../sites-available/example.com.conf
│   └── api.example.com.conf -> ../sites-available/api.example.com.conf
└── snippets/                     # 可复用的配置片段
    ├── letsencrypt.conf
    └── fastcgi-php.conf
# nginx.conf 主文件
http {
    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*.conf;
}

2. Virtual Host / Server Block 配置

nginx 的 Server Block 相当于 Apache 的 Virtual Host,让一台服务器可以托管多个域名。

2.1 基本 Server Block 配置

# /etc/nginx/sites-available/example.com.conf

# ── 主域名 ──
server {
    listen 80;
    listen [::]:80;                          # IPv6 支持
    server_name example.com www.example.com;

    root /var/www/example.com/html;
    index index.html index.htm;

    # 按域名拆分访问日志
    access_log /var/log/nginx/example.com.access.log;
    error_log /var/log/nginx/example.com.error.log;

    location / {
        try_files $uri $uri/ =404;
    }
}

# ── 第二个域名 ──
server {
    listen 80;
    listen [::]:80;
    server_name blog.example.com;

    root /var/www/blog.example.com/html;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }
}

2.2 Default Server (catch-all)

这是处理未定义域名请求的默认 server 块。出于安全考虑,推荐用 444(直接断开连接)响应。

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name _;                           # 所有未匹配的域名

    # 未定义主机的请求立即断开连接
    return 444;
}

2.3 Server Name 匹配优先级

nginx 匹配 server_name 时遵循以下优先级:

  1. 精确名称server_name example.com
  2. 前置通配符server_name *.example.com
  3. 后置通配符server_name example.*
  4. 正则表达式server_name ~^(?<subdomain>.+)\.example\.com$
  5. default_server:以上全部未匹配时
# 用正则表达式捕获子域名
server {
    listen 80;
    server_name ~^(?<subdomain>.+)\.example\.com$;

    location / {
        root /var/www/$subdomain;
    }
}

2.4 站点的启用与停用

# 启用站点
sudo ln -s /etc/nginx/sites-available/example.com.conf \
           /etc/nginx/sites-enabled/example.com.conf

# 校验配置
sudo nginx -t

# 重载(不中断服务)
sudo systemctl reload nginx

3. 反向代理配置

3.1 基本 Reverse Proxy

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

    location / {
        proxy_pass http://127.0.0.1:3000;

        # ── 必需的代理请求头 ──
        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_set_header X-Forwarded-Host  $host;
        proxy_set_header X-Forwarded-Port  $server_port;
    }
}

各请求头的作用:

请求头用途
Host传递原始请求的 Host 头
X-Real-IP真实客户端 IP(在代理之后识别原始 IP)
X-Forwarded-For经过代理链累积的客户端 IP 列表
X-Forwarded-Proto原始协议 (http/https) —— 后端据此判断是否重定向
X-Forwarded-Host原始 Host 头
X-Forwarded-Port原始端口

3.2 可复用的代理参数片段

# /etc/nginx/conf.d/proxy-params.conf
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_set_header X-Forwarded-Host  $host;
proxy_set_header X-Forwarded-Port  $server_port;

proxy_http_version 1.1;
proxy_connect_timeout 60s;
proxy_send_timeout    60s;
proxy_read_timeout    60s;
proxy_buffering on;
# 在 Server Block 中用 include 复用
location / {
    proxy_pass http://backend;
    include /etc/nginx/conf.d/proxy-params.conf;
}

3.3 WebSocket 代理

WebSocket 使用 HTTP Upgrade 机制,因此必须显式传递 UpgradeConnection 这两个逐跳(hop-by-hop)头。nginx 默认不会转发这些头。

# ── 用 map 动态设置 Connection 头 ──
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

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

    location /ws/ {
        proxy_pass http://127.0.0.1:8080;

        # WebSocket 必需配置
        proxy_http_version 1.1;                          # 必须用 HTTP/1.1 (支持 Upgrade)
        proxy_set_header Upgrade    $http_upgrade;       # 传递客户端的 Upgrade 头
        proxy_set_header Connection $connection_upgrade; # 动态 Connection 头

        # 常规代理请求头
        proxy_set_header Host            $host;
        proxy_set_header X-Real-IP       $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        # WebSocket 是长连接,需要延长超时
        proxy_read_timeout  86400s;    # 24 小时 (默认 60 秒会断开空闲连接)
        proxy_send_timeout  86400s;
    }
}

3.4 基于路径的路由(微服务)

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

    # 用户服务
    location /api/users/ {
        proxy_pass http://user-service:3001/;     # 注意结尾的 /: 去掉 /api/users/ 后再转发
        include /etc/nginx/conf.d/proxy-params.conf;
    }

    # 订单服务
    location /api/orders/ {
        proxy_pass http://order-service:3002/;
        include /etc/nginx/conf.d/proxy-params.conf;
    }

    # 支付服务
    location /api/payments/ {
        proxy_pass http://payment-service:3003/;
        include /etc/nginx/conf.d/proxy-params.conf;
        proxy_read_timeout 120s;                  # 支付需要延长超时
    }
}

注意proxy_pass URL 结尾若带 /location 匹配到的部分会被去掉。/api/users/123 请求会转发为 http://user-service:3001/123。若不带 /,则原样转发完整 URI。


4. 负载均衡

4.1 Upstream 块与算法

# ============================================
# 1. Round Robin (默认值)
# 按顺序分发请求
# ============================================
upstream backend_roundrobin {
    server 10.0.0.1:8080;           # 默认权重 1
    server 10.0.0.2:8080;
    server 10.0.0.3:8080;
}

# ============================================
# 2. Weighted Round Robin (加权轮询)
# 按服务器性能比例分发
# ============================================
upstream backend_weighted {
    server 10.0.0.1:8080 weight=5;  # 请求的 5/8
    server 10.0.0.2:8080 weight=2;  # 请求的 2/8
    server 10.0.0.3:8080 weight=1;  # 请求的 1/8
}

# ============================================
# 3. Least Connections (最少连接)
# 转发到活跃连接最少的服务器
# ============================================
upstream backend_leastconn {
    least_conn;
    server 10.0.0.1:8080;
    server 10.0.0.2:8080;
    server 10.0.0.3:8080;
}

# ============================================
# 4. IP Hash (会话保持)
# 相同客户端 IP → 相同服务器
# ============================================
upstream backend_iphash {
    ip_hash;
    server 10.0.0.1:8080;
    server 10.0.0.2:8080;
    server 10.0.0.3:8080;
}

# ============================================
# 5. Generic Hash (自定义哈希)
# 基于任意变量的哈希
# ============================================
upstream backend_hash {
    hash $request_uri consistent;   # 基于 URI + 一致性哈希
    server 10.0.0.1:8080;
    server 10.0.0.2:8080;
    server 10.0.0.3:8080;
}

算法选择指南:

算法适用场景注意事项
Round Robin无状态服务、配置相同的服务器服务器配置有差异时会失衡
Weighted服务器配置不同的情况需要手动维护权重
Least Connections请求处理时间波动大的情况若以短请求为主则与 Round Robin 相近
IP Hash需要会话保持的情况(遗留应用)增减服务器时会发生重新分配
Generic Hash缓存优化(相同 URI → 相同服务器)推荐使用 consistent 哈希

4.2 服务器状态与备份

upstream backend {
    least_conn;

    server 10.0.0.1:8080;                         # 正常服务器
    server 10.0.0.2:8080;                         # 正常服务器
    server 10.0.0.3:8080 backup;                  # 备份: 上面服务器全部宕机时启用
    server 10.0.0.4:8080 down;                    # 停用 (维护中)

    server 10.0.0.5:8080 max_fails=3 fail_timeout=30s;
    # max_fails=3: 30 秒内失败 3 次判定为异常
    # fail_timeout=30s: 判定异常后 30 秒内不再分配请求
}

4.3 Keepalive 连接池

复用与后端服务器之间的 TCP 连接,降低连接建立/释放的开销。

upstream backend {
    server 10.0.0.1:8080;
    server 10.0.0.2:8080;

    keepalive 32;                # 每个 Worker 保持的空闲连接数
    keepalive_requests 1000;     # 单个连接可处理的最大请求数
    keepalive_timeout 60s;       # 空闲连接的保持时间
}

server {
    location / {
        proxy_pass http://backend;
        proxy_http_version 1.1;                    # keepalive 必须用 HTTP/1.1
        proxy_set_header Connection "";             # 用空值代替 "close" 以启用 keepalive
    }
}

5. SSL/TLS 配置

5.1 基本 HTTPS 配置

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name example.com www.example.com;

    # ── 证书 ──
    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # ── 协议 ──
    ssl_protocols TLSv1.2 TLSv1.3;              # 停用 TLS 1.0, 1.1

    # ── Cipher Suites (用于 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;
    ssl_prefer_server_ciphers off;               # TLS 1.3 下推荐 off

    # ── Elliptic Curves ──
    ssl_ecdh_curve X25519:secp384r1:secp256r1;

    # ── 会话复用 ──
    ssl_session_cache shared:SSL:10m;            # 10MB = 约 40,000 个会话
    ssl_session_timeout 1d;                      # 会话有效期
    ssl_session_tickets off;                     # 保证 Forward Secrecy

    root /var/www/example.com/html;
    index index.html;
}

5.2 HTTP → HTTPS 重定向

server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;

    # 所有 HTTP 请求 301 重定向到 HTTPS
    return 301 https://$host$request_uri;
}

5.3 HSTS (HTTP Strict Transport Security)

# 添加到 HTTPS server block 内部
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
  • max-age=63072000:2 年内只使用 HTTPS(建议至少 1 年)
  • includeSubDomains:对所有子域名同样生效
  • preload:具备登记到浏览器 HSTS Preload 列表的资格
  • always:即使是错误响应(4xx、5xx)也发送该头

5.4 OCSP Stapling

OCSP Stapling 由服务器代为完成证书有效性校验,省去客户端直接查询 CA 的过程。既提升了初次连接速度,也保护了隐私。

ssl_stapling on;
ssl_stapling_verify on;

# 用于校验 OCSP 响应的信任链 (包含 Let's Encrypt 中间证书)
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;

# 用于查询 OCSP 响应服务器的 DNS resolver
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;

5.5 生产环境 SSL 整合片段

# /etc/nginx/conf.d/ssl-params.conf
ssl_protocols TLSv1.2 TLSv1.3;
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;
ssl_prefer_server_ciphers off;
ssl_ecdh_curve X25519:secp384r1:secp256r1;

ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;

ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;

add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
# 在 Server Block 中 include
server {
    listen 443 ssl http2;
    server_name example.com;

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    include /etc/nginx/conf.d/ssl-params.conf;

    # ... 其余配置
}

5.6 Let's Encrypt 自动续期 (Certbot)

# 签发证书
sudo certbot --nginx -d example.com -d www.example.com

# 测试自动续期
sudo certbot renew --dry-run

# Cron 自动续期 (certbot 通常已自行配置,此处显式写出)
echo "0 0,12 * * * root certbot renew --quiet --deploy-hook 'systemctl reload nginx'" \
  | sudo tee /etc/cron.d/certbot-renew

6. 缓存配置

6.1 Proxy Cache(反向代理缓存)

# ── 在 HTTP Context 中定义缓存路径 ──
proxy_cache_path /var/cache/nginx/proxy
    levels=1:2                       # 两级目录结构 (分散文件)
    keys_zone=proxy_cache:10m        # 存放缓存键的共享内存 (1MB ≈ 8,000 个键)
    max_size=1g                      # 磁盘缓存最大容量
    inactive=60m                     # 60 分钟未使用的缓存会被删除
    use_temp_path=off;               # 不使用临时文件路径 (直接写入 → 提升性能)

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

    location / {
        proxy_pass http://backend;
        proxy_cache proxy_cache;                   # 指定缓存区
        proxy_cache_valid 200 302 10m;             # 200, 302 响应缓存 10 分钟
        proxy_cache_valid 404     1m;              # 404 响应缓存 1 分钟
        proxy_cache_use_stale error timeout
                              updating
                              http_500 http_502
                              http_503 http_504;   # 后端出错时提供 stale 缓存
        proxy_cache_lock on;                       # 并发请求时只放行一个到后端
        proxy_cache_min_uses 2;                    # 只缓存被请求 2 次以上的 URL

        # 用于调试的缓存状态响应头
        add_header X-Cache-Status $upstream_cache_status;
    }

    # 绕过缓存 (管理员用)
    location /api/ {
        proxy_pass http://backend;
        proxy_cache proxy_cache;

        # Cookie 中含 nocache,或请求头带 Cache-Control: no-cache 时绕过
        proxy_cache_bypass $http_cache_control;
        proxy_no_cache $cookie_nocache;
    }
}

X-Cache-Status 的取值:

状态含义
HIT直接由缓存提供
MISS无缓存 → 请求后端
EXPIRED缓存已过期 → 重新请求后端
STALE已过期但按 stale 策略提供
UPDATING更新过程中提供 stale 缓存
BYPASS已绕过缓存

6.2 FastCGI Cache(PHP 等)

fastcgi_cache_path /var/cache/nginx/fastcgi
    levels=1:2
    keys_zone=fastcgi_cache:10m
    max_size=512m
    inactive=30m;

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

    # 定义绕过缓存的条件
    set $skip_cache 0;

    # POST 请求不缓存
    if ($request_method = POST) {
        set $skip_cache 1;
    }

    # 带查询字符串时不缓存
    if ($query_string != "") {
        set $skip_cache 1;
    }

    # 排除 WordPress 管理页面
    if ($request_uri ~* "/wp-admin/|/xmlrpc.php|wp-.*.php") {
        set $skip_cache 1;
    }

    # 排除已登录用户
    if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in") {
        set $skip_cache 1;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/run/php/php-fpm.sock;
        fastcgi_index index.php;
        include fastcgi_params;

        fastcgi_cache fastcgi_cache;
        fastcgi_cache_valid 200 30m;
        fastcgi_cache_bypass $skip_cache;
        fastcgi_no_cache $skip_cache;
        fastcgi_cache_use_stale error timeout updating http_500;

        add_header X-FastCGI-Cache $upstream_cache_status;
    }
}

6.3 浏览器缓存(静态资源)

# ── 对静态文件设置长期浏览器缓存 ──
location ~* \.(jpg|jpeg|png|gif|ico|webp|avif|svg)$ {
    expires 30d;                                   # 设置 Expires 头
    add_header Cache-Control "public, immutable";  # 浏览器无需重新验证即可使用缓存
    access_log off;                                # 关闭静态文件日志 (减少 I/O)
}

location ~* \.(css|js)$ {
    expires 7d;
    add_header Cache-Control "public";
}

location ~* \.(woff|woff2|ttf|eot)$ {
    expires 365d;
    add_header Cache-Control "public, immutable";
    add_header Access-Control-Allow-Origin "*";    # 字体 CORS
}

# ── HTML 使用短缓存或 no-cache ──
location ~* \.html$ {
    expires -1;                                    # no-cache
    add_header Cache-Control "no-store, no-cache, must-revalidate";
}

7. Rate Limiting 与 Connection Limiting

7.1 Rate Limiting(请求速率限制)

nginx 的 Rate Limiting 使用漏桶(Leaky Bucket)算法。请求进入桶(zone),并按设定的速率(rate)被处理。

# ── 在 HTTP Context 中定义 Zone ──
# 键: 客户端 IP
# 共享内存: 10MB (约可存 160,000 个 IP 地址)
# 速率: 每秒 10 个请求
limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;

# 登录端点: 每秒 1 个 (防止 Brute Force)
limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s;

# API 端点: 每秒 50 个
limit_req_zone $binary_remote_addr zone=api:10m rate=50r/s;

# 超出 Rate Limit 时的日志级别
limit_req_status 429;                  # 用 429 Too Many Requests 代替默认的 503
limit_req_log_level warn;

server {
    listen 80;
    server_name example.com;

    # ── 普通页面 ──
    location / {
        limit_req zone=general burst=20 nodelay;
        # burst=20: 瞬时最多允许超出 20 个
        # nodelay: burst 范围内的请求不延迟,立即处理
        proxy_pass http://backend;
    }

    # ── 登录页面 ──
    location /login {
        limit_req zone=login burst=5 nodelay;
        proxy_pass http://backend;
    }

    # ── API ──
    location /api/ {
        limit_req zone=api burst=100 nodelay;
        proxy_pass http://backend;
    }
}

burst 与 nodelay 的工作原理:

Rate: 10r/s, Burst: 20

时刻 0 到达 30 个请求:
├── 10 : 立即处理 (rate 范围内)
├── 20 : 存入 burst 队列
│         无 nodelay 时: 以 100ms 间隔处理 (2)
│         有 nodelay 时: 立即处理 (仅占用队列槽位)
└── 其余: 429 拒绝

7.2 Connection Limiting(并发连接限制)

# 限制每个客户端 IP 的并发连接数
limit_conn_zone $binary_remote_addr zone=conn_per_ip:10m;

# 限制整台服务器的并发连接数
limit_conn_zone $server_name zone=conn_per_server:10m;

limit_conn_status 429;
limit_conn_log_level warn;

server {
    listen 80;
    server_name example.com;

    # 每个 IP 最多 100 个并发连接
    limit_conn conn_per_ip 100;

    # 每台服务器最多 10,000 个并发连接
    limit_conn conn_per_server 10000;

    # 下载带宽限制 (可选)
    location /downloads/ {
        limit_conn conn_per_ip 5;        # 下载按 IP 限制为 5 个
        limit_rate 500k;                 # 每个连接限速 500KB/s
        limit_rate_after 10m;            # 前 10MB 不限速,之后开始限速
    }
}

7.3 IP 白名单与 Rate Limiting 结合

# 内网免除 Rate Limiting
geo $limit {
    default        1;
    10.0.0.0/8     0;    # 内网
    192.168.0.0/16 0;    # 内网
    172.16.0.0/12  0;    # 内网
}

map $limit $limit_key {
    0 "";                  # 空键 → 不应用 Rate Limiting
    1 $binary_remote_addr; # 外部 IP → 应用 Rate Limiting
}

limit_req_zone $limit_key zone=api:10m rate=10r/s;

8. Gzip/Brotli 压缩

8.1 Gzip 压缩配置

# /etc/nginx/conf.d/gzip.conf

gzip on;
gzip_vary on;                         # 添加 Vary: Accept-Encoding 头
gzip_proxied any;                     # 代理响应也压缩
gzip_comp_level 6;                    # 压缩级别 (1-9, 6 是性能/压缩率的平衡点)
gzip_min_length 1000;                 # 小于 1KB 的文件压缩无效 → 排除
gzip_buffers 16 8k;                   # 压缩缓冲区

gzip_types
    text/plain
    text/css
    text/javascript
    text/xml
    application/javascript
    application/json
    application/xml
    application/rss+xml
    application/atom+xml
    application/vnd.ms-fontobject
    font/opentype
    font/ttf
    image/svg+xml
    image/x-icon;

# 排除已压缩的文件 (图片、视频等不列入 gzip_types)
gzip_disable "msie6";                 # 对 IE6 停用 (遗留)

8.2 Brotli 压缩配置

Brotli 比 Gzip 提供高出 15-25% 的压缩率。绝大多数现代浏览器都支持,在 nginx 中需要 ngx_brotli 模块。

# Brotli 动态压缩
brotli on;
brotli_comp_level 6;                  # 动态压缩推荐 6 (11 会让 CPU 过载)
brotli_min_length 1000;

brotli_types
    text/plain
    text/css
    text/javascript
    text/xml
    application/javascript
    application/json
    application/xml
    application/rss+xml
    font/opentype
    font/ttf
    image/svg+xml;

# Brotli 静态压缩 (提供预先压缩好的 .br 文件)
brotli_static on;

8.3 Dual Compression 策略

对支持 Brotli 的浏览器用 Brotli,对不支持的浏览器回退到 Gzip。

# 构建时预压缩静态文件 (CI/CD 流水线)
# gzip -k -9 dist/**/*.{js,css,html,json,svg}
# brotli -k -q 11 dist/**/*.{js,css,html,json,svg}

# nginx 配置
brotli_static on;             # 存在 .br 文件时优先提供
gzip_static on;               # 存在 .gz 文件时提供 (不支持 Brotli 时)
gzip on;                      # 没有预压缩文件时动态 gzip

压缩性能对比:

算法压缩率 (普通 JS)CPU 负载 (动态)浏览器支持
Gzip L670-75%99%+
Brotli L675-80%96%+
Brotli L1180-85%高 (仅限静态)96%+

9. 安全响应头

9.1 综合安全响应头配置

# /etc/nginx/conf.d/security-headers.conf

# ── 防止 Clickjacking ──
add_header X-Frame-Options "DENY" always;
# DENY: 任何站点都不能用 iframe 嵌入
# SAMEORIGIN: 仅同域名允许 iframe
# 参考: CSP frame-ancestors 是更现代的替代方案

# ── 防止 MIME 类型嗅探 ──
add_header X-Content-Type-Options "nosniff" always;
# 防止浏览器忽略 Content-Type 而自行判断

# ── XSS 过滤器 (遗留, 现代浏览器使用 CSP) ──
add_header X-XSS-Protection "0" always;
# 最新建议: "0" (停用) —— CSP 更安全也更准确

# ── 控制 Referrer 信息 ──
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# 同域名: 发送完整 URL
# 不同域名: 只发送 origin(域名)
# HTTP→HTTPS 降级: 不发送

# ── 权限策略 (控制摄像头、麦克风、定位等) ──
add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=()" always;
# 停用所有功能 (只放行需要的)

# ── HSTS ──
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

# ── Cross-Origin 策略 ──
add_header Cross-Origin-Opener-Policy "same-origin" always;
add_header Cross-Origin-Embedder-Policy "require-corp" always;
add_header Cross-Origin-Resource-Policy "same-origin" always;

9.2 Content Security Policy (CSP)

CSP 是最强大的安全响应头,但配置复杂。推荐先以 Report-Only 模式起步,监控违规情况后再逐步启用。

# ── 第 1 阶段: Report-Only 模式 (不拦截,仅上报违规) ──
add_header Content-Security-Policy-Report-Only
    "default-src 'self';
     script-src 'self' https://cdn.example.com;
     style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
     img-src 'self' data: https:;
     font-src 'self' https://fonts.gstatic.com;
     connect-src 'self' https://api.example.com;
     frame-ancestors 'none';
     base-uri 'self';
     form-action 'self';
     report-uri /csp-report;" always;

# ── 第 2 阶段: 正式启用 (违规时拦截) ──
add_header Content-Security-Policy
    "default-src 'self';
     script-src 'self' https://cdn.example.com;
     style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
     img-src 'self' data: https:;
     font-src 'self' https://fonts.gstatic.com;
     connect-src 'self' https://api.example.com;
     frame-ancestors 'none';
     base-uri 'self';
     form-action 'self';" always;

9.3 在 Server Block 中统一应用

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

    include /etc/nginx/conf.d/ssl-params.conf;
    include /etc/nginx/conf.d/security-headers.conf;

    # 安全相关的补充配置
    server_tokens off;                   # 隐藏 nginx 版本信息
    more_clear_headers Server;           # 移除 Server 头 (headers-more 模块)

    # ...
}

10. 访问控制 (Access Control)

10.1 基于 IP 的访问控制

# ── 管理页面: 仅允许特定 IP ──
location /admin/ {
    allow 10.0.0.0/8;            # 内网
    allow 203.0.113.50;          # 特定管理员 IP
    deny all;                    # 其余全部拒绝

    proxy_pass http://backend;
}

# ── 屏蔽特定 IP ──
location / {
    deny 192.168.1.100;          # 屏蔽特定 IP
    deny 10.0.0.0/24;            # 屏蔽子网
    allow all;                   # 其余放行
    # 注意: allow/deny 的顺序很重要! 先匹配到的规则生效

    proxy_pass http://backend;
}

10.2 HTTP Basic Authentication

# 创建 htpasswd 文件
sudo apt install apache2-utils          # Debian/Ubuntu
# sudo yum install httpd-tools          # RHEL/CentOS

# 创建用户 (-c: 新建文件, -B: bcrypt 哈希)
sudo htpasswd -cB /etc/nginx/.htpasswd admin

# 追加用户
sudo htpasswd -B /etc/nginx/.htpasswd developer
# ── 对特定路径启用 Basic Auth ──
location /admin/ {
    auth_basic "Administrator Area";               # 认证提示信息
    auth_basic_user_file /etc/nginx/.htpasswd;     # 密码文件

    proxy_pass http://backend;
}

# ── 仅对特定路径免除认证 ──
location /admin/health {
    auth_basic off;                                # 健康检查免除认证
    proxy_pass http://backend;
}

10.3 IP + Auth 组合(satisfy 指令)

location /admin/ {
    # satisfy any → IP 放行 OR 认证成功,满足其一即可访问
    # satisfy all → IP 放行 AND 认证成功,两者都满足才能访问
    satisfy any;

    # IP 白名单
    allow 10.0.0.0/8;
    deny all;

    # Basic Auth (从 IP 白名单之外访问时)
    auth_basic "Restricted";
    auth_basic_user_file /etc/nginx/.htpasswd;

    proxy_pass http://backend;
}

10.4 基于 GeoIP 的访问控制

# 需要 GeoIP2 模块 (ngx_http_geoip2_module)
geoip2 /usr/share/GeoIP/GeoLite2-Country.mmdb {
    auto_reload 60m;
    $geoip2_data_country_code country iso_code;
}

# 屏蔽特定国家
map $geoip2_data_country_code $allowed_country {
    default yes;
    CN      no;    # 中国
    RU      no;    # 俄罗斯
}

server {
    if ($allowed_country = no) {
        return 403;
    }
}

11. 日志配置

11.1 基本日志配置

# ── Access Log ──
# 默认 log_format: combined
log_format combined '$remote_addr - $remote_user [$time_local] '
                    '"$request" $status $body_bytes_sent '
                    '"$http_referer" "$http_user_agent"';

access_log /var/log/nginx/access.log combined;

# ── Error Log ──
# 级别: debug, info, notice, warn, error, crit, alert, emerg
error_log /var/log/nginx/error.log warn;

11.2 JSON 日志格式(对接日志分析工具)

JSON 形式的结构化日志,对于对接 Elasticsearch、Datadog、Splunk 这类分析工具是必不可少的。

log_format json_combined escape=json
    '{'
        '"time":"$time_iso8601",'
        '"remote_addr":"$remote_addr",'
        '"remote_user":"$remote_user",'
        '"request_method":"$request_method",'
        '"request_uri":"$request_uri",'
        '"server_protocol":"$server_protocol",'
        '"status":$status,'
        '"body_bytes_sent":$body_bytes_sent,'
        '"request_time":$request_time,'
        '"http_referer":"$http_referer",'
        '"http_user_agent":"$http_user_agent",'
        '"http_x_forwarded_for":"$http_x_forwarded_for",'
        '"upstream_addr":"$upstream_addr",'
        '"upstream_status":"$upstream_status",'
        '"upstream_response_time":"$upstream_response_time",'
        '"ssl_protocol":"$ssl_protocol",'
        '"ssl_cipher":"$ssl_cipher",'
        '"request_id":"$request_id"'
    '}';

access_log /var/log/nginx/access.json.log json_combined;

11.3 条件日志

# ── 排除健康检查请求的日志 ──
map $request_uri $loggable {
    ~*^/health   0;
    ~*^/ready    0;
    ~*^/metrics  0;
    default      1;
}

access_log /var/log/nginx/access.log combined if=$loggable;

# ── 只把出错请求单独记录 ──
map $status $is_error {
    ~^[45]  1;
    default 0;
}

access_log /var/log/nginx/error_requests.log combined if=$is_error;

# ── 记录慢请求 (1 秒以上) ──
map $request_time $is_slow {
    ~^[1-9]    1;    # 1 秒以上
    ~^[0-9]{2} 1;    # 10 秒以上
    default    0;
}

access_log /var/log/nginx/slow_requests.log json_combined if=$is_slow;

11.4 按域名拆分日志

server {
    server_name example.com;
    access_log /var/log/nginx/example.com.access.log json_combined;
    error_log /var/log/nginx/example.com.error.log warn;
}

server {
    server_name api.example.com;
    access_log /var/log/nginx/api.example.com.access.log json_combined;
    error_log /var/log/nginx/api.example.com.error.log warn;
}

11.5 日志轮转 (logrotate)

# /etc/logrotate.d/nginx
/var/log/nginx/*.log {
    daily                    # 每天轮转
    missingok                # 日志文件不存在也不报错
    rotate 14                # 保留 14 天
    compress                 # gzip 压缩
    delaycompress            # 上一个文件暂不压缩
    notifempty               # 空文件不轮转
    create 0640 nginx adm    # 新文件权限
    sharedscripts
    postrotate
        # 向 nginx 发送重新打开日志文件的信号
        if [ -f /run/nginx.pid ]; then
            kill -USR1 $(cat /run/nginx.pid)
        fi
    endscript
}

12. 性能调优

12.1 Worker 进程与连接

# ── Main Context ──
worker_processes auto;             # 按 CPU 核心数 (手动: 4, 8 等)
worker_rlimit_nofile 65535;        # 每个 Worker 的最大文件描述符数

events {
    worker_connections 4096;       # 每个 Worker 的最大并发连接数
    multi_accept on;               # 在一次事件循环中接受多个连接
    use epoll;                     # Linux: epoll (默认值)
}

最大并发连接数公式:

最大连接数 = worker_processes x worker_connections
: 4 workers x 4096 connections = 16,384 个并发连接

作为反向代理时 (客户端 + 后端共 2 个连接):
实际并发客户端 = 16,384 / 2 = 8,192

12.2 Keepalive 配置

http {
    # ── 客户端 Keepalive ──
    keepalive_timeout 65;          # 客户端 keepalive 保持时间 (秒)
    keepalive_requests 1000;       # 单个 keepalive 连接可处理的最大请求数

    # ── 超时 ──
    client_body_timeout 12;        # 接收客户端请求体的超时
    client_header_timeout 12;      # 接收客户端请求头的超时
    send_timeout 10;               # 向客户端发送响应的超时
    reset_timedout_connection on;  # 超时连接立即重置 (释放内存)
}

12.3 缓冲区配置

http {
    # ── 客户端请求缓冲区 ──
    client_body_buffer_size 16k;   # 请求体缓冲区 (超出则写入磁盘)
    client_header_buffer_size 1k;  # 请求头缓冲区
    client_max_body_size 100m;     # 最大上传大小 (默认 1MB)
    large_client_header_buffers 4 16k;  # 大请求头用的缓冲区

    # ── 代理缓冲区 ──
    proxy_buffers 16 32k;          # 存放后端响应的缓冲区
    proxy_buffer_size 16k;         # 第一段响应 (响应头) 缓冲区
    proxy_busy_buffers_size 64k;   # 正在发往客户端的缓冲区大小
    proxy_temp_file_write_size 64k; # 写入磁盘的临时文件大小
}

12.4 生产环境性能调优汇总

# /etc/nginx/nginx.conf —— 生产环境优化

user nginx;
worker_processes auto;
worker_rlimit_nofile 65535;
pid /run/nginx.pid;

events {
    worker_connections 4096;
    multi_accept on;
    use epoll;
}

http {
    # ── MIME 与基础配置 ──
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    charset utf-8;
    server_tokens off;              # 隐藏版本信息

    # ── 文件传输优化 ──
    sendfile on;                    # 内核级文件传输
    tcp_nopush on;                  # 与 sendfile 配合: 优化数据包
    tcp_nodelay on;                 # 停用 Nagle 算法
    aio on;                         # 异步 I/O

    # ── Keepalive ──
    keepalive_timeout 65;
    keepalive_requests 1000;

    # ── 超时 ──
    client_body_timeout 12;
    client_header_timeout 12;
    send_timeout 10;
    reset_timedout_connection on;

    # ── 缓冲区 ──
    client_body_buffer_size 16k;
    client_header_buffer_size 1k;
    client_max_body_size 100m;
    large_client_header_buffers 4 16k;

    # ── Open File Cache ──
    open_file_cache max=10000 inactive=30s;
    open_file_cache_valid 60s;
    open_file_cache_min_uses 2;
    open_file_cache_errors on;

    # ── 压缩、SSL、日志等的 include ──
    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*.conf;
}

13. URL Rewriting 与 Redirection

13.1 return 指令(推荐)

returnrewrite 更简单也更高效。大多数需要变更 URL 的场景,都应优先考虑 return

# ── HTTP → HTTPS 重定向 ──
server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$host$request_uri;
}

# ── www → non-www 归一化 ──
server {
    listen 443 ssl http2;
    server_name www.example.com;
    return 301 https://example.com$request_uri;
}

# ── 域名变更重定向 ──
server {
    listen 80;
    listen 443 ssl http2;
    server_name old-domain.com www.old-domain.com;
    return 301 https://new-domain.com$request_uri;
}

# ── 特定路径重定向 ──
location /old-page {
    return 301 /new-page;
}

# ── 维护模式 ──
location / {
    return 503;                  # Service Unavailable
}

# 与 error_page 组合
error_page 503 /maintenance.html;
location = /maintenance.html {
    root /var/www/html;
    internal;
}

13.2 rewrite 指令

rewrite 用于需要基于正则表达式做 URL 转换的场景。

# ── 基本语法 ──
# rewrite regex replacement [flag];
# flag: last | break | redirect (302) | permanent (301)

# ── 转换为不带版本号的 API 路径 ──
rewrite ^/api/v1/(.*)$ /api/$1 last;
# /api/v1/users → 内部重写为 /api/users
# last: 重新开始 location 匹配

# ── 去掉扩展名 (Clean URL) ──
rewrite ^/(.*)\.html$ /$1 permanent;
# /about.html → /about (301 重定向)

# ── 带查询字符串的重写 ──
rewrite ^/search/(.*)$ /search?q=$1? last;
# 在结尾加 ? 可去掉原有的查询字符串

# ── 多语言 URL ──
rewrite ^/ko/(.*)$ /$1?lang=ko last;
rewrite ^/en/(.*)$ /$1?lang=en last;
rewrite ^/ja/(.*)$ /$1?lang=ja last;

rewrite 标志对比:

标志行为用途
last重写后重新匹配 location变更内部路由
break重写后在当前 location 内继续处理当前块内的转换
redirect302 临时重定向临时迁移
permanent301 永久重定向永久迁移

13.3 try_files 指令

try_files 会按顺序检查文件/目录是否存在,在 SPA 或各类框架中不可或缺。

# ── SPA (React、Vue、Angular 等) ──
location / {
    root /var/www/spa;
    try_files $uri $uri/ /index.html;
    # 1. $uri: 检查请求的文件是否存在
    # 2. $uri/: 检查目录是否存在
    # 3. /index.html: 以上都没有则返回 index.html (SPA 路由)
}

# ── Next.js / Nuxt.js ──
location / {
    try_files $uri $uri/ @proxy;
}
location @proxy {
    proxy_pass http://127.0.0.1:3000;
    include /etc/nginx/conf.d/proxy-params.conf;
}

# ── PHP (WordPress、Laravel) ──
location / {
    try_files $uri $uri/ /index.php?$args;
}

# ── 静态文件优先 → 回退到后端 ──
location / {
    root /var/www/static;
    try_files $uri @backend;
}
location @backend {
    proxy_pass http://app_server;
}

13.4 条件重定向

# ── 移动设备重定向 ──
if ($http_user_agent ~* "(Android|iPhone|iPad)") {
    return 302 https://m.example.com$request_uri;
}

# ── 基于特定查询参数 ──
if ($arg_redirect) {
    return 302 $arg_redirect;
}

# ── 用 map 管理复杂的重定向 ──
map $request_uri $redirect_uri {
    /old-blog/post-1    /blog/new-post-1;
    /old-blog/post-2    /blog/new-post-2;
    /products/legacy    /shop/all;
    default             "";
}

server {
    if ($redirect_uri) {
        return 301 $redirect_uri;
    }
}

14. 静态文件服务优化

14.1 核心文件传输指令

http {
    # ── sendfile ──
    # 使用内核的 sendfile() 系统调用把文件直接发送到 socket
    # 消除用户态内存拷贝,降低 CPU 使用率和上下文切换
    sendfile on;

    # ── tcp_nopush ──
    # 与 sendfile 协同工作: 把 HTTP 头和文件开头放进同一个数据包发送
    # 减少网络包数量,提升带宽效率
    tcp_nopush on;

    # ── tcp_nodelay ──
    # 停用 Nagle 算法: 小数据包立即发送
    # 降低 keepalive 连接上的延迟 (可与 tcp_nopush 同时使用)
    tcp_nodelay on;
}

传输方式对比:

sendfile off (默认):
磁盘 → 内核缓冲区 → 用户态内存 (nginx) → 内核 socket 缓冲区 → 网络
       [读取]       [拷贝]                [写入]

sendfile on:
磁盘 → 内核缓冲区 ─────────────────────────→ 内核 socket 缓冲区 → 网络
       [读取]     [零拷贝: 无需 CPU 介入]     [发送]

14.2 Open File Cache

缓存被反复请求的文件的描述符、大小和修改时间,把文件系统查询降到最低。

http {
    # 最多缓存 10,000 个文件信息, 30 秒未使用则移除
    open_file_cache max=10000 inactive=30s;

    # 每 60 秒重新校验一次已缓存的信息
    open_file_cache_valid 60s;

    # 只缓存被请求 2 次以上的文件 (过滤一次性请求)
    open_file_cache_min_uses 2;

    # 文件不存在(ENOENT)的错误也缓存 (避免反复查询不存在的文件)
    open_file_cache_errors on;
}

14.3 静态文件服务综合配置

server {
    listen 443 ssl http2;
    server_name static.example.com;

    root /var/www/static;

    # ── 图片 ──
    location ~* \.(jpg|jpeg|png|gif|ico|webp|avif|svg)$ {
        expires 30d;
        add_header Cache-Control "public, immutable";
        add_header Vary "Accept-Encoding";
        access_log off;
        log_not_found off;                 # 404 日志也关闭

        # 图片专用限制
        limit_rate 2m;                     # 每个连接 2MB/s
    }

    # ── CSS/JS (配合缓存失效策略) ──
    location ~* \.(css|js)$ {
        expires 365d;                      # 文件名带哈希时可长期缓存
        add_header Cache-Control "public, immutable";
        gzip_static on;
        brotli_static on;
    }

    # ── 字体 ──
    location ~* \.(woff|woff2|ttf|eot|otf)$ {
        expires 365d;
        add_header Cache-Control "public, immutable";
        add_header Access-Control-Allow-Origin "*";
    }

    # ── 媒体文件 ──
    location ~* \.(mp4|webm|ogg|mp3|wav)$ {
        expires 30d;
        add_header Cache-Control "public";

        # 支持 Range 请求 (视频拖动)
        add_header Accept-Ranges bytes;
    }

    # ── 禁止目录列表 ──
    location / {
        autoindex off;
    }

    # ── 阻止访问隐藏文件 ──
    location ~ /\. {
        deny all;
        access_log off;
        log_not_found off;
    }
}

14.4 HTTP/2 Server Push(可选)

# 在客户端请求之前预先推送关键资源
location = /index.html {
    http2_push /css/main.css;
    http2_push /js/app.js;
    http2_push /images/logo.webp;
}

参考:HTTP/2 Server Push 已在部分浏览器中停止支持,103 Early Hints 正成为其替代方案。


15. 健康检查与监控

15.1 Stub Status(基础监控)

用 nginx OSS 自带的 stub_status 模块可以查看实时连接状态。

server {
    listen 8080;                          # 用独立端口隔离
    server_name localhost;

    # 只允许内网访问
    allow 10.0.0.0/8;
    allow 127.0.0.1;
    deny all;

    location /nginx_status {
        stub_status;
    }

    location /health {
        access_log off;
        return 200 "OK\n";
        add_header Content-Type text/plain;
    }
}

stub_status 输出示例:

Active connections: 291
server accepts handled requests
 16630948 16630948 31070465
Reading: 6 Writing: 179 Waiting: 106
项目含义
Active connections当前活跃连接数 (Reading + Writing + Waiting)
accepts累计接受的连接数
handled累计处理的连接数 (等于 accepts 即为正常)
requests累计处理的请求数
Reading正在读取客户端请求头的连接数
Writing正在向客户端发送响应的连接数
Waitingkeepalive 空闲连接数

15.2 被动健康检查(Upstream 监控)

nginx OSS 只支持基于真实流量的被动健康检查。

upstream backend {
    server 10.0.0.1:8080 max_fails=3 fail_timeout=30s;
    server 10.0.0.2:8080 max_fails=3 fail_timeout=30s;
    server 10.0.0.3:8080 max_fails=3 fail_timeout=30s backup;

    # max_fails=3: 30 秒内连续失败 3 次则判定该服务器异常
    # fail_timeout=30s: 判定异常后 30 秒内不再向该服务器发送请求
    #                   30 秒后重新发送请求确认是否恢复
}

server {
    location / {
        proxy_pass http://backend;

        # 设置哪些响应被判定为"失败"
        proxy_next_upstream error timeout http_500 http_502 http_503 http_504;
        proxy_next_upstream_timeout 10s;   # 尝试下一台服务器的最长时间
        proxy_next_upstream_tries 3;       # 最大重试次数
    }
}

15.3 主动健康检查(NGINX Plus 或外部方案)

# ── NGINX Plus 中的主动健康检查 ──
upstream backend {
    zone backend_zone 64k;           # 必须有共享内存区

    server 10.0.0.1:8080;
    server 10.0.0.2:8080;
}

server {
    location / {
        proxy_pass http://backend;
        health_check interval=5s      # 每 5 秒检查一次
                     fails=3           # 失败 3 次判定异常
                     passes=2          # 成功 2 次判定恢复
                     uri=/health;      # 健康检查端点
    }
}

15.4 Prometheus 集成 (nginx-prometheus-exporter)

# docker-compose.yml
services:
  nginx-exporter:
    image: nginx/nginx-prometheus-exporter:1.3
    command:
      - --nginx.scrape-uri=http://nginx:8080/nginx_status
    ports:
      - '9113:9113'
    depends_on:
      - nginx
# prometheus.yml
scrape_configs:
  - job_name: 'nginx'
    static_configs:
      - targets: ['nginx-exporter:9113']

15.5 自定义健康检查端点

# ── Liveness Probe (确认 nginx 自身在运行) ──
location = /healthz {
    access_log off;
    return 200 "alive\n";
    add_header Content-Type text/plain;
}

# ── Readiness Probe (含后端连接的确认) ──
location = /readyz {
    access_log off;
    proxy_pass http://backend/health;
    proxy_connect_timeout 2s;
    proxy_read_timeout 2s;

    # 后端响应失败时返回 503
    error_page 502 503 504 = @not_ready;
}

location @not_ready {
    return 503 "not ready\n";
    add_header Content-Type text/plain;
}
# 在 Kubernetes 中的用法
apiVersion: v1
kind: Pod
spec:
  containers:
    - name: nginx
      livenessProbe:
        httpGet:
          path: /healthz
          port: 8080
        initialDelaySeconds: 5
        periodSeconds: 10
      readinessProbe:
        httpGet:
          path: /readyz
          port: 8080
        initialDelaySeconds: 5
        periodSeconds: 5

生产环境配置综合检查清单

这里整理了把 nginx 部署到生产环境时需要确认的核心项目。

分类项目状态
架构设置 worker_processes auto[ ]
架构设置合适的 worker_connections 值[ ]
SSL/TLS仅启用 TLSv1.2 + TLSv1.3[ ]
SSL/TLS配置强 Cipher Suites[ ]
SSL/TLS启用 OCSP Stapling[ ]
SSL/TLS配置 HSTS 响应头[ ]
SSL/TLSHTTP → HTTPS 重定向[ ]
安全server_tokens off[ ]
安全配置安全响应头 (CSP、X-Frame-Options 等)[ ]
安全配置 Rate Limiting[ ]
安全管理页面的访问控制[ ]
性能启用 sendfile、tcp_nopush、tcp_nodelay[ ]
性能配置 Gzip/Brotli 压缩[ ]
性能配置 open_file_cache[ ]
性能配置静态文件的浏览器缓存[ ]
性能配置 Upstream keepalive[ ]
缓存配置 proxy_cache 或 fastcgi_cache[ ]
缓存配置 proxy_cache_use_stale[ ]
监控启用 stub_status (仅限内部)[ ]
监控搭建健康检查端点[ ]
日志配置 JSON 日志格式[ ]
日志配置日志轮转[ ]
日志排除健康检查/静态文件日志[ ]
代理配置必需的代理请求头[ ]
代理配置 WebSocket 代理 (按需)[ ]
负载均衡选择合适的算法[ ]
负载均衡配置备份服务器[ ]

参考资料