include指令仅作文本拼接,不解耦逻辑;解耦靠目录结构、作用域隔离与文件职责收敛;conf.d/只允许server块,upstream需在http{}顶层定义,应单独建upstreams/目录并用绝对路径include。

include 指令本身不提供逻辑解耦能力,它只是文本拼接;真正的解耦靠的是目录结构设计 + 作用域隔离 + 文件职责收敛。
conf.d/ 只能放 server 块,不能放 upstream 或 map
很多人把 upstream 块直接丢进 /etc/nginx/conf.d/ 下的某个 .conf 文件里,结果 nginx -t 报错:upstream directive is not allowed here。这是因为 conf.d/ 被 include 在 http{} 块内,但它的文件内容仍需符合上下文语法——upstream 必须在 http{} 内顶层定义,不能嵌套在 server{} 里,也不能由 server{} 文件“顺带”带进来。
正确做法是:
- 单独建
/etc/nginx/upstreams/目录,里面放backend-api.conf、cache-cluster.conf等,每个只含一个upstream块 - 在
nginx.conf的http{}块顶部(server{}之前)加一行:include /etc/nginx/upstreams/*.conf; - 确保这些文件不包含
server{}、location{}或任何非upstream{}的顶层指令
相对路径的基准不是当前文件,而是 nginx 启动时的 conf 目录
写 include ./snippets/ssl-params.conf; 很常见,但容易失效。因为 . 指向的是 nginx -t 显示的配置根目录(通常是 /etc/nginx),不是你正在编辑的那个 .conf 文件所在位置。比如你在 /etc/nginx/conf.d/site.conf 里写这行,它实际去找的是 /etc/nginx/./snippets/ssl-params.conf,而非 /etc/nginx/conf.d/snippets/ssl-params.conf。
所以更稳妥的方式是:
- 统一用绝对路径:
include /etc/nginx/snippets/ssl-params.conf; - 或用从
/etc/nginx出发的相对路径:include snippets/ssl-params.conf;(注意没有./) - 避免跨级跳转如
../common/,除非你确认部署时权限和路径结构严格一致
通配符加载顺序影响 server_name 匹配结果
include /etc/nginx/conf.d/*.conf; 按字母顺序加载,这意味着 00-default.conf 会比 z-api.conf 先被读入。如果多个 server{} 块监听同一端口且 server_name 有重叠(比如一个写 example.com,另一个写 .example.com),Nginx 会用第一个完全匹配的块——而这个“第一个”取决于文件名排序,不是你主观认为的“主站优先”。
控制加载顺序的实操建议:
- 给文件名加前缀:如
10-main.conf、20-api.conf、99-fallback.conf - 避免使用泛域名
server_name .example.com和精确域名example.com共存于不同文件,除非你明确需要 fallback 行为 - 用
nginx -T | grep "server_name"查看最终拼接后的完整配置,验证顺序是否符合预期
最常被忽略的一点:模块化之后,include 不再是“可有可无的便利”,而是配置生效的前提条件。少写一行 include,整个目录就变成废纸;多 include 一个含语法错误的文件,整个 Nginx 就起不来。它不智能,也不容错——你得亲手对每个路径、每个作用域、每个文件职责画清楚边界。











