主题
03 · 配置文件结构 · 五层洋葱
生活类比:Nginx 配置文件就像一座写字楼:
- 整栋楼(main 块) → 物业管的事:用电、监控、保洁
- 进出门禁(events 块) → 门卫怎么登记访客
- 办公区域(http 块) → 公司租了哪一片
- 每家公司(server 块) → 一个虚拟主机(一个网站)
- 公司里的部门(location 块) → 不同 URL 路径的处理规则
1. 一张图把整体结构刻进脑子
┌─────────────────────────────────────────────────────┐
│ main(全局) │
│ ├─ user / worker_processes / pid / error_log │
│ │ │
│ ├─ events { ... } │
│ │ └─ worker_connections │
│ │ │
│ └─ http { │
│ ├─ include mime.types │
│ ├─ log_format / access_log │
│ ├─ sendfile / keepalive_timeout / gzip │
│ │ │
│ ├─ upstream backend { ... } │
│ │ │
│ ├─ server { │
│ │ listen 80; │
│ │ server_name foo.com; │
│ │ location / { ... } │
│ │ location /api/ { ... } │
│ │ } │
│ │ │
│ └─ server { ... 第二个网站 ... } │
│ } │
└─────────────────────────────────────────────────────┘记住这 5 层就够了:main → events → http → server → location。
2. 第 1 层 · main(全局)
直接写在文件顶部,不在任何 {} 里。
nginx
user nginx; # 用什么用户跑 worker(安全建议非 root)
worker_processes auto; # 几个工作进程,auto = CPU 核数
worker_rlimit_nofile 65535; # 每个进程最大可打开文件数(含 socket)
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;生活类比:物业告诉门卫"今天上班几个保安、每人最多记多少个访客"。
| 关键指令 | 意义 | 推荐值 |
|---|---|---|
user | worker 进程跑在哪个 OS 用户下 | nginx / www-data |
worker_processes | 工作进程数量 | auto(= CPU 核心数) |
worker_rlimit_nofile | 单进程最大文件描述符 | 至少 65535 |
error_log | 错误日志路径 + 级别(debug/info/warn/error) | warn 或 error |
pid | 主进程 PID 文件 | /run/nginx.pid |
3. 第 2 层 · events
nginx
events {
use epoll; # Linux 上最高性能的 I/O 多路复用
worker_connections 10240; # 每个 worker 同时能处理多少连接
multi_accept on; # 一次循环尽量多 accept 新连接
}生活类比:门卫用什么"对讲机型号"(epoll/kqueue)、一次能放几个人进来。
⚠️ 重要公式:
max_clients = worker_processes × worker_connections例子:4 核机器 + 默认配置 → 4 × 10240 ≈ 4 万并发连接。
4. 第 3 层 · http(全站通用配置)
nginx
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"';
access_log /var/log/nginx/access.log main;
# 性能相关
sendfile on; # 零拷贝传输文件
tcp_nopush on; # 攒包再发
keepalive_timeout 65; # 长连接保活时间
# 压缩
gzip on;
gzip_types text/plain text/css application/json application/javascript;
# 引入子配置(推荐拆分到 conf.d/*.conf)
include /etc/nginx/conf.d/*.conf;
}生活类比:写字楼物业贴在大堂的"全楼通用规定"——日志怎么记、是否要节能开关(gzip)、所有公司都得遵守。
5. 第 4 层 · server(一个网站)
nginx
server {
listen 80;
server_name example.com www.example.com;
root /var/www/example;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}每个 server 块 = 一个虚拟主机 = 一个网站。
生活类比:写字楼里有十几家公司,每家都有自己的"门牌号"(server_name)和"前台"(location)。
5.1 listen 的几种写法
nginx
listen 80; # 监听 IPv4 80 端口
listen 443 ssl http2; # 监听 443 + 启用 SSL + HTTP/2
listen [::]:80; # 监听 IPv6
listen 8080 default_server; # 该端口的"默认网站",没匹配上其他 server_name 时来这5.2 server_name 匹配优先级(重要⚠️)
nginx
server_name example.com # ① 精确匹配
*.example.com # ② 前缀通配
example.* # ③ 后缀通配
~^(www|api)\.example # ④ 正则匹配优先级:① > ② > ③ > ④ > default_server
6. 第 5 层 · location(最常用)
location 决定这个 URL 路径怎么处理——location 是 Nginx 配置的"灵魂",第 7 章会专门讲匹配规则,这里先看个皮毛:
nginx
server {
location / {
# 兜底规则:所有路径
root /var/www/html;
index index.html;
}
location /static/ {
# 静态资源
alias /var/www/cdn/;
expires 7d;
}
location /api/ {
# 反向代理到后端
proxy_pass http://localhost:3000/;
}
location ~* \.(jpg|png|gif)$ {
# 图片:1 年强缓存
expires 1y;
add_header Cache-Control "public, immutable";
}
}生活类比:公司前台贴的"业务办理指引"——
- "前台办的(
/)" → 自己处理 - "图片复印(
*.jpg)" → 给个长期通行证 - "技术维修(
/api/)" → 转给二楼工程部
7. 配置文件加载机制 · 一句话
nginx.conf
├─ include conf.d/*.conf ← 主流大厂这么拆
└─ include sites-enabled/* ← Debian 系默认拆法最佳实践:
- 公共配置写在
nginx.conf(main + events + http 头部) - 每个网站单独建一个
conf.d/<site>.conf(一个 server 块) - 改 conf 后必跑
nginx -t再nginx -s reload
8. 配置继承规则 · 父子关系
外层定义的,内层默认继承;内层重新定义的,覆盖外层。
nginx
http {
gzip on; # ← 父层
server {
# 这里没写 gzip → 继承 http 的 on
location /api/ {
gzip off; # ← 子层覆盖:API 不压缩
}
}
}生活类比:物业全楼禁烟(gzip on),但 22 楼老板特批办公室能抽烟(局部 gzip off)。
9. 变量系统 · 写灵活配置的关键
Nginx 内置一堆变量(以 $ 开头),方便你在 if / proxy_set_header / log_format 里用:
| 变量 | 含义 |
|---|---|
$host | 请求的 Host 头 |
$remote_addr | 客户端 IP |
$request_uri | 完整 URI(带 query,如 /foo?x=1) |
$uri | 规范化的 URI(不含 query,可被改写) |
$args / $query_string | URL 后面的查询字符串 |
$request_method | GET / POST / ... |
$http_user_agent | 浏览器 UA |
$cookie_<name> | 取某个 cookie |
$arg_<name> | 取某个 query 参数 |
$scheme | http 或 https |
应用举例 —— 接口防盗链:
nginx
location /api/ {
if ($http_referer !~* ^https?://(www\.)?example\.com) {
return 403;
}
proxy_pass http://backend/;
}10. 一份新手能直接抄的最小 nginx.conf
下方"💻 示例代码"里有 nginx.conf(带详细中文注释)和 example.com.conf(一个 server 块的标准模板)。
11. ⚠️ 配置常见错误
{}没配对 →nginx -t直接告诉你"unexpected}"- 指令末尾忘了
;→ 同上 include路径错了 → 仔细看nginx -t输出server_name写错没生效 → 先看是不是命中了default_server- 改配置忘了 reload →
docker exec nginx-xx nginx -s reload - 复制网上配置粘贴中文引号 → "不可见字符"鬼地狱(巨坑),永远用英文
"
12. 章末面试题速览
详见
qa.md第 6-8 题。
- Nginx 配置的层级结构是怎样的? → main / events / http / server / location(五层洋葱)。
- worker_processes 应该设几个? →
auto,等于 CPU 核数;I/O 密集型可适当 ×2。 - 同一个端口能配多个 server 吗? → 能,靠
server_name区分(虚拟主机);都没匹配上走default_server。
13. 一句话总结
看到 nginx.conf 不要怕:从外往里就是
main → events → http → server → location,外层管全局、内层管细节、内层覆盖外层。
下一章 → 04 · 静态文件服务:让 Nginx 当个合格的"网页保姆"。
💻 示例代码
💻 示例代码
txt
###############################################################################
# /etc/nginx/conf.d/example.com.conf
# 一个标准 server 块的入门模板:静态站 + /api 反向代理
###############################################################################
server {
listen 80;
server_name example.com www.example.com;
# 网站根目录
root /var/www/example;
index index.html;
# ---- 路由 1:根路径(页面) ----
location / {
try_files $uri $uri/ /index.html; # SPA 兜底(详见 04 章)
}
# ---- 路由 2:静态资源 → 强缓存 1 年 ----
location ~* \.(?:css|js|jpg|jpeg|png|gif|webp|svg|ico|woff2?)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off; # 静态资源不写 access log,省磁盘
}
# ---- 路由 3:API → 反向代理后端(详见 05 章) ----
location /api/ {
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;
}
# ---- 路由 4:健康检查 ----
location = /healthz {
access_log off;
return 200 "ok\n";
add_header Content-Type text/plain;
}
# ---- 错误页 ----
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}txt
###############################################################################
# nginx.conf · 学习版 · 带详细中文注释
# 目标:让你看完这一份配置,对"五层洋葱"有完整认知
###############################################################################
# ===== 第 1 层 · main(全局,写在文件顶部) =====================================
user nginx; # worker 进程跑在哪个 OS 用户下(推荐非 root)
worker_processes auto; # auto = CPU 核数;要排查问题可临时设 1 调试
worker_rlimit_nofile 65535; # 单进程最大文件描述符(含 socket)
# 错误日志:路径 + 级别。生产用 warn,调试时改 info / debug
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
# ===== 第 2 层 · events(连接处理模型) ========================================
events {
use epoll; # Linux 上首选 epoll;FreeBSD 用 kqueue
worker_connections 10240; # 每个 worker 同时能 hold 多少连接(含上游)
multi_accept on; # 一个事件循环尽量多接收新连接
accept_mutex off; # 现代多核机器关掉它,让所有 worker 都有机会 accept
}
# ===== 第 3 层 · http(全 HTTP 通用配置) ======================================
http {
# ---- MIME / 默认类型 ----
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" '
'rt=$request_time uct="$upstream_connect_time" '
'urt="$upstream_response_time"';
access_log /var/log/nginx/access.log main;
# ---- 性能 ----
sendfile on; # 零拷贝传输文件(kernel 直接把文件丢给 socket)
tcp_nopush on; # 配合 sendfile:等满 1 个 MSS 再发,省小包
tcp_nodelay on; # 长连接里小包立刻发(Nagle 算法关闭)
keepalive_timeout 65; # 长连接保活时间(秒)
keepalive_requests 1000; # 一条长连接最多复用多少次
# 隐藏 Nginx 版本号(防止扫描器盯上)
server_tokens off;
# 默认请求体上限(上传文件大的可在 server / location 内单独覆盖)
client_max_body_size 8m;
# ---- 压缩(gzip) ----
gzip on;
gzip_min_length 1k; # 小于 1KB 的不压(不划算)
gzip_comp_level 5; # 1 ~ 9,数字越大压缩比越高 / CPU 越累
gzip_vary on; # 加 Vary: Accept-Encoding 头,给 CDN 用
gzip_types
text/plain text/css application/json application/javascript
application/xml application/xml+rss image/svg+xml;
# ---- 引入子配置(推荐拆文件管理) ----
include /etc/nginx/conf.d/*.conf;
}