Nginx 常用配置大全

文章目录

  • 一、不同系统下的安装路径
  • 二、配置文件结构
  • 三、静态资源服务
    • [3.1 基础静态文件服务](#3.1 基础静态文件服务)
    • [3.2 location 匹配符号详解](#3.2 location 匹配符号详解)
    • [3.3 静态资源缓存](#3.3 静态资源缓存)
    • [3.4 文件下载服务](#3.4 文件下载服务)
    • [3.5 大文件下载与断点续传](#3.5 大文件下载与断点续传)
    • [3.6 防盗链](#3.6 防盗链)
  • 四、反向代理
  • 五、负载均衡
    • [5.1 五种策略](#5.1 五种策略)
    • [5.2 生产环境的完整配置](#5.2 生产环境的完整配置)
    • [5.3 灰度发布](#5.3 灰度发布)
  • [六、HTTPS 配置(含 80 端口跳转)](#六、HTTPS 配置(含 80 端口跳转))
  • [七、Gzip 压缩](#七、Gzip 压缩)
  • [八、跨域 CORS](#八、跨域 CORS)
  • 九、限流
  • 十、常用命令
  • 十一、小结

一、不同系统下的安装路径

刚接触 Nginx 的时候,最容易卡住的不是配置,而是「我装的 Nginx 配置文件到底在哪」。不同发行版、不同安装方式,路径差别挺大

CentOS / RHEL(yum 安装)

bash 复制代码
- 主配置:`/etc/nginx/nginx.conf`
- 站点配置:`/etc/nginx/conf.d/*.conf`
- 静态资源:`/usr/share/nginx/html`
- 日志目录:`/var/log/nginx/`

Ubuntu / Debian(apt 安装)

bash 复制代码
- 主配置:`/etc/nginx/nginx.conf`
- 站点配置:`/etc/nginx/sites-available/` 配合 `/etc/nginx/sites-enabled/`
- 静态资源:`/var/www/html`
- 日志目录:`/var/log/nginx/`

Alpine(apk 安装)

bash 复制代码
- 主配置:`/etc/nginx/nginx.conf`
- 站点配置:`/etc/nginx/conf.d/*.conf`
- 静态资源:`/usr/share/nginx/html`
- 日志目录:`/var/log/nginx/`

macOS Homebrew(M 系列)

bash 复制代码
- 主配置:`/opt/homebrew/etc/nginx/nginx.conf`
- 站点配置:`/opt/homebrew/etc/nginx/servers/`
- 静态资源:`/opt/homebrew/var/www`
- 日志目录:`/opt/homebrew/var/log/nginx/`

macOS Homebrew(Intel 芯片)

bash 复制代码
- 主配置:`/usr/local/etc/nginx/nginx.conf`
- 站点配置:`/usr/local/etc/nginx/servers/`
- 静态资源:`/usr/local/var/www`
- 日志目录:`/usr/local/var/log/nginx/`

源码编译安装(默认前缀)

bash 复制代码
- 主配置:`/usr/local/nginx/conf/nginx.conf`
- 站点配置:无独立目录,需要在主配置里手动 `include`
- 静态资源:`/usr/local/nginx/html`
- 日志目录:`/usr/local/nginx/logs/`

Docker 官方镜像

bash 复制代码
- 主配置:`/etc/nginx/nginx.conf`
- 站点配置:`/etc/nginx/conf.d/*.conf`
- 静态资源:`/usr/share/nginx/html`
- 日志:默认输出到 stdout 和 stderr,方便 `docker logs` 查看

二、配置文件结构

Nginx 的配置是分层的,从外到内依次是 全局 → events → http → server → location,内层会继承外层。

conf 复制代码
# ============ 全局块 ============
user  nginx;                       # 运行 Nginx 的用户
worker_processes  auto;            # 工作进程数,auto 表示与 CPU 核心数一致
events {
    # ============ events 块 ============
    worker_connections  1024;      # 每个工作进程的最大连接数
}
http {
    # ============ http 块 ============
    include       mime.types;      # 引入 MIME 类型映射
    default_type  application/octet-stream;  # 默认 MIME 类型
    server {
        # ============ server 块 ============
        listen       80;           # 监听 80 端口
        server_name  example.com;  # 绑定的域名
        location / {
            # ============ location 块 ============
            root   html;           # 根目录
            index  index.html;     # 默认首页
        }
    }
}

三、静态资源服务

3.1 基础静态文件服务

最常见的用法,把 Nginx 当一个静态文件服务器:

conf 复制代码
server {
    listen 80;                          # 监听 80 端口
    server_name static.example.com;     # 绑定静态资源域名
    root /data/www;                     # 网站根目录,所有 location 默认继承
    index index.html index.htm;         # 默认首页文件,按顺序查找
    location / {
        try_files $uri $uri/ =404;      # 依次尝试:文件 → 目录 → 返回 404
    }
}

try_files 比直接 root + autoindex 更安全,可以避免在没有 index 文件时暴露目录结构

前端打包目录的完整配置示例

平时用得最多的就是部署 Vue / React 这类 SPA 项目。npm run build 出来的目录一般长这样:

bash 复制代码
dist/
├── index.html
├── favicon.ico
├── static/
│   ├── js/
│   │   ├── app.a1b2c3d4.js
│   │   └── vendor.e5f6g7h8.js
│   ├── css/
│   │   └── app.i9j0k1l2.css
│   └── img/
│       └── logo.png
└── assets/
    └── ...

对应的 Nginx 配置:

conf 复制代码
server {
    listen       80;
    server_name  app.example.com;                    # 前端域名
    root  /data/www/dist;                            # 指向打包产物目录
    index index.html;                                # 默认入口文件
    # 开启 Gzip,传输体积可以减小 60% 以上
    gzip               on;                           # 开启压缩
    gzip_min_length    1k;                           # 小于 1KB 不压缩
    gzip_comp_level    6;                            # 压缩级别 1-9
    gzip_types         text/plain text/css application/json
                       application/javascript image/svg+xml;  # 压缩的 MIME 类型
    # ----- 首页入口:不缓存 -----
    # 浏览器访问 / 拿到的就是 index.html,必须不缓存
    # 否则发版后用户拿到旧入口,引用的还是旧版本 JS/CSS hash,导致 404
    location = / {
        try_files /index.html =404;
        add_header Cache-Control "no-cache, no-store, must-revalidate";
        add_header Pragma "no-cache";
        expires 0;
    }
    # ----- favicon:单独处理,避免被兜底成 index.html -----
    location = /favicon.ico {
        access_log    off;                            # 不记日志
        log_not_found off;                            # 找不到也不报错
    }
    # ----- 带 hash 的静态资源:长期强缓存 -----
    # 文件名带 hash,发版会变文件名,所以可以放心缓存一年
    location ^~ /static/ {
        expires 1y;                                   # 浏览器缓存 1 年
        add_header Cache-Control "public, immutable"; # 告诉浏览器内容永不变
        access_log off;                               # 静态资源不记日志
    }
    # ----- 接口反代到后端服务 -----
    # 前端调 /api/xxx,Nginx 转发到后端,避免前端跨域
    location /api/ {
        proxy_pass http://127.0.0.1:8080/;            # 末尾带 /,去掉 /api/ 前缀
        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;
        client_max_body_size 20m;                     # 上传走 /api/ 的话调大
    }
    # ----- 兜底:SPA history 路由 -----
    # Vue Router / React Router 用 history 模式时,刷新 /user/123 这种路径
    # 服务器上并没有对应文件,需要回退到 index.html 让前端路由接管
    location / {
        try_files $uri $uri/ /index.html;             # 文件 → 目录 → 回到 index.html
    }
    # ----- 安全相关响应头(按需开启)-----
    add_header X-Frame-Options          "SAMEORIGIN";  # 防止被嵌入 iframe
    add_header X-Content-Type-Options   "nosniff";     # 禁止 MIME 嗅探
    add_header Referrer-Policy          "no-referrer-when-downgrade";
}

几个容易踩的点:

  • index.html 必须配 no-cache,否则发版后用户看到的还是旧的入口,会去请求已经被删除的旧 hash 文件
  • 带 hash 的静态资源可以放心配 immutable,浏览器以后连"问一下服务器是不是变了"都省了
  • SPA 项目一定要配 try_files uri uri/ /index.html,否则刷新内页会 404
  • proxy_pass 末尾的 / 决定了前缀要不要去掉,注意区分

3.2 location 匹配符号详解

上一节示例里出现了 *、^、= 这些符号,对应不同的匹配方式,先把它们一次性讲清楚,后面的配置就容易看懂了。

各符号的作用

= :精确匹配

conf 复制代码
location = /favicon.ico {
    # 只匹配请求路径正好是 /favicon.ico 的请求
    # /favicon.ico/  ❌ 不匹配(多了斜杠)
    # /favicon.ico?v=1  ✅ 匹配(查询参数不算路径)
    log_not_found off;     # 没找到也不记日志
    access_log off;
}

适合频繁访问、路径固定的资源(首页、favicon、健康检查接口),匹配速度最快。

^~ :前缀匹配(命中后不再查正则)

conf 复制代码
location ^~ /static/ {
    # 凡是 /static/ 开头的请求一律命中这条
    # 即使下面还有 ~* \.css$ 这种正则,也不会再去比
    expires 1y;
    add_header Cache-Control "public, immutable";
}

适合明确按目录区分的静态资源前缀,能跳过后续的正则匹配,性能更好。

~ :正则匹配(区分大小写)

bash 复制代码
location ~ \.PHP$ {
    # 只匹配 .PHP 结尾,不匹配 .php
    # /index.PHP  ✅
    # /index.php  ❌
    deny all;
}

Linux 文件系统区分大小写时常用,实际项目里 ~* 用得更多。

*~ :正则匹配(不区分大小写)**

bash 复制代码
location ~* \.(jpg|jpeg|png|gif)$ {
    # /a.JPG /a.jpg /a.Png 都能命中
    expires 30d;
}

最常用,绝大多数按后缀匹配的场景都用它。

无修饰符 :普通前缀匹配

bash 复制代码
location /api/ {
    # 匹配 /api/ 开头的请求
    # 但如果还有更长的前缀如 /api/v2/ 命中,会优先走长的
    proxy_pass http://backend;
}

最朴素的写法,遵循"最长前缀优先"。

@ :命名 location(只能内部跳转)

bash 复制代码
location / {
    try_files $uri @fallback;     # 文件找不到时跳到 @fallback
}
location @fallback {
    # 不能被外部直接访问,只能通过 try_files / error_page 等指令内部跳进来
    proxy_pass http://backend;
}

常用来做兜底逻辑,比如静态资源找不到时回退到后端处理。

匹配优先级

同一个请求可能命中多个 location,Nginx 实际的匹配过程是这样的(容易被误解):

  1. 先找有没有 = 精确匹配,命中立即返回
  2. 再做普通前缀匹配,找到 最长前缀 后先记录下来,不立即返回
  3. 如果第 2 步命中的是带 ^~ 修饰的前缀,直接返回,不再查正则
  4. 否则按 配置文件中出现的顺序 逐条查正则(~ / ~*),第一个命中的就用它
  5. 如果所有正则都没命中,回到第 2 步记录的最长前缀

简化记法(从高到低):= > ^~ > 正则 > 普通前缀。但严格说"普通前缀"和"正则"是有交互的,正则只能"打断"普通前缀的命中,不能反过来。

注意第 4 条:正则匹配是按配置顺序的,所以把更精确的正则写在前面。

一个例子看懂

bash 复制代码
server {
    listen 80;
    server_name example.com;
    location = / {
        return 200 "精确匹配根路径";
    }
    location ^~ /static/ {
        return 200 "前缀 /static/ 命中后不查正则";
    }
    location ~* \.(jpg|png)$ {
        return 200 "图片正则匹配";
    }
    location /api/ {
        return 200 "普通前缀匹配 /api/";
    }
    location / {
        return 200 "兜底";
    }
}

不同请求的命中结果:

bash 复制代码
- `GET /`              → "精确匹配根路径"(`=` 优先级最高)
- `GET /static/a.jpg`  → "前缀 /static/ 命中后不查正则"(`^~` 直接拦截,不会去匹配 `~*`)
- `GET /a.jpg`         → "图片正则匹配"(命中 `~*`)
- `GET /api/users`     → "普通前缀匹配 /api/"
- `GET /about`         → "兜底"

实用建议

bash 复制代码
- 高频访问的固定路径(favicon、health)用 `=`,最快
- 按目录前缀分流(如 `/static/`)用 `^~`,跳过正则更快
- 按后缀分流(如 `\.(jpg|png)$`)用 `~*`,注意 **不要写得太宽**,复杂的正则匹配很耗 CPU
- 调试时如果不确定命中哪条,在 location 里加 `add_header X-Matched "/static/"` 这种响应头,`curl -I` 一看便知

3.3 静态资源缓存

在 SPA 示例里已经用过这套规则,这里单独按文件类型抽出来讲一下,便于针对老项目(非 SPA)灵活调整。

conf 复制代码
# 图片、字体:长期缓存
location ~* \.(jpg|jpeg|png|gif|ico|webp|woff2?|ttf|eot)$ {
    expires 30d;                                       # 浏览器缓存 30 天
    add_header Cache-Control "public, no-transform";   # 公共缓存,禁止中间代理修改
    access_log off;                                    # 不记访问日志,减少 IO
}
# JS / CSS:配合文件名 hash 可以长期缓存
location ~* \.(js|css)$ {
    expires 7d;                                        # 缓存 7 天
    add_header Cache-Control "public";                 # 允许 CDN/代理缓存
    access_log off;
}
# HTML 文件:不缓存,保证发布即时生效
location ~* \.html$ {
    add_header Cache-Control "no-cache, no-store, must-revalidate";  # 禁止缓存
    add_header Pragma "no-cache";                                    # 兼容 HTTP/1.0
    expires 0;  
}

3.4 文件下载服务

如果需要让浏览器直接下载文件(而不是在线打开),可以这样配:

conf 复制代码
location /download/ {
    alias /data/files/;                                        # 实际文件路径(注意末尾 /)
    autoindex on;                                              # 开启目录浏览
    autoindex_exact_size off;                                  # 文件大小用 KB/MB 显示
    autoindex_localtime on;                                    # 显示本地时间
    # 命中下列后缀时强制下载
    # 注:Nginx 官方有句名言「if is evil」,if 在 location 里行为微妙
    # 不过 add_header 是少数允许的合法场景之一,可以放心用
    if ($request_filename ~* ^.*?\.(zip|rar|pdf|doc|docx|xls|xlsx|mp4)$) {
        add_header Content-Disposition "attachment";           # 触发浏览器下载行为
        add_header Content-Type application/octet-stream;      # 二进制流类型
    }
    # 文件传输优化
    sendfile on;                                               # 启用零拷贝
    tcp_nopush on;                                             # 累积数据一次性发送
    sendfile_max_chunk 1m;                                     # 单次最大发送 1MB
}

root 和 alias 的区别

这两个指令容易混。简单记:root 是拼接,alias 是替换。

conf 复制代码
# root:把 location 路径拼到 root 后面
location /download/ {
    root /data/files;
}
# 请求 /download/a.zip → 查找 /data/files/download/a.zip
# alias:用 alias 路径替换掉 location 路径
location /download/ {
    alias /data/files/;
}
# 请求 /download/a.zip → 查找 /data/files/a.zip

3.5 大文件下载与断点续传

conf 复制代码
location /bigfile/ {
    alias /data/bigfiles/;             # 文件目录
    # 断点续传需要支持 HTTP Range
    add_header Accept-Ranges bytes;    # 告诉客户端支持范围请求
    # 限速,避免单用户跑满带宽
    limit_rate_after 10m;              # 前 10MB 不限速
    limit_rate 500k;                   # 之后限速 500KB/s
    # 大文件传输调优
    sendfile on;                       # 零拷贝
    tcp_nopush on;                     # 累积发送
    aio on;                            # 异步 IO(Linux 内核 ≥ 2.6.22)
    directio 5m;                       # 大于 5MB 走 directio,绕过系统缓存
    output_buffers 2 1m;               # 输出缓冲区:2 个 × 1MB
}

3.6 防盗链

防止图片、视频被其他站点直接外链:

bash 复制代码
location ~* \.(jpg|png|gif|mp4)$ {
    # valid_referers:定义合法的 referer 来源
    # none     ------ 允许没有 referer 的请求
    # blocked  ------ 允许被防火墙/代理去掉 referer 的请求
    # 后面跟允许的域名
    valid_referers none blocked example.com *.example.com;
    if ($invalid_referer) {            # referer 不在白名单
        return 403;                    # 直接 403
        # rewrite ^/ /images/403.png break;  # 或返回一张占位图(图片需自己存在)
    }
}

四、反向代理

conf 复制代码
location /api/ {
    proxy_pass http://127.0.0.1:8080/;                              # 转发到后端
    # 透传原始请求信息
    proxy_set_header Host              $host;                       # 原始 Host 头
    proxy_set_header X-Real-IP         $remote_addr;                # 客户端真实 IP
    proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;  # 代理链 IP 列表
    proxy_set_header X-Forwarded-Proto $scheme;                     # 原始协议(http/https)
    # 超时
    proxy_connect_timeout 60s;                                       # 与后端建连超时
    proxy_send_timeout    60s;                                       # 发送请求超时
    proxy_read_timeout    60s;                                       # 读取响应超时
    # 缓冲区
    proxy_buffering    on;                                           # 开启响应缓冲
    proxy_buffer_size  16k;                                          # 响应头缓冲区
    proxy_buffers      4 64k;                                        # 响应体缓冲区
}

proxy_pass 末尾带不带 / 行为不一样,

五、负载均衡

5.1 五种策略

conf 复制代码
# ========== 策略一:轮询(默认)==========
upstream backend_rr {
    server 192.168.1.10:8080;        # 节点 1
    server 192.168.1.11:8080;        # 节点 2
    server 192.168.1.12:8080;        # 节点 3
    # 请求按顺序依次分配,后端宕机会自动跳过
}
# ========== 策略二:加权轮询 ==========
upstream backend_weight {
    server 192.168.1.10:8080 weight=5;   # 权重 5,承担 5/8 流量
    server 192.168.1.11:8080 weight=2;   # 权重 2,承担 2/8 流量
    server 192.168.1.12:8080 weight=1;   # 权重 1,承担 1/8 流量
    # 适合后端机器配置不一致的场景
}
# ========== 策略三:IP 哈希 ==========
upstream backend_iphash {
    ip_hash;                              # 按客户端 IP 计算 hash
    server 192.168.1.10:8080;             # 同一 IP 命中同一台后端
    server 192.168.1.11:8080;             # 早期常用来做会话保持(同一用户固定到同一台机器)
}

不过现在的最佳实践是把 session 放进 Redis 或者用 JWT 这种无状态方案,ip_hash 在客户端走代理、IP 不固定时反而会出问题,新项目尽量不要依赖它。

conf 复制代码
# ========== 策略四:最少连接 ==========
upstream backend_leastconn {
    least_conn;                           # 优先选当前连接数最少的节点
    server 192.168.1.10:8080;             # 适合请求耗时差异较大的场景
    server 192.168.1.11:8080;
}
# ========== 策略五:一致性哈希 ==========
upstream backend_hash {
    hash $request_uri consistent;         # 按 URI 哈希,consistent 表示一致性哈希
    server 192.168.1.10:8080;             # 适合缓存场景,相同 URL 命中同一节点
    server 192.168.1.11:8080;
}

5.2 生产环境的完整配置

只配策略是不够的,生产环境还要考虑健康检查、长连接复用、故障重试。

conf 复制代码
upstream backend {
    least_conn;                                                # 策略:最少连接
    # 主节点
    server 192.168.1.10:8080 weight=3 max_fails=3 fail_timeout=30s;
    server 192.168.1.11:8080 weight=2 max_fails=3 fail_timeout=30s;
    server 192.168.1.12:8080 weight=1 max_fails=3 fail_timeout=30s;
    # 备用节点:主节点全挂时才启用
    server 192.168.1.13:8080 backup;
    # 与后端复用长连接,减少握手开销
    keepalive          32;        # 每个 worker 保留的最大空闲长连接数
    keepalive_requests 1000;      # 每个长连接最多复用 1000 次请求
    keepalive_timeout  60s;       # 空闲超时
}
server {
    listen 80;
    server_name api.example.com;
    location / {
        proxy_pass http://backend;                  # 转发到 upstream
        # 用 upstream 长连接必须配下面两项
        proxy_http_version 1.1;                     # HTTP/1.1(默认是 1.0)
        proxy_set_header   Connection "";           # 清掉 Connection 头(默认 close)
        # 故障自动重试
        proxy_next_upstream error timeout http_500 http_502 http_503 http_504;
        proxy_next_upstream_tries   3;              # 最多重试 3 个节点
        proxy_next_upstream_timeout 10s;            # 重试总超时
        # 透传请求信息
        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 行常用参数:

bash 复制代码
-`weight`:权重,数字越大流量越多
-`max_fails`:允许的最大失败次数
-`fail_timeout`:失败次数超限后,该节点被熔断的时间
-`backup`:备用机,主节点全挂时启用
-`down`:手动标记节点下线
-`max_conns`:该节点的最大并发连接数

5.3 灰度发布

简单粗暴的做法:用 down 把要下线的节点摘掉

conf 复制代码
upstream backend {
    server 192.168.1.10:8080;             # 在线
    server 192.168.1.11:8080 down;        # 标记下线,不再分流
    server 192.168.1.12:8080;             # 在线
}

更灵活的做法是按请求头分流,让带特定标记的请求走灰度集群:

conf 复制代码
# 定义两个集群
upstream backend_stable {
    server 192.168.1.10:8080;             # 稳定版
}
upstream backend_gray {
    server 192.168.1.20:8080;             # 灰度版
}
# 按请求头 X-Gray 选择集群
map $http_x_gray $backend_pool {
    default  backend_stable;              # 默认走稳定版
    "true"   backend_gray;                # 带 X-Gray: true 走灰度版
}
server {
    location / {
        proxy_pass http://$backend_pool;  # 动态选集群
    }
}

六、HTTPS 配置(含 80 端口跳转)

生产环境一般要配两个 server 块:一个监听 443 处理 HTTPS 请求,一个监听 80 把 HTTP 请求重定向到 HTTPS。

conf 复制代码
# ============ HTTP:301 永久重定向到 HTTPS ============
server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$host$request_uri;            # 永久重定向
    # $host        ------ 请求的 Host 头
    # $request_uri ------ 完整原始请求(含查询参数)
    # 用 return 不用 rewrite,性能更好、语义也清晰
}
# ============ HTTPS:实际处理业务 ============
server {
    listen 443 ssl http2;                                          # 监听 443,启用 SSL 和 HTTP/2
    server_name example.com www.example.com;
    # 证书
    ssl_certificate     /etc/nginx/ssl/example.com.crt;            # 公钥证书
    ssl_certificate_key /etc/nginx/ssl/example.com.key;            # 私钥
    # 协议与加密套件
    ssl_protocols       TLSv1.2 TLSv1.3;                           # 禁用老旧 SSL/TLS 版本
    ssl_ciphers         ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;                                  # 优先使用服务端加密套件
    # 会话缓存,减少握手开销
    ssl_session_cache    shared:SSL:10m;
    ssl_session_timeout  10m;
    # HSTS,强制后续请求用 HTTPS
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    location / {
        root /data/www;
        index index.html;
    }
}

七、Gzip 压缩

下面这些指令一般放在 http 块里全局生效,让所有 server 都启用 Gzip。也可以下沉到 server 或 location 单独开关。

conf 复制代码
gzip on;                                  # 开启 Gzip
gzip_min_length  1k;                      # 小于 1KB 不压缩(压缩反而更大)
gzip_comp_level  6;                       # 压缩级别 1-9,6 比较平衡
gzip_buffers     4 16k;                   # 压缩缓冲区:4 × 16KB
gzip_http_version 1.1;                    # HTTP/1.1 及以上才压缩
gzip_vary        on;                      # 添加 Vary: Accept-Encoding
gzip_disable     "MSIE [1-6]\.";          # 老 IE 不支持,禁用
# 压缩的 MIME 类型(图片/视频自身已压缩,别重复压)
gzip_types
    text/plain
    text/css
    text/xml
    application/json
    application/javascript
    application/xml
    application/xml+rss
    image/svg+xml;

八、跨域 CORS

conf 复制代码
location /api/ {
    # ===== 预检请求(OPTIONS)单独处理 =====
    # 注意:在 if 内 return 会跳过外层的 add_header
    # 所以预检的响应头必须写在 if 块内,否则浏览器拿不到 CORS 头,跨域失败
    if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Allow-Origin'      'https://www.example.com';
        add_header 'Access-Control-Allow-Methods'     'GET, POST, PUT, DELETE, OPTIONS';
        add_header 'Access-Control-Allow-Headers'     'Content-Type, Authorization, X-Requested-With';
        add_header 'Access-Control-Allow-Credentials' 'true';
        add_header 'Access-Control-Max-Age'           '3600';
        add_header 'Content-Length' 0;
        add_header 'Content-Type'   'text/plain charset=UTF-8';
        return 204;                       # 预检直接 204,不转发后端
    }
    # ===== 实际请求(GET/POST/...)也要带 CORS 头 =====
    # 用 always 保证错误响应(4xx/5xx)也带,避免前端拿不到错误信息
    add_header 'Access-Control-Allow-Origin'      'https://www.example.com' always;
    add_header 'Access-Control-Allow-Credentials' 'true' always;
    proxy_pass http://backend;
}

几个常见坑:

  • 预检请求 OPTIONS 不带 CORS 头是最常见的失败原因,必须在 if 块内手动加 add_header
  • 生产环境的 Access-Control-Allow-Origin 不要写 *,要写明确域名,否则不能配合 Allow-Credentials: true
  • add_header 是追加不是替换,如果后端已经返回了同名 CORS 头,浏览器会看到两个值导致失败;要么后端不加,要么 Nginx 用 headers-more 模块强制覆盖

九、限流

防爆破、防刷常用。

conf 复制代码
# ============ http 块中定义限流规则 ============
# 按客户端 IP 限流,每秒 10 个请求,共享内存 10MB
limit_req_zone $binary_remote_addr zone=req_limit:10m rate=10r/s;
# 按客户端 IP 限制并发连接数
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
server {
    # 登录接口:严格限流
    location /login {
        limit_req zone=req_limit burst=5 nodelay;     # 突发允许 5 个,超出立刻拒绝
        # burst   ------ 突发队列长度
        # nodelay ------ 超过 rate 立即处理,不加会排队等
        proxy_pass http://backend;
    }
    # 下载接口:限制并发连接
    location /download/ {
        limit_conn conn_limit 3;                       # 同一 IP 最多 3 个并发连接
        alias /data/files/;
    }
}

十、常用命令

bash 复制代码
nginx -t              # 检查配置语法
nginx -s reload       # 平滑重载(不中断现有连接)
nginx -s stop         # 快速停止(立即终止)
nginx -s quit         # 优雅停止(处理完当前请求再退出)
nginx -V              # 查看版本号和编译参数
nginx -c /path/conf   # 指定配置文件启动

十一、小结

整篇配置可以按使用场景对照着翻:

  • 新机器装好不知道配置在哪 → 第一章,按发行版查路径,或直接 nginx -V 看编译参数
  • 部署一个前端项目 → 3.1 的 SPA 完整示例,覆盖入口、hash 资源、接口反代、history 路由
  • 看不懂别人的 *^ → 3.2 location 匹配符号,附优先级和命中示例
  • 静态资源加缓存 / 文件下载 / 防盗链 → 3.3、3.4、3.6
  • 后端是集群 → 第五章,从 5.1 选策略,再参考 5.2 加上熔断和长连接
  • 上 HTTPS → 第六章,一个 server 跳转 + 一个 server 提供服务
  • 前后端跨域报错 → 第八章,注意 OPTIONS 预检要单独 add_header
  • 接口被刷 / 登录被爆破 → 第九章限流

一些反复出现、值得单独记的点:

  • proxy_pass 末尾的 /:决定后端拿到的路径是否带 location 前缀,每次写完最好用 curl -v 验证一次
  • root 与 alias:root 是拼接,alias 是替换,文件下载和静态托管的 404 很多源自这里
  • if 在 location 里少用:少数允许的场景(return、rewrite、add_header)之外,能用 map 就用 map
  • 改完配置先 nginx -t:语法错误时 reload 不会真的生效,且会留下进程状态不一致的隐患
  • 新项目别依赖 ip_hash 做会话保持:session 放 Redis 或者用 JWT 才是当下的做法
  • 修改配置后总有一次"奇怪的现象":先怀疑浏览器缓存 / CDN 缓存 / 代理链上某一层没改,再怀疑配置本身