文章目录
- 一、不同系统下的安装路径
- 二、配置文件结构
- 三、静态资源服务
-
- [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 实际的匹配过程是这样的(容易被误解):
- 先找有没有 = 精确匹配,命中立即返回
- 再做普通前缀匹配,找到 最长前缀 后先记录下来,不立即返回
- 如果第 2 步命中的是带 ^~ 修饰的前缀,直接返回,不再查正则
- 否则按 配置文件中出现的顺序 逐条查正则(~ / ~*),第一个命中的就用它
- 如果所有正则都没命中,回到第 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 缓存 / 代理链上某一层没改,再怀疑配置本身