结论: Nginx 的配置本质是一棵"块嵌套树"——main 包含 events 与 http,http 包含若干 server,server 包含若干 location。指令只在特定作用域生效(如 worker_processes 只在 main,server_name 只在 server),子块未显式声明时继承父块值,数组型指令(如 proxy_set_header)则不继承而是完全覆盖。改完配置务必 nginx -t 校验再用 nginx -s reload 热加载,而不是重启。
引言:为什么照抄的配置常常不生效?
网上 Nginx 配置片段一抓一大把,但很多人照抄后发现"不生效":加了 client_max_body_size 还是报 413;在 location 里写了 proxy_set_header,server 里配的几条头全丢了;try_files 写对了却 404。
原因几乎都指向同一件事——不理解 Nginx 配置的作用域与继承规则。Nginx 不是"配置文件里写了就全局生效",每条指令都有严格的上下文(context),放错位置会直接报错 directive is not allowed here,或者更糟:静默地不生效。
本篇把 Nginx 配置的骨架、作用域、匹配优先级、变量、日志和调优参数一次讲透,目标是让你看完能自己写出配置,而不是拼凑片段。
一、配置文件结构树:先看清骨架
Nginx 主配置默认在 /etc/nginx/nginx.conf,通过 include 引入其他文件。以 Ubuntu 24.04 上的 Nginx 1.26(截至 2026 年主流稳定版)为例,完整结构如下:
/etc/nginx/
├── nginx.conf # 主配置:main + events + http
├── mime.types # 文件扩展名与 Content-Type 映射
├── fastcgi_params # FastCGI 默认参数(PHP 用)
├── proxy_params # 反向代理默认参数
├── sites-available/ # 可用站点配置(Debian 系约定)
│ ├── default
│ └── example.com.conf
├── sites-enabled/ # 已启用站点(通常是指向 available 的软链接)
│ └── default -> ../sites-available/default
└── conf.d/ # 额外配置片段,nginx.conf 中 include 进来
└── gzip.conf对应的逻辑块结构:
# ===== main 块(全局)=====
user www-data;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /run/nginx.pid;
# ===== events 块:连接处理模型 =====
events {
worker_connections 10240;
use epoll;
multi_accept on;
}
# ===== http 块:所有 HTTP 相关配置 =====
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'rt=$request_time urt=$upstream_response_time';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
server_tokens off;
gzip on;
gzip_types text/plain text/css application/json application/javascript;
# ===== upstream 块:定义后端服务器组 =====
upstream backend {
server 127.0.0.1:8080 weight=3 max_fails=3 fail_timeout=30s;
server 127.0.0.1:8081 weight=1 backup;
keepalive 32;
}
# ===== server 块:一个虚拟主机 =====
server {
listen 80;
server_name example.com www.example.com;
root /var/www/example;
index index.html index.htm;
# ===== location 块:按 URI 匹配 =====
location / {
try_files $uri $uri/ =404;
}
location /api/ {
proxy_pass http://backend;
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_connect_timeout 5s;
proxy_read_timeout 60s;
}
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 30d;
access_log off;
}
}
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}要点清单:
main块里没有{}包裹的指令就是全局指令,最外层。events和http只能在main下各出现一次(除非用include拆文件)。server必须在http内;location必须在server(或location)内。upstream在http内,但不在server内。- Debian/Ubuntu 用
sites-available+sites-enabled软链接管理站点;RHEL 系通常只用conf.d/*.conf。两种约定本质相同,不要混用导致重复定义。
二、指令作用域对照表:写错位置是最常见错误
每条 Nginx 指令都有固定的允许上下文(context),放错会报 nginx: [emerg] "xxx" directive is not allowed here。下表列出高频指令的作用域。
| 指令 | main | events | http | server | location | 说明 |
|---|---|---|---|---|---|---|
worker_processes | ✅ | ❌ | ❌ | ❌ | ❌ | 工作进程数,auto 为等于 CPU 核数 |
worker_connections | ❌ | ✅ | ❌ | ❌ | ❌ | 单进程最大连接数 |
use epoll | ❌ | ✅ | ❌ | ❌ | ❌ | Linux 下默认 epoll,一般无需写 |
error_log | ✅ | ❌ | ✅ | ✅ | ✅ | 作用域越小优先级越高 |
access_log | ❌ | ❌ | ✅ | ✅ | ✅ | 可在 location 中 off 关闭 |
log_format | ❌ | ❌ | ✅ | ❌ | ❌ | 只能在 http,这是最容易犯错的一条 |
include | ✅ | ✅ | ✅ | ✅ | ✅ | 任意位置 |
server_name | ❌ | ❌ | ❌ | ✅ | ❌ | 只能在 server 块 |
listen | ❌ | ❌ | ❌ | ✅ | ❌ | 只能在 server 块 |
root / alias | ❌ | ❌ | ✅ | ✅ | ✅ | location 中常用 |
index | ❌ | ❌ | ✅ | ✅ | ✅ | — |
try_files | ❌ | ❌ | ❌ | ✅ | ✅ | 不能写在 http |
proxy_pass | ❌ | ❌ | ❌ | ✅ | ✅ | location 中最常见 |
proxy_set_header | ❌ | ❌ | ✅ | ✅ | ✅ | 数组型指令,子块会覆盖父块 |
client_max_body_size | ❌ | ❌ | ✅ | ✅ | ✅ | 默认 1m,上传大文件必须调 |
gzip / gzip_types | ❌ | ❌ | ✅ | ✅ | ✅ | — |
expires | ❌ | ❌ | ✅ | ✅ | ✅ | 缓存控制头 |
upstream {} | ❌ | ❌ | ✅ | ❌ | ❌ | 定义后端组 |
ssl_certificate | ❌ | ❌ | ✅ | ✅ | ❌ | — |
if | ❌ | ❌ | ✅ | ✅ | ✅ | 仅在某些模块上下文中安全,慎用 |
三、继承与覆盖规则:为什么子块里的配置"丢了父块的"
Nginx 的继承规则分两类,理解这一点能解决一半的配置困惑:
- 普通(标量)指令:子块未声明时继承父块的值;子块声明则覆盖。例如 http 里
client_max_body_size 10m,server 不写就是 10m,写了就用自己的值。 - 数组型指令(如
proxy_set_header、add_header、fastcgi_param、access_log某些形式):子块一旦声明,完全替换父块的所有值,不合并。这是最经典的坑——server里配了 3 条proxy_set_header,location里只写 1 条,结果那 3 条全没了。
http {
add_header X-Frame-Options SAMEORIGIN; # http 级别
server {
# 未声明 add_header → 继承 http 的 X-Frame-Options
location /a/ {
add_header X-Custom "1"; # 声明了 → X-Frame-Options 不再生效!
}
}
}规避方法:在需要的每个子块里重复写全部头,或用 include 引入统一的头文件片段。
其他继承相关要点:
root在 location 中若使用相对路径,是相对server的root拼接,容易出错,location 中建议写绝对路径。error_log和access_log是"就近覆盖"——定义了就用最内层的。location嵌套时,内层 location 完全独立继承 server 级配置,而不是继承外层 location。
四、location 匹配优先级:一张表定胜负
location 的匹配顺序是 Nginx 面试和排错的高频考点,规则是固定的:
- 先匹配精确匹配
location = /path,命中立即结束。 - 再匹配前缀匹配,记住"最长"的那个;若最长的前缀带
^~,立即使用并结束。 - 若最长前缀没有
^~,则按顺序尝试正则匹配(~大小写敏感、~*不敏感),第一个命中的正则胜出。 - 正则都没命中,则使用第 2 步记住的最长前缀。
| 写法 | 类型 | 优先级 | 示例与说明 | |
|---|---|---|---|---|
location = /login | 精确匹配 | 最高(命中即结束) | 只匹配 /login,不匹配 /login/ | |
location ^~ /static/ | 前缀匹配(禁止正则) | 次高 | 匹配 /static/ 开头,且不再试正则 | |
location ~ \.php$ | 正则(大小写敏感) | 第三 | 按配置中出现顺序匹配 | |
| `location ~* \.(png\ | jpg)$` | 正则(大小写不敏感) | 第三 | 同上 |
location /api/ | 普通前缀 | 较长者优先 | 无正则命中时使用 | |
location / | 默认兜底 | 最低 | 所有请求最终都能落到这里 |
要点清单:
- 正则 location 的顺序有意义,写在前面的先匹配;把具体规则写前面,通用规则写后面。
^~是提升静态资源性能的常用手段,避免每个静态请求都跑一遍正则。location /api/与proxy_pass结尾是否带/会导致 URI 拼接差异,见下文 FAQ。
五、rewrite、变量与日志格式
5.1 rewrite 与重定向
rewrite 用于改写 URI,return 用于直接返回状态码——能用 return 就不用 rewrite。
# 全站 HTTP 跳 HTTPS(推荐用 return,性能更好、语义清晰)
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
# rewrite 三要素:regex replacement flag
# flag: last(重新走 location 匹配) / break(停止 rewrite) / redirect(302) / permanent(301)
location /old/ {
rewrite ^/old/(.*)$ /new/$1 permanent;
}
# 前端路由 SPA 的 history 模式兜底
location / {
try_files $uri $uri/ /index.html;
}常见 flag 对比:
| flag | 行为 | 典型用途 |
|---|---|---|
last | 改写后重新进入 location 匹配阶段 | 内部重写,继续走 PHP 解析等 |
break | 改写后停止 rewrite 指令,仍在当前 location 处理 | 静态文件重写 |
redirect | 返回 302 临时重定向 | 临时跳转 |
permanent | 返回 301 永久重定向 | 域名变更、HTTPS 强制跳转 |
5.2 常用内置变量
| 变量 | 含义 | 常见用途 |
|---|---|---|
$host | 请求头 Host(优先)或 server_name | 日志、proxy_set_header |
$request_uri | 完整原始 URI 含查询串(不改) | 301 跳转保留参数 |
$uri | 当前规范化后的 URI(可能被改写) | try_files |
$remote_addr | 客户端 IP(直连时) | 日志、限流 key |
$proxy_add_x_forwarded_for | 追加客户端 IP 到 XFF 链 | 反代透传真实 IP |
$request_method | 请求方法 GET/POST 等 | 条件判断 |
$request_time | 请求总耗时(秒,毫秒精度) | 慢请求排查 |
$upstream_response_time | 上游响应耗时 | 定位后端瓶颈 |
$status | 响应状态码 | 日志、条件 |
$http_user_agent | UA | 日志、爬虫识别 |
$args / $query_string | 查询串 | 条件判断、重写 |
5.3 日志格式定制
# 只能写在 http 块中
log_format detailed escape=json '{'
'"time":"$time_iso8601",'
'"remote_addr":"$remote_addr",'
'"request":"$request",'
'"status":$status,'
'"bytes":$body_bytes_sent,'
'"referer":"$http_referer",'
'"ua":"$http_user_agent",'
'"request_time":$request_time,'
'"upstream_time":"$upstream_response_time",'
'"upstream_addr":"$upstream_addr"'
'}';
access_log /var/log/nginx/access.log detailed buffer=64k flush=5s;
# 按状态码过滤:只记录 5xx 到单独文件(map 需在 http 块)
map $status $loggable {
~^5 1;
default 0;
}
access_log /var/log/nginx/error-only.log detailed if=$loggable;排查要点:$request_time 大而 $upstream_response_time 小,说明慢在 Nginx 自身或客户端网络;两者都大,说明慢在后端。
六、性能优化参数表与配置校验
Nginx 调优的核心参数集中在 events + http 两层,下表给出生产常用值。
| 参数 | 作用域 | 建议值 | 作用与注意 |
|---|---|---|---|
worker_processes | main | auto(等于 CPU 核数) | 超过核数不会更快,反而增加调度开销 |
worker_connections | events | 10240~65535 | 最大并发 ≈ worker_processes × worker_connections / 2(反代时除以 4) |
worker_rlimit_nofile | main | 65535 | 必须 ≥ worker_connections,同时调系统 ulimit -n |
use epoll | events | epoll(Linux 默认) | 高并发下效率远高于 select |
multi_accept | events | on | 一次 accept 多个连接 |
sendfile | http/server/location | on | 零拷贝发送静态文件 |
tcp_nopush | http | on | 与 sendfile 配合,减少小包 |
tcp_nodelay | http | on | keepalive 下降低延迟 |
keepalive_timeout | http | 60~75s | 太长会占用连接资源 |
keepalive_requests | http | 1000~10000 | 单连接最大请求数 |
client_max_body_size | http/server/location | 10m~100m | 默认仅 1m,上传必改 |
client_body_buffer_size | http | 128k~1m | 超过则写临时文件 |
gzip / gzip_comp_level | http | on / 1~4 | 压缩级别超过 4,CPU 换压缩率不划算 |
proxy_buffering | http/server/location | on(大文件下载关) | 关闭会导致后端连接长时间占用 |
proxy_read_timeout | location | 60s(长轮询按需调大) | 太短会 504 |
open_file_cache | http | max=10000 inactive=60s | 缓存文件描述符,静态站提升明显 |
server_tokens | http | off | 隐藏版本号,安全加固 |
配置校验与热加载(每次改配置的标准动作):
# 1. 语法校验,-t 会输出配置文件路径与是否 OK
sudo nginx -t
# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
# nginx: configuration file /etc/nginx/nginx.conf test is successful
# 2. 校验指定的非默认配置文件(-c 指定主配置)
sudo nginx -t -c /etc/nginx/nginx.conf
# 3. 热加载:master 进程重新读配置,平滑替换 worker,不断连接
sudo nginx -s reload
# 等价:sudo systemctl reload nginx
# 4. 其他信号
sudo nginx -s reopen # 重新打开日志文件(日志切割后)
sudo nginx -s quit # 优雅停止(处理完当前请求)
sudo nginx -s stop # 立即停止
# 5. 修改配置后忘记 reload 是最常见的"改了没生效"原因
企业QQ咨询




