Nginx 配置详解教程

本文聚焦 Nginx 各类配置场景,从基础语法到生产实战,覆盖静态服务、反向代理、负载均衡、HTTPS、缓存、限流、日志等核心主题。


目录

  1. 配置文件结构

  2. 全局配置(main 块)

  3. events 块

  4. http 块核心参数

  5. server 块与虚拟主机

  6. location 匹配规则

  7. 静态文件服务

  8. 反向代理配置

  9. 负载均衡配置

  10. HTTPS / SSL 配置

  11. HTTP/2 与 HTTP/3

  12. Gzip 压缩

  13. 缓存配置

  14. 限流与限速

  15. 访问控制

  16. 日志配置

  17. 安全加固配置

  18. WebSocket 代理

  19. upstream 高级配置

  20. 重写与重定向

  21. 变量与 map 模块

  22. 超时参数全解

  23. 常用完整配置示例

  24. 调试与排障技巧


1. 配置文件结构

Nginx 配置文件采用层级块结构,核心层次如下:

main(全局)
├── events { }
└── http { }
    ├── upstream { }
    ├── server { }
    │   ├── location { }
    │   └── location { }
    └── server { }

文件路径(Ubuntu/Debian):

/etc/nginx/
├── nginx.conf              # 主配置文件,通常只保留全局和 http 骨架
├── conf.d/                 # 推荐在此放自定义配置(*.conf 自动加载)
├── sites-available/        # 虚拟主机配置文件存放
├── sites-enabled/          # 软链接到 sites-available 中已启用的站点
├── snippets/               # 可复用的配置片段
├── mime.types              # MIME 类型映射表
└── fastcgi_params          # FastCGI 默认参数

主配置文件骨架:

# nginx.conf
user  www-data;
worker_processes  auto;
error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    # 引入 conf.d 下所有配置
    include /etc/nginx/conf.d/*.conf;
    # 引入已启用的虚拟主机
    include /etc/nginx/sites-enabled/*;
}

2. 全局配置(main 块)

全局块位于配置文件最外层,影响 Nginx 整体行为。

# 运行 Nginx 的系统用户(需与静态文件权限匹配)
user  www-data;

# 工作进程数,auto = CPU 核心数
# 可手动指定,如 worker_processes 4;
worker_processes  auto;

# 工作进程绑定 CPU(多核优化,减少上下文切换)
# worker_cpu_affinity auto;
# worker_cpu_affinity 0001 0010 0100 1000;  # 手动绑定 4 核

# 工作进程优先级,-20(最高)到 19(最低),默认 0
worker_priority  0;

# 工作进程可打开的最大文件描述符数(需与系统 ulimit 配合)
worker_rlimit_nofile  65535;

# 错误日志路径及级别(debug | info | notice | warn | error | crit | alert | emerg)
error_log  /var/log/nginx/error.log  warn;

# PID 文件路径
pid  /var/run/nginx.pid;

# 是否以守护进程方式运行(生产环境 on,Docker 中通常 off)
# daemon  on;

# 加载动态模块
# load_module modules/ngx_http_image_filter_module.so;

3. events 块

events 块控制 Nginx 处理网络事件的方式。

events {
    # 每个工作进程的最大并发连接数
    # 总并发 = worker_processes × worker_connections
    worker_connections  1024;

    # 事件驱动模型(Linux 推荐 epoll,macOS 用 kqueue,默认自动选择)
    use  epoll;

    # 是否允许一个工作进程同时接受多个新连接(on = 高并发场景更高效)
    multi_accept  on;

    # accept 互斥锁,防止惊群问题(Nginx 1.11.3+ 默认关闭,性能更好)
    # accept_mutex  on;
    # accept_mutex_delay  500ms;
}

4. http 块核心参数

http {
    # MIME 类型
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;  # 未知类型默认值

    # 字符集
    charset  utf-8;

    # 高效文件传输(直接从内核发送文件,绕过用户空间,显著提升静态文件性能)
    sendfile        on;

    # 与 sendfile 配合,等待数据积累到一定量再发送(减少网络包数量)
    tcp_nopush      on;

    # 禁用 Nagle 算法,减少延迟(适合实时交互场景)
    tcp_nodelay     on;

    # 关闭 server_tokens,不在响应头暴露 Nginx 版本号
    server_tokens   off;

    # 长连接保持时间(单位秒),第二个参数是发送给客户端的 Keep-Alive 头值
    keepalive_timeout  65 60;

    # 单个长连接最多处理的请求数
    keepalive_requests  1000;

    # 客户端请求体最大值(文件上传限制)
    client_max_body_size  20m;

    # 请求体缓冲区大小(超过则写入临时文件)
    client_body_buffer_size  128k;

    # 请求头缓冲区(超大 Header 时调大)
    client_header_buffer_size  4k;
    large_client_header_buffers  4 16k;

    # 哈希表大小(服务器名称哈希,域名多时需调大)
    server_names_hash_bucket_size  128;
    server_names_hash_max_size     1024;

    # 开启变量哈希(map 等模块需要)
    variables_hash_max_size   1024;
    variables_hash_bucket_size  64;
}

5. server 块与虚拟主机

一个 http 块中可以有多个 server,Nginx 根据 listen 端口和 server_name 来匹配请求。

5.1 基本虚拟主机

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

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

    access_log  /var/log/nginx/example.access.log  main;
    error_log   /var/log/nginx/example.error.log   warn;
}

5.2 默认服务器

当请求的 Host 不匹配任何 server_name 时,使用 default_server

server {
    listen  80 default_server;
    server_name  _;       # _ 匹配所有未命中的域名

    return 444;           # 直接断开连接,不返回任何内容
}

5.3 server_name 匹配方式

# 精确匹配(优先级最高)
server_name  example.com;

# 通配符前缀
server_name  *.example.com;

# 通配符后缀
server_name  example.*;

# 正则匹配(以 ~ 开头,优先级低于精确和通配符)
server_name  ~^(www\.)?example\.com$;

# 正则捕获组(可在配置中引用 $1)
server_name  ~^(api|admin)\.example\.com$;
# 此时 $1 = api 或 admin,可用于 root 路径等
root  /var/www/$1;

匹配优先级: 精确名称 > 通配符前缀(*.example.com)> 通配符后缀(example.*)> 正则

5.4 多端口监听

server {
    listen  80;
    listen  8080;
    listen  [::]:80;         # IPv6
    server_name  example.com;
    # ...
}

6. location 匹配规则

location 是 Nginx 最灵活的配置单元,决定如何处理特定路径的请求。

6.1 匹配语法

语法

类型

说明

location /path

前缀匹配

URI 以 /path 开头即匹配

location = /path

精确匹配

URI 完全等于 /path

location ^~ /path

优先前缀

匹配后不再检查正则

location ~ regex

正则(区分大小写)

正则匹配

location ~* regex

正则(忽略大小写)

正则匹配,不区分大小写

location @name

命名 location

用于内部跳转

6.2 匹配优先级(从高到低)

1. = 精确匹配
2. ^~ 优先前缀匹配
3. ~ 或 ~* 正则匹配(按配置文件顺序,先匹配先生效)
4. 普通前缀匹配(取最长前缀)

6.3 示例

server {
    # 精确匹配首页,直接返回
    location = / {
        return 200 "exact root";
    }

    # 优先前缀匹配 /static,不走正则
    location ^~ /static/ {
        root /var/www;
    }

    # 正则:匹配图片文件(不区分大小写)
    location ~* \.(jpg|jpeg|png|gif|ico|svg|webp)$ {
        expires 30d;
        add_header Cache-Control "public";
    }

    # 正则:PHP 处理
    location ~ \.php$ {
        fastcgi_pass  unix:/run/php/php8.1-fpm.sock;
        include       fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }

    # 普通前缀匹配(兜底)
    location / {
        try_files $uri $uri/ /index.html;
    }
}

6.4 try_files

try_files 按顺序检查文件是否存在,找不到则执行最后一个参数:

location / {
    # 依次尝试:文件 → 目录 → 重写到 index.php
    try_files  $uri  $uri/  /index.php?$query_string;
}

location / {
    # 依次尝试:文件 → 返回 404
    try_files  $uri  $uri/  =404;
}

# 命名 location 跳转
location / {
    try_files  $uri  @backend;
}
location @backend {
    proxy_pass  http://127.0.0.1:3000;
}

7. 静态文件服务

7.1 基本配置

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

    # 文档根目录
    root   /var/www/static;
    index  index.html;

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

    # 禁止访问隐藏文件(.htaccess, .git 等)
    location ~ /\. {
        deny all;
    }
}

7.2 静态文件缓存头

# 图片、字体、媒体文件 - 长期缓存
location ~* \.(jpg|jpeg|png|gif|ico|svg|webp|woff|woff2|ttf|eot)$ {
    expires      90d;
    add_header   Cache-Control "public, immutable";
    access_log   off;   # 关闭静态资源日志,减少 IO
}

# CSS、JS - 中期缓存(配合 Hash 文件名使用 immutable)
location ~* \.(css|js)$ {
    expires      30d;
    add_header   Cache-Control "public";
}

# HTML - 不缓存,确保用户拿到最新页面
location ~* \.html$ {
    expires      -1;
    add_header   Cache-Control "no-cache, no-store, must-revalidate";
}

7.3 目录列表(文件服务器)

location /files/ {
    root   /data;
    autoindex  on;              # 开启目录列表
    autoindex_exact_size  off;  # 文件大小用 KB/MB 显示,不显示精确字节数
    autoindex_localtime  on;    # 使用本地时间
}

7.4 alias 与 root 的区别

# root:将 location 前缀追加到 root 后
# 请求 /static/a.js → 实际路径 /var/www/static/a.js
location /static/ {
    root  /var/www;
}

# alias:用 alias 路径替换 location 前缀
# 请求 /static/a.js → 实际路径 /data/assets/a.js
location /static/ {
    alias  /data/assets/;   # 注意:alias 末尾必须有斜杠
}

8. 反向代理配置

8.1 基本反向代理

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

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

        # 传递真实 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;
        proxy_set_header  X-Forwarded-Proto $scheme;

        # HTTP/1.1 支持 WebSocket 和长连接
        proxy_http_version  1.1;
        proxy_set_header    Upgrade    $http_upgrade;
        proxy_set_header    Connection "upgrade";
    }
}

8.2 proxy_pass 的 URL 末尾斜杠问题

# 不带斜杠:将完整 URI 传给后端
# 请求 /api/users → 后端收到 /api/users
location /api {
    proxy_pass  http://backend;
}

# 带斜杠:去掉 location 前缀后转发
# 请求 /api/users → 后端收到 /users
location /api/ {
    proxy_pass  http://backend/;
}

# 带路径的 proxy_pass:同样去掉前缀再拼接
# 请求 /api/users → 后端收到 /v1/users
location /api/ {
    proxy_pass  http://backend/v1/;
}

8.3 代理缓冲配置

location / {
    proxy_pass  http://backend;

    # 开启代理缓冲(默认开启)
    proxy_buffering  on;

    # 响应头缓冲区
    proxy_buffer_size          4k;

    # 响应体缓冲区(个数 × 大小)
    proxy_buffers              8 16k;

    # 忙碌缓冲区上限(正在写入客户端的最大缓冲量)
    proxy_busy_buffers_size    32k;

    # 临时文件大小上限(缓冲区满后溢出到磁盘)
    proxy_temp_file_write_size  64k;
    proxy_max_temp_file_size    1024m;

    # 对大文件/流媒体关闭缓冲,直接透传
    # proxy_buffering  off;
}

8.4 代理错误处理

location / {
    proxy_pass  http://backend;

    # 后端返回这些状态码时,转给 error_page 处理
    proxy_intercept_errors  on;

    # 自定义错误页
    error_page  502 503 504  /50x.html;
    location = /50x.html {
        root  /var/www/errors;
    }

    # 后端不可用时尝试备用(需配合 upstream)
    proxy_next_upstream  error timeout http_502 http_503;
    proxy_next_upstream_tries  3;
    proxy_next_upstream_timeout  10s;
}

9. 负载均衡配置

9.1 upstream 基本配置

upstream backend {
    # 默认:轮询(Round Robin)
    server  192.168.1.10:3000;
    server  192.168.1.11:3000;
    server  192.168.1.12:3000;
}

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

9.2 server 参数详解

upstream backend {
    server  192.168.1.10:3000  weight=5;      # 权重,默认 1,越大越多请求
    server  192.168.1.11:3000  weight=3;
    server  192.168.1.12:3000  weight=1
                               max_fails=3     # 失败多少次标记为不可用
                               fail_timeout=30s;  # 标记不可用后多久重新尝试

    server  192.168.1.13:3000  backup;         # 备用服务器,主服务器全挂才启用
    server  192.168.1.14:3000  down;           # 标记为永久下线(维护用)

    # 每个 worker 进程保持的长连接数
    keepalive  32;
}

9.3 负载均衡策略

# 最少连接(适合请求处理时间差异大的场景)
upstream backend {
    least_conn;
    server  192.168.1.10:3000;
    server  192.168.1.11:3000;
}

# IP Hash(同一客户端 IP 固定到同一服务器,适合有 Session 的应用)
upstream backend {
    ip_hash;
    server  192.168.1.10:3000;
    server  192.168.1.11:3000;
}

# 哈希(按任意 key 分配,如 URL,适合缓存服务器)
upstream backend {
    hash  $request_uri  consistent;   # consistent = 一致性哈希,节点变动影响最小
    server  192.168.1.10:3000;
    server  192.168.1.11:3000;
}

# 随机(Nginx 1.15.1+)
upstream backend {
    random  two  least_conn;   # 随机选 2 个,再选连接数少的
    server  192.168.1.10:3000;
    server  192.168.1.11:3000;
}

9.4 健康检查(商业版 / OpenResty)

开源 Nginx 通过 max_fails + fail_timeout 做被动检查。如需主动健康检查,使用 nginx_upstream_check_module

upstream backend {
    server  192.168.1.10:3000;
    server  192.168.1.11:3000;

    # 需要 nginx_upstream_check_module
    check  interval=3000  rise=2  fall=3  timeout=1000  type=http;
    check_http_send  "HEAD / HTTP/1.0\r\n\r\n";
    check_http_expect_alive  http_2xx  http_3xx;
}

10. HTTPS / SSL 配置

10.1 Let’s Encrypt 自动证书

# 安装 certbot
sudo apt install certbot python3-certbot-nginx

# 自动申请并配置证书
sudo certbot --nginx -d example.com -d www.example.com

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

# 查看证书到期时间
sudo certbot certificates

10.2 完整 SSL 配置

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;

    # TLS 版本(只允许 1.2 和 1.3,禁用旧版本)
    ssl_protocols  TLSv1.2 TLSv1.3;

    # 加密套件(Mozilla 现代推荐配置)
    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 客户端选择更合理

    # Session 缓存(减少握手开销)
    ssl_session_cache    shared:SSL:10m;  # 10MB,约可缓存 40000 个 Session
    ssl_session_timeout  1d;
    ssl_session_tickets  off;  # 禁用 Tickets(提升前向保密性)

    # OCSP Stapling(加速证书验证,让 Nginx 代替浏览器查询证书状态)
    ssl_stapling         on;
    ssl_stapling_verify  on;
    ssl_trusted_certificate  /etc/letsencrypt/live/example.com/chain.pem;
    resolver  8.8.8.8 1.1.1.1  valid=300s;
    resolver_timeout  5s;

    # HSTS(强制 HTTPS,max-age 单位秒,2年 = 63072000)
    add_header  Strict-Transport-Security  "max-age=63072000; includeSubDomains; preload"  always;

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

# HTTP 强制跳转 HTTPS
server {
    listen  80;
    listen  [::]:80;
    server_name  example.com www.example.com;

    # Let's Encrypt 验证路径不跳转
    location /.well-known/acme-challenge/ {
        root  /var/www/html;
    }

    location / {
        return  301  https://$host$request_uri;
    }
}

10.3 DH 参数(增强密钥交换安全性)

# 生成 DH 参数(2048 位,耗时较长)
openssl dhparam -out /etc/nginx/dhparam.pem 2048
ssl_dhparam  /etc/nginx/dhparam.pem;

10.4 双证书配置(ECC + RSA)

# ECC 证书(速度快,优先使用)
ssl_certificate      /path/to/ecc/fullchain.pem;
ssl_certificate_key  /path/to/ecc/privkey.pem;

# RSA 证书(兼容旧客户端)
ssl_certificate      /path/to/rsa/fullchain.pem;
ssl_certificate_key  /path/to/rsa/privkey.pem;

11. HTTP/2 与 HTTP/3

11.1 HTTP/2

server {
    # 在 listen 后加 http2 即开启(需要 SSL)
    listen  443 ssl http2;

    # HTTP/2 服务器推送(提前推送关键资源)
    location = /index.html {
        http2_push  /style.css;
        http2_push  /app.js;
    }

    # 推送预加载 Link 头中声明的资源
    http2_push_preload  on;
}

11.2 HTTP/3(QUIC,Nginx 1.25+)

server {
    listen  443 ssl http2;
    listen  443 quic reuseport;   # HTTP/3 over QUIC(UDP)
    server_name  example.com;

    ssl_certificate     /path/to/fullchain.pem;
    ssl_certificate_key /path/to/privkey.pem;

    # 通知客户端支持 HTTP/3
    add_header  Alt-Svc  'h3=":443"; ma=86400';
}

12. Gzip 压缩

http {
    # 开启 Gzip
    gzip  on;

    # 压缩级别(1-9,越高压缩率越好但越耗 CPU,推荐 4-6)
    gzip_comp_level  5;

    # 最小压缩大小(小于此值不压缩,单位字节)
    gzip_min_length  1024;

    # 压缩缓冲区
    gzip_buffers  16 8k;

    # 兼容 HTTP/1.0 代理
    gzip_http_version  1.1;

    # 需要压缩的 MIME 类型(text/html 默认压缩,无需指定)
    gzip_types
        text/plain
        text/css
        text/javascript
        text/xml
        application/json
        application/javascript
        application/x-javascript
        application/xml
        application/xml+rss
        application/atom+xml
        image/svg+xml
        font/ttf
        font/otf;

    # 在响应头加 Vary: Accept-Encoding(告知代理服务器分别缓存压缩和非压缩版本)
    gzip_vary  on;

    # 对代理请求也压缩(any = 无论 Via 头如何)
    gzip_proxied  any;

    # 关闭对 IE6 的 Gzip(IE6 有 Gzip bug)
    gzip_disable  "msie6";
}

预压缩静态文件(减少实时压缩 CPU 开销):

location ~* \.(css|js|json|svg)$ {
    # 优先使用预压缩的 .gz 文件
    gzip_static  on;
}
# 提前生成 .gz 文件
gzip -k -9 /var/www/static/app.js
# 生成 app.js.gz,Nginx 会自动选用

13. 缓存配置

13.1 代理缓存(proxy_cache)

http {
    # 定义缓存区(名称 cache_zone,路径,层级,共享内存大小,最大缓存大小,非活跃时间)
    proxy_cache_path  /var/cache/nginx
        levels=1:2                      # 目录层级(避免单目录文件过多)
        keys_zone=cache_zone:10m        # 共享内存 key 存储区,10MB 约存 80000 个 key
        max_size=2g                     # 磁盘最大使用量
        inactive=60m                    # 60 分钟内未被访问则删除
        use_temp_path=off;              # 直接写入缓存目录(减少一次文件复制)

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

            # 缓存 key(默认按完整 URL 缓存)
            proxy_cache_key  "$scheme$proxy_host$request_uri";

            # 对不同状态码设置不同缓存时间
            proxy_cache_valid  200 302  10m;
            proxy_cache_valid  301      1h;
            proxy_cache_valid  any      1m;   # 其他状态码缓存 1 分钟

            # 当后端请求失败时,继续使用过期缓存(防止缓存穿透)
            proxy_cache_use_stale  error timeout updating http_500 http_502 http_503 http_504;

            # 后台异步更新缓存(不阻塞当前请求)
            proxy_cache_background_update  on;

            # 同一 key 同时只允许一个请求回源,其他请求等待(防止缓存击穿)
            proxy_cache_lock  on;
            proxy_cache_lock_timeout  5s;

            # 在响应头中添加缓存命中状态(HIT/MISS/BYPASS/EXPIRED)
            add_header  X-Cache-Status  $upstream_cache_status;

            # 跳过缓存的条件(POST 请求、带 Cookie 的请求等)
            proxy_cache_bypass  $http_pragma  $http_authorization;
            proxy_no_cache      $http_pragma  $http_authorization;
        }

        # 手动清除缓存(需要 ngx_cache_purge 模块)
        # location ~ /purge(/.*) {
        #     proxy_cache_purge  cache_zone  "$scheme$proxy_host$1";
        # }
    }
}

13.2 FastCGI 缓存(PHP)

http {
    fastcgi_cache_path  /var/cache/nginx/fastcgi
        levels=1:2
        keys_zone=fcgi_cache:10m
        max_size=1g
        inactive=30m;

    server {
        set  $skip_cache  0;

        # POST 请求不缓存
        if ($request_method = POST) { set $skip_cache 1; }
        # 带 query string 的请求不缓存
        if ($query_string != "")    { set $skip_cache 1; }
        # 管理后台不缓存
        if ($request_uri ~* "/admin/") { set $skip_cache 1; }

        location ~ \.php$ {
            fastcgi_pass   unix:/run/php/php8.1-fpm.sock;
            include        fastcgi_params;
            fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;

            fastcgi_cache       fcgi_cache;
            fastcgi_cache_key   "$scheme$request_method$host$request_uri";
            fastcgi_cache_valid  200  60m;
            fastcgi_cache_bypass  $skip_cache;
            fastcgi_no_cache      $skip_cache;
            add_header  X-Cache  $upstream_cache_status;
        }
    }
}

14. 限流与限速

14.1 请求频率限制(limit_req)

防止单个 IP 请求过于频繁,抵御暴力破解和 DDoS。

http {
    # 定义限流区域(按 IP 限制,内存 10MB)
    # 1MB 约存 16000 个 IP
    limit_req_zone  $binary_remote_addr  zone=req_limit:10m  rate=10r/s;

    # 也可以按 URL 限制
    limit_req_zone  $uri  zone=uri_limit:10m  rate=100r/s;

    # 登录接口单独严格限制
    limit_req_zone  $binary_remote_addr  zone=login_limit:10m  rate=1r/s;

    server {
        # 全局应用限流(允许突发 20 个请求,超出排队等待)
        location / {
            limit_req  zone=req_limit  burst=20  nodelay;
            # burst:允许突发请求数(短期超出 rate 的队列容量)
            # nodelay:突发请求直接处理,不延迟(超出 burst 才拒绝)
        }

        # 登录接口严格限流
        location /login {
            limit_req  zone=login_limit  burst=5;
            limit_req_status  429;   # 超限返回 429(默认 503)
        }
    }
}

14.2 并发连接限制(limit_conn)

http {
    # 按 IP 限制并发连接数
    limit_conn_zone  $binary_remote_addr  zone=conn_limit:10m;

    # 按 server_name 限制
    limit_conn_zone  $server_name  zone=server_conn:10m;

    server {
        # 每个 IP 最多 10 个并发连接
        limit_conn  conn_limit  10;

        # 整个 server 最多 1000 个连接
        limit_conn  server_conn  1000;

        # 超限返回状态码(默认 503)
        limit_conn_status  503;

        # 日志级别(默认 error)
        limit_conn_log_level  warn;
    }
}

14.3 带宽限速

location /downloads/ {
    # 下载速度限制为 512KB/s
    limit_rate  512k;

    # 前 10MB 不限速,之后限速(让小文件快速下载,大文件限速)
    limit_rate_after  10m;
    limit_rate        200k;
}

15. 访问控制

15.1 IP 白名单 / 黑名单

location /admin/ {
    # 白名单
    allow  192.168.1.0/24;   # 允许整个网段
    allow  10.0.0.1;          # 允许单个 IP
    allow  127.0.0.1;
    deny   all;               # 拒绝其他所有
}

# 黑名单(先放行,最后允许其他)
location / {
    deny   192.168.1.100;     # 屏蔽指定 IP
    deny   10.0.0.0/8;        # 屏蔽整个网段
    allow  all;
}

15.2 HTTP Basic 认证

# 生成密码文件(需要 apache2-utils)
sudo apt install apache2-utils
sudo htpasswd -c /etc/nginx/.htpasswd admin
# 添加更多用户(不带 -c,否则覆盖)
sudo htpasswd /etc/nginx/.htpasswd user2
location /private/ {
    auth_basic           "Restricted Area";
    auth_basic_user_file /etc/nginx/.htpasswd;
}

15.3 防盗链(referer 检查)

location ~* \.(jpg|png|gif|mp4)$ {
    valid_referers  none  blocked  example.com  *.example.com;
    # none:直接访问(无 Referer 头)
    # blocked:Referer 存在但被防火墙删除了 http:// 前缀

    if ($invalid_referer) {
        return  403;
        # 或者返回替换图片
        # rewrite ^ /images/forbidden.jpg last;
    }
}

15.4 地理位置限制(需要 GeoIP2 模块)

# 加载 GeoIP2 模块
load_module  modules/ngx_http_geoip2_module.so;

http {
    geoip2  /var/lib/GeoIP/GeoLite2-Country.mmdb {
        auto_reload  5m;
        $geoip2_country_code  country iso_code;
    }

    map  $geoip2_country_code  $allowed_country {
        default  1;
        CN       1;
        US       1;
        # 屏蔽特定国家
        KP       0;
        RU       0;
    }

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

16. 日志配置

16.1 日志格式

http {
    # 自定义日志格式(JSON 格式,方便 ELK/Loki 采集)
    log_format  json_log  escape=json
        '{'
            '"time":"$time_iso8601",'
            '"remote_addr":"$remote_addr",'
            '"request":"$request",'
            '"status":$status,'
            '"body_bytes_sent":$body_bytes_sent,'
            '"request_time":$request_time,'
            '"upstream_response_time":"$upstream_response_time",'
            '"upstream_addr":"$upstream_addr",'
            '"http_referer":"$http_referer",'
            '"http_user_agent":"$http_user_agent",'
            '"http_x_forwarded_for":"$http_x_forwarded_for"'
        '}';

    # 传统格式(combined 是默认格式)
    log_format  main
        '$remote_addr - $remote_user [$time_local] "$request" '
        '$status $body_bytes_sent "$http_referer" '
        '"$http_user_agent" "$http_x_forwarded_for"';

    # 带响应时间的扩展格式
    log_format  detailed
        '$remote_addr - [$time_local] "$request" $status $body_bytes_sent '
        'rt=$request_time uct=$upstream_connect_time uht=$upstream_header_time urt=$upstream_response_time';

    # 全局访问日志
    access_log  /var/log/nginx/access.log  main;
}

16.2 server 和 location 级别的日志

server {
    access_log  /var/log/nginx/app.access.log  json_log;
    error_log   /var/log/nginx/app.error.log   warn;

    # 静态资源不记录日志(减少 IO)
    location ~* \.(ico|css|js|gif|jpg|png|woff)$ {
        access_log  off;
    }

    # 健康检查不记录日志
    location = /health {
        access_log  off;
        return  200  "OK";
    }
}

16.3 日志轮转(logrotate)

# /etc/logrotate.d/nginx
/var/log/nginx/*.log {
    daily           # 每天轮转
    missingok       # 文件不存在不报错
    rotate  14      # 保留 14 天
    compress        # 压缩旧日志
    delaycompress   # 上一个周期不压缩(方便排查最近问题)
    notifempty      # 日志为空不轮转
    sharedscripts
    postrotate
        # 向 Nginx 发送 USR1 信号,重新打开日志文件
        nginx -s reopen
    endscript
}

17. 安全加固配置

http {
    # 隐藏版本号
    server_tokens  off;

    # 防止点击劫持(禁止 iframe 嵌入)
    add_header  X-Frame-Options  "SAMEORIGIN"  always;

    # 禁止 MIME 类型嗅探
    add_header  X-Content-Type-Options  "nosniff"  always;

    # XSS 防护(现代浏览器 CSP 更好,但保留兼容旧浏览器)
    add_header  X-XSS-Protection  "1; mode=block"  always;

    # 内容安全策略(CSP)- 根据业务调整
    add_header  Content-Security-Policy
        "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:;"
        always;

    # 控制 Referer 信息泄露
    add_header  Referrer-Policy  "strict-origin-when-cross-origin"  always;

    # 权限策略(限制浏览器功能)
    add_header  Permissions-Policy  "geolocation=(), microphone=(), camera=()"  always;

    # 限制请求方法(只允许 GET/POST/HEAD)
    server {
        if ($request_method !~ ^(GET|HEAD|POST)$) {
            return  405;
        }
    }

    # 防止慢速攻击(Slowloris)
    client_body_timeout    10s;
    client_header_timeout  10s;
    send_timeout           10s;

    # 限制请求体(防止大文件攻击)
    client_max_body_size  10m;
}

18. WebSocket 代理

WebSocket 升级需要特殊处理 UpgradeConnection 头。

http {
    # map 处理 Connection 升级头
    map  $http_upgrade  $connection_upgrade {
        default  upgrade;
        ''       close;
    }

    server {
        location /ws/ {
            proxy_pass  http://websocket_backend;

            # WebSocket 必须是 HTTP/1.1
            proxy_http_version  1.1;

            # 传递升级头
            proxy_set_header  Upgrade    $http_upgrade;
            proxy_set_header  Connection $connection_upgrade;

            # 传递客户端信息
            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 小时
            proxy_send_timeout   86400s;

            # 关闭缓冲(WebSocket 需要实时传输)
            proxy_buffering  off;
        }
    }
}

19. upstream 高级配置

19.1 长连接复用

upstream backend {
    server  127.0.0.1:3000;
    server  127.0.0.1:3001;

    # Nginx 与后端保持的长连接数(HTTP/1.1 开启)
    keepalive  32;

    # 单个长连接最多复用请求数(防止连接过久)
    keepalive_requests  1000;

    # 长连接空闲超时
    keepalive_timeout  60s;
}

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

        # 必须是 HTTP/1.1 才能复用长连接
        proxy_http_version  1.1;
        # 清除 Connection 头,否则默认 close 会关闭长连接
        proxy_set_header  Connection  "";
    }
}

19.2 故障转移配置

upstream backend {
    server  192.168.1.10:3000  max_fails=3  fail_timeout=30s;
    server  192.168.1.11:3000  max_fails=3  fail_timeout=30s;
    server  192.168.1.12:3000  backup;
}

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

        # 在哪些情况下尝试下一台服务器
        proxy_next_upstream  error timeout http_500 http_502 http_503 http_504;

        # 最多重试几次
        proxy_next_upstream_tries  3;

        # 重试总时间限制
        proxy_next_upstream_timeout  10s;
    }
}

20. 重写与重定向

20.1 return 指令(推荐,性能好)

# 301 永久重定向(浏览器会缓存)
return  301  https://$host$request_uri;

# 302 临时重定向
return  302  https://new.example.com$request_uri;

# 直接返回响应(不经过 proxy)
return  200  "OK";
return  404;
return  429  "Too Many Requests";

20.2 rewrite 指令

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

# 去掉 index.php(SEO 优化)
rewrite  ^/index\.php(/?.*)$  $1  last;

# 旧路径重写到新路径
rewrite  ^/old/(.*)$  /new/$1  permanent;

# 添加 www
if ($host = 'example.com') {
    rewrite  ^(.*)$  https://www.example.com$1  permanent;
}

# 目录结构重写(WordPress / Laravel 等)
location / {
    try_files  $uri  $uri/  /index.php?$args;
}

20.3 rewrite flag 说明

flag

说明

last

终止当前 rewrite,用新 URI 重新匹配 location

break

终止 rewrite,在当前 location 继续处理

redirect

返回 302 临时重定向

permanent

返回 301 永久重定向

20.4 常用重写场景

# www 和非 www 统一(非 www 跳 www)
server {
    listen  80;
    server_name  example.com;
    return  301  https://www.example.com$request_uri;
}

# 强制末尾斜杠
rewrite  ^([^.]*[^/])$  $1/  permanent;

# 移除末尾斜杠
rewrite  ^/(.*)/+$  /$1  permanent;

# URL 大小写统一(转小写,需要 Lua 或 map)
map  $uri  $uri_lowercase {
    ~^(.+)$  $1;
}

21. 变量与 map 模块

21.1 常用内置变量

变量

说明

$host

请求的 Host(优先:请求行 > 请求头 > server_name)

$server_name

匹配的 server_name

$request_uri

完整 URI(含查询字符串)

$uri

当前 URI(不含查询字符串,rewrite 后会变化)

$args

查询字符串

$remote_addr

客户端 IP

$binary_remote_addr

二进制格式 IP(limit_req 用,节省内存)

$scheme

http 或 https

$request_method

GET / POST / PUT 等

$status

响应状态码

$upstream_addr

后端服务器地址

$upstream_response_time

后端响应时间

$request_time

总请求时间(含客户端传输)

$http_user_agent

客户端 UA

$http_referer

来源页

$http_x_forwarded_for

经过代理的原始 IP 链

21.2 map 模块

map 根据一个变量的值映射出另一个变量,性能优于 if。

http {
    # 移动端检测
    map  $http_user_agent  $is_mobile {
        default          0;
        ~*android        1;
        ~*iphone         1;
        ~*ipod           1;
        ~*mobile         1;
    }

    # 根据来源国家设置语言
    map  $geoip2_country_code  $lang {
        default  en;
        CN       zh;
        TW       zh;
        JP       ja;
        KR       ko;
    }

    # 根据设备跳转不同站点
    server {
        if ($is_mobile) {
            return  302  https://m.example.com$request_uri;
        }
    }
}

21.3 geo 模块(按 IP 段设置变量)

geo  $remote_addr  $is_internal {
    default        0;
    10.0.0.0/8     1;
    172.16.0.0/12  1;
    192.168.0.0/16 1;
    127.0.0.1/32   1;
}

22. 超时参数全解

http {
    # ===== 客户端相关超时 =====

    # 读取请求头的超时(从连接建立到读完请求头)
    client_header_timeout  10s;

    # 读取请求体的超时(两次读操作之间的最大间隔,不是总时间)
    client_body_timeout  30s;

    # 向客户端发送响应的超时(两次写操作之间的间隔)
    send_timeout  30s;

    # 长连接超时
    keepalive_timeout  65s;

    # ===== 代理相关超时 =====

    # 与后端建立连接的超时(通常 60s 内连不上就是后端挂了)
    proxy_connect_timeout  60s;

    # 读取后端响应的超时(两次读操作之间,不是整个响应时间)
    proxy_read_timeout  60s;

    # 向后端发送请求的超时
    proxy_send_timeout  60s;

    # ===== FastCGI 相关超时 =====
    fastcgi_connect_timeout  60s;
    fastcgi_read_timeout     300s;   # PHP 脚本执行可能较慢
    fastcgi_send_timeout     60s;

    # ===== 解析器超时 =====
    resolver_timeout  10s;
}

23. 常用完整配置示例

23.1 前端单页应用(SPA)

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

    ssl_certificate      /etc/letsencrypt/live/app.example.com/fullchain.pem;
    ssl_certificate_key  /etc/letsencrypt/live/app.example.com/privkey.pem;
    ssl_protocols        TLSv1.2 TLSv1.3;

    root   /var/www/app/dist;
    index  index.html;

    # HTML 不缓存,保证更新即时生效
    location = /index.html {
        add_header  Cache-Control  "no-cache, no-store, must-revalidate";
    }

    # 静态资源长期缓存(文件名含 Hash)
    location ~* \.(js|css|png|jpg|svg|woff2?)$ {
        expires  1y;
        add_header  Cache-Control  "public, immutable";
        access_log  off;
    }

    # SPA 路由:所有未匹配路径返回 index.html
    location / {
        try_files  $uri  $uri/  /index.html;
    }

    # API 反向代理
    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;
    }
}

server {
    listen  80;
    server_name  app.example.com;
    return  301  https://$host$request_uri;
}

23.2 Node.js / Python 应用反向代理

upstream node_app {
    server  127.0.0.1:3000;
    keepalive  16;
}

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

    ssl_certificate      /etc/ssl/certs/example.crt;
    ssl_certificate_key  /etc/ssl/private/example.key;
    ssl_protocols        TLSv1.2 TLSv1.3;

    # 请求日志
    access_log  /var/log/nginx/api.access.log  detailed;

    # 全局限流
    limit_req_zone  $binary_remote_addr  zone=api_limit:10m  rate=30r/s;

    location / {
        limit_req  zone=api_limit  burst=50  nodelay;

        proxy_pass         http://node_app;
        proxy_http_version  1.1;
        proxy_set_header   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;
        proxy_set_header   X-Forwarded-Proto $scheme;

        proxy_connect_timeout  5s;
        proxy_read_timeout     30s;

        # 后端出错时的降级
        proxy_next_upstream  error timeout http_502 http_503;
        proxy_next_upstream_tries  2;
    }

    location = /health {
        access_log  off;
        proxy_pass  http://node_app;
    }
}

23.3 WordPress / PHP 配置

server {
    listen  80;
    server_name  blog.example.com;
    root   /var/www/wordpress;

    index  index.php;

    # 阻止 WordPress 系统文件直接访问
    location = /wp-config.php     { deny all; }
    location ~ /\.                { deny all; }
    location ~ /wp-admin/includes { deny all; }
    location ~ /readme\.(html|txt) { deny all; }

    # 静态资源缓存
    location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff2?)$ {
        expires  30d;
        log_not_found  off;
        access_log  off;
    }

    # WordPress 固定链接支持
    location / {
        try_files  $uri  $uri/  /index.php?$args;
    }

    # PHP 处理
    location ~ \.php$ {
        try_files  $uri  =404;
        fastcgi_split_path_info  ^(.+\.php)(/.+)$;
        fastcgi_pass   unix:/run/php/php8.1-fpm.sock;
        fastcgi_index  index.php;
        include        fastcgi_params;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        fastcgi_param  PATH_INFO        $fastcgi_path_info;
    }
}

23.4 微服务网关配置

http {
    # 各微服务 upstream
    upstream user_service    { server 127.0.0.1:8001; keepalive 8; }
    upstream order_service   { server 127.0.0.1:8002; keepalive 8; }
    upstream product_service { server 127.0.0.1:8003; keepalive 8; }

    limit_req_zone  $binary_remote_addr  zone=global:20m  rate=100r/s;

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

        # 全局限流
        limit_req  zone=global  burst=200  nodelay;

        # 用户服务
        location /api/users/ {
            proxy_pass  http://user_service/;
            include     snippets/proxy_params.conf;
        }

        # 订单服务
        location /api/orders/ {
            proxy_pass  http://order_service/;
            include     snippets/proxy_params.conf;
        }

        # 商品服务
        location /api/products/ {
            proxy_pass  http://product_service/;
            include     snippets/proxy_params.conf;
        }
    }
}
# /etc/nginx/snippets/proxy_params.conf
proxy_http_version  1.1;
proxy_set_header    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;
proxy_set_header    X-Forwarded-Proto $scheme;
proxy_connect_timeout  5s;
proxy_read_timeout     30s;
proxy_next_upstream    error timeout http_502 http_503;

24. 调试与排障技巧

24.1 配置验证

# 测试配置语法(不重启)
sudo nginx -t

# 显示完整编译参数(查看已编译的模块)
nginx -V

# 显示配置文件路径
nginx -T  # 输出所有配置(含 include 的文件,适合排查 include 问题)

24.2 常用调试命令

# 重新生效
nginx -s reload

# 实时跟踪访问日志
tail -f /var/log/nginx/access.log

# 实时跟踪错误日志
tail -f /var/log/nginx/error.log

# 查看指定域名的请求
tail -f /var/log/nginx/access.log | grep "example.com"

# 统计最多访问的 IP(排查攻击)
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20

# 统计响应状态码分布
awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -rn

# 查看 Nginx 进程
ps aux | grep nginx

# 查看 Nginx 监听端口
ss -tlnp | grep nginx

24.3 开启 debug 日志

# 只对特定 IP 开启 debug(避免日志量过大)
error_log  /var/log/nginx/debug.log  debug;

events {
    debug_connection  192.168.1.100;  # 只 debug 这个 IP
}

24.4 常见问题速查

现象

排查方向

502 Bad Gateway

后端服务未启动,或 proxy_read_timeout 太短

504 Gateway Timeout

后端响应慢,增大 proxy_read_timeout

413 Request Entity Too Large

client_max_body_size 设置过小

403 Forbidden

文件权限不对(需 www-data 可读),或 deny all 生效

499 Client Closed Request

客户端主动断开,后端响应太慢

location 不生效

检查匹配优先级,用 nginx -T 确认最终配置

upstream 无法连接

确认后端端口和防火墙规则,检查 SELinux

SSL 握手失败

证书链不完整,或 TLS 版本不兼容

24.5 性能分析

# 查看 stub_status(需开启 stub_status 模块)
location = /nginx_status {
    stub_status;
    allow 127.0.0.1;
    deny  all;
}

# 输出示例:
# Active connections: 43
# server accepts handled requests
#  1000 1000 5000
# Reading: 1 Writing: 2 Waiting: 40
# 使用 ab 压测
ab -n 10000 -c 100 http://localhost/

# 使用 wrk 压测
wrk -t4 -c100 -d30s http://localhost/

最后更新:2025 年 | Nginx 版本:1.24 / 1.25(Mainline)