配置格式

Nginx 配置工程化:目录结构与配置组织

Nginx 配置最容易膨胀成一份三千行的 monolith,改一个 upstream 要全量 review。本文给出一套可落地的目录拆分方案:include 边界怎么划、map 与 geo 怎么用、location 匹配优先级如何用测试固化下来。

作者:巧匠团队·9 分钟阅读·更新于 2026-09-30

为什么必须拆分 include

Nginx 的主配置只适合承载「全局骨架」:worker 进程数、事件模型、日志格式、以及指向各个功能片段的 include。把业务站点直接写进主配置的后果是任何改动都要全量 diff,评审成本随行数线性上升,回滚粒度也粗得可怕。

合理的切分单位是「按职责」而不是「按行数」:HTTP 通用设置一份、upstream 池一份、每个 server 站点一份、location 级别再按功能(限流、静态资源缓存、真实 IP)拆分。这样新增站点只需新增文件并加一行 include。

要注意 include 的路径是相对于 `prefix`(通常是 `/etc/nginx`)而非当前配置文件所在目录,这是最常见的踩坑点。规范的做法是全仓库统一用绝对路径,并在 README 里写清目录约定。

# /etc/nginx/nginx.conf 主配置只留骨架
user  nginx;
worker_processes  auto;

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

  log_format  main  "$remote_addr - $request_uri $status $body_bytes_sent $request_time";
  access_log  /var/log/nginx/access.log  main;

  sendfile        on;
  keepalive_timeout  65;

  # map 必须定义在 http 层级,才能被 server/location 引用
  include /etc/nginx/conf.d/maps/*.conf;
  include /etc/nginx/conf.d/upstreams/*.conf;
  include /etc/nginx/conf.d/servers/*.conf;
}

location 匹配优先级必须写进文档

location 的匹配顺序是最容易出错也最难凭直觉确认的部分:先精确匹配 `=` 的 uri,再处理最长前缀匹配(同一最长前缀内,正则按声明顺序优先,`^~` 则直接终止正则匹配),最后才是正则与前缀兜底。这套规则必须以表格形式固化在文档里。

实践中的常见错误是同时使用 `^~` 和正则。对静态资源目录用 `^~` 是正确的(避免正则误伤),但如果同一层又写了 `location ~ \.php$` 之类的正则,命中顺序就会变得难以预测。稳妥做法是:静态资源一律 `^~`,动态请求才进正则。

验证方式是把优先级写成可执行的断言。用 `curl -I` 对每类路径做一次探测,断言响应头是否符合预期;再配合 `nginx -T` 打印展开后的完整配置,确认 include 全部生效且没有重复定义。

# 静态资源:^~ 终止正则匹配,避免被 location ~ .* 抢走
location ^~ /static/ {
  root /var/www/app;
  expires 30d;
  add_header Cache-Control "public, immutable";
  access_log off;
}

# 精确匹配:优先级最高
location = /healthz {
  default_type application/json;
  return 200 "{\"status\":\"ok\"}";
}

# 正则匹配:只处理动态请求
location ~ \.php$ {
  fastcgi_pass unix:/run/php-fpm.sock;
  fastcgi_index index.php;
  include fastcgi_params;
  fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}

# 兜底反代:只有上面都没命中才会到这里
location / {
  proxy_pass http://app_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;
}

map 与 geo:把重复 if 变成声明式

`map` 在 http 层级建立「输入 → 输出」的映射表,常用于把 User-Agent 转成限流键、把域名规范化为上游名、或把老参数名映射到新名字。它替代的是一长串 `if ($http_user_agent ~ ...)`,而且支持默认值 `default`,不会出现某个分支没命中就静默落空。

`geo` 专门处理 IP 归属,`geo $variable { default 0; 1.0.0.0/8 1; ... }` 之后可以在 server 里直接 `if ($is_cn)` 做判断。两者都支持 `include`,所以 IP 段列表和 UA 规则都可以独立维护,甚至从 GeoIP 数据库自动生成。

注意事项:`map` 的第一个参数可以是变量、字符串或正则,但**不能是**组合了捕获的复杂变量(历史上对 `$http_x_forwarded_for` 之类的处理有限制);`geo` 的 CIDR 列表很大时应放在独立文件里,避免主配置膨胀。

# conf.d/maps/limit-key.conf  —— http 层级
map $http_user_agent $limit_key {
  default                    "other";
  "~*bot|crawler|spider"    "crawler";
  "~*mobile|android|iphone" "mobile";
  ""                         "nobot";
}

map $host $upstream_name {
  default              "app_backend";
  api.example.com      "api_backend";
  admin.example.com    "admin_backend";
}

# conf.d/maps/geo.conf —— CIDR 列表放独立文件
geo $is_blocked_region {
  default 0;
  include /etc/nginx/geo/blocked.cidr;
}

server {
  listen 80;
  server_name example.com;

  if ($is_blocked_region) {
    return 403;
  }

  location /api/ {
    limit_req zone=perip burst=20 nodelay;
    proxy_pass http://$upstream_name;
  }
}

配置校验与安全基线

任何配置改动都必须先过 `nginx -t`,再 reload,绝不直接 `restart`。`-t` 会解析所有 include 并做语法与指令合法性检查;配合 `-T` 可以打印展开后的完整配置,便于 diff 审查实际生效内容。注意 `-t` 只做静态检查,不验证上游主机可达性。

安全基线里最常被漏掉的两项:一是隐藏版本号 `server_tokens off;`,二是 `limit_req`/`limit_conn` 与 `client_max_body_size` 的显式设置——默认 1MB 上传限制经常在接入新功能时突然冒出来。TLS 层面建议启用 HSTS 并关闭弱协议(`ssl_protocols TLSv1.2 TLSv1.3`)。

工程上把校验写进 CI:仓库里维护渲染模板(模板 + 环境变量 → 最终配置),CI 渲染后跑 `nginx -t`,通过才允许部署。这样环境差异不会漂移到线上,也避免了「本地 nginx 版本与线上不一致」导致的隐性兼容问题。

http {
  server_tokens off;
  client_max_body_size 20m;

  limit_req_zone $binary_remote_addr zone=perip:10m rate=20r/s;
  limit_conn_zone $binary_remote_addr zone=connperip:10m;

  ssl_protocols TLSv1.2 TLSv1.3;
  ssl_prefer_server_ciphers off;
  add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
}

# CI 校验脚本片段
set -euo pipefail
envsubst < templates/nginx.conf.tpl > build/nginx.conf
nginx -t -c "$PWD/build/nginx.conf"
nginx -T -c "$PWD/build/nginx.conf" | grep -E "server_name|location" > build/nginx.effective.txt

官方参考来源

下方为命令对应的官方权威文档,供你核对最新用法与深入查阅。