Nginx配置文件结构详解

2 分钟阅读·Matthieu|

全面解析Nginx配置文件的磁盘组织方式、context的嵌套关系、include指令的文件引入机制,以及指令继承的实际工作原理。

Nginx配置文件结构详解

你已经安装好了Nginx。打开/etc/nginx/nginx.conf,发现这个文件include了其他文件,那些文件又include了更多文件。有些指令在花括号里面,有些漂浮在最顶层。在你开始配置server block、SSL或反向代理之前,你需要一张地图。

本文就是那张地图。没有配置模板,只有Nginx配置工作原理的思维模型,让你能读懂和修改遇到的任何配置。

Nginx配置文件存放在哪里?

在Debian 12和Ubuntu 24.04上,apt包将所有文件安装在/etc/nginx/目录下。以下是全新安装后该目录的样子:

/etc/nginx/
├── nginx.conf              # Main config entry point
├── mime.types              # Maps file extensions to MIME types
├── conf.d/                 # Drop-in config files (*.conf auto-included)
├── sites-available/        # All virtual host config files
│   └── default             # Default server block
├── sites-enabled/          # Symlinks to active virtual hosts
│   └── default -> ../sites-available/default
├── snippets/               # Reusable config fragments
│   └── fastcgi-php.conf
├── modules-available/      # Available dynamic module configs
├── modules-enabled/        # Symlinks to loaded modules
├── fastcgi.conf            # FastCGI directive defaults
├── fastcgi_params          # FastCGI parameter mappings
├── proxy_params            # Proxy header defaults
├── scgi_params             # SCGI parameter mappings
├── uwsgi_params            # uWSGI parameter mappings
├── koi-utf                 # Character set mapping files
├── koi-win
└── win-utf

你可以在自己的服务器上验证:

ls -la /etc/nginx/

你最常编辑的文件不是nginx.conf本身,通常是sites-available/conf.d/里面的文件。主配置文件nginx.conf设置全局默认值,并通过include指令引入那些文件。

Nginx配置文件是如何组织的?

Nginx配置使用嵌套的context(上下文)树结构。每个context是一个用花括号包裹的块指令。放在context内部的指令只在该作用域内生效。

层级结构如下:

main (top level, outside any braces)
├── events { }
└── http { }
    └── server { }
        └── location { }

配置中的每条指令都位于这些层级之一。main context就是文件本身,其他所有内容都嵌套在里面。

main context控制什么?

main context是nginx.conf中所有块指令之外的部分。它控制影响整个Nginx实例的进程级设置。

该层级的关键指令:

指令 用途 典型值
user worker进程运行的系统用户 www-data
worker_processes worker进程数量 auto(匹配CPU核心数)
pid PID文件路径 /run/nginx.pid
error_log 全局错误日志路径和级别 /var/log/nginx/error.log
include 加载模块配置 /etc/nginx/modules-enabled/*.conf

以下是Debian 12默认nginx.conf的main context部分:

user www-data;
worker_processes auto;
pid /run/nginx.pid;
error_log /var/log/nginx/error.log;
include /etc/nginx/modules-enabled/*.conf;

这五行在其他所有内容之前运行。它们设置Nginx以什么用户运行、启动多少个worker进程、以及将错误写入哪里。

events块包含什么?

events块配置Nginx在操作系统层面如何处理连接。它位于main context内部。

events {
    worker_connections 768;
    # multi_accept on;
}

worker_connections设置每个worker进程的最大并发连接数。在一台4核机器上使用worker_processes auto时,你可以获得4 x 768 = 3,072个并发连接。Nginx源码的默认值是512,但Debian将其设置为768。

http context的作用是什么?

http块包含所有与处理HTTP流量相关的配置。每个server块都在它内部。放在这里的指令会应用到所有虚拟主机,除非被serverlocation块覆盖。

http {
    sendfile on;
    tcp_nopush on;
    types_hash_max_size 2048;
    server_tokens off;

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

    access_log /var/log/nginx/access.log;

    gzip on;

    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

注意顶部附近的server_tokens off;。这会在响应头中隐藏Nginx版本号。版本信息泄露会帮助攻击者针对已知漏洞发起攻击。Debian的默认配置不包含这一行,建议添加。

底部的两行include是Nginx引入实际站点配置的方式。下面会详细介绍include

server块如何工作?

server块定义一个虚拟主机。它位于http context内部。每个server块监听一个地址/端口组合,并通过Host头匹配请求。

server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;

    root /var/www/example.com/html;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }
}

当请求到达时,Nginx通过将server_nameHost头进行匹配来选择server块。如果没有块匹配,Nginx使用default_server

listen 80 default_server;

通常你会在sites-available/中为每个域名创建一个文件,每个文件包含一个server块(如果同时有HTTP和HTTPS,则为两个)。

Nginx如何匹配location块?

location块定义Nginx如何处理特定URI模式的请求。它位于server块内部(或另一个location内部)。

Nginx按照特定顺序评估location块:

修饰符 类型 示例 行为
= 精确匹配 location = / 仅匹配/。立即停止搜索。
^~ 优先前缀 location ^~ /images/ 匹配前缀后跳过正则检查。
~ 正则(区分大小写) location ~ \.php$ 第一个匹配的正则生效。
~* 正则(不区分大小写) location ~* \.(jpg|png)$ 第一个匹配的正则生效。
(无) 前缀 location /api/ 最长前缀匹配,但正则可以覆盖。

匹配算法:

  1. Nginx检查所有前缀location,记住最长匹配。
  2. 如果该匹配使用了=^~,停止,直接使用。
  3. 否则,按配置文件中出现的顺序检查正则location。
  4. 第一个匹配的正则生效。如果没有正则匹配,使用第1步的最长前缀。

这意味着配置文件中前缀location的顺序不重要,但正则的顺序很重要。

include指令如何工作?

include指令将另一个文件(或匹配glob模式的多个文件)的内容插入到配置的当前位置。Nginx在加载配置时解析它,在整体解析配置之前完成。

include /etc/nginx/mime.types;
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;

glob模式*.conf匹配该目录中所有以.conf结尾的文件。*匹配所有文件。两种模式都很常见。

include指令在任何context中都可以使用。你可以在httpserverlocation块中使用它:

server {
    listen 443 ssl;
    include snippets/ssl-params.conf;
}

这就是snippets/目录的工作方式。你编写一个可复用的配置片段,然后在需要的地方include它。

需要注意一点:如果include路径不是glob且匹配不到文件,Nginx会启动失败。如果你写include /etc/nginx/conf.d/*.conf;而该目录为空,Nginx可以正常启动(glob允许匹配零个文件)。但include /etc/nginx/ssl.conf;在文件不存在时会失败。

sites-available、sites-enabled和conf.d有什么区别?

这三个目录有不同的用途。Debian和Ubuntu三个都使用。

目录 用途 Nginx如何读取 使用场景
sites-available/ 存储所有虚拟主机配置 不直接读取 始终使用。每个域名一个文件。
sites-enabled/ 包含指向活跃配置的符号链接 通过include /etc/nginx/sites-enabled/*;包含 创建符号链接以激活站点。
conf.d/ 即插即用的配置片段 通过include /etc/nginx/conf.d/*.conf;包含 全局http级设置、upstream块、map。

sites-available/sites-enabled模式让你可以在不删除配置的情况下禁用站点。启用站点:

ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/

禁用站点:

rm /etc/nginx/sites-enabled/example.com

然后重新加载:

sudo nginx -t && sudo systemctl reload nginx

重新加载之前务必运行nginx -t。它会验证配置而不影响正在运行的流量。

conf.d/目录更简单。其中每个.conf文件都会被自动加载,不需要符号链接。一些管理员偏好conf.d/的简洁性,完全跳过sites-available/sites-enabled。两种方式都可以。选择一种并保持一致。

一个陷阱:如果你同时使用conf.d/sites-enabled/,确保不要在两个地方定义冲突的server块。Nginx会加载两者,结果取决于指令合并规则和server_name匹配。

Nginx指令继承如何工作?

Nginx将指令从父context向下传递到子context。这是大多数人容易搞错的部分,也是生产环境隐蔽bug的根源。

指令分为三种类型,每种的继承方式不同。

普通指令

普通指令持有单个值。如果子context定义了相同的指令,它会完全替换父级的值。如果子context没有定义,则继承父级的值。

例如:root是一个普通指令。

http {
    root /var/www/default;

    server {
        server_name example.com;
        # root is inherited: /var/www/default

        location /app {
            root /var/www/app;
            # root is overridden: /var/www/app
        }

        location /blog {
            # root is inherited: /var/www/default
        }
    }
}

其他普通指令:indexaccess_log(单次使用时)、error_logclient_max_body_size

数组指令

数组指令可以在同一个context中多次出现以累积值。但当子context定义了哪怕一个实例时,它会替换所有父级的值。不是合并,是替换。

add_header是最常见的数组指令。这种行为会导致一个广为人知的生产环境bug。

在http和location块中同时添加header会发生什么?

如果你在http context中定义了add_header,然后在location块中定义了另一个add_header,location块会清除http context中的所有header。不只是你要覆盖的那个,是全部。

http {
    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-Content-Type-Options "nosniff";

    server {
        server_name example.com;

        location /api/ {
            add_header X-Custom "api-response";
            # X-Frame-Options is GONE
            # X-Content-Type-Options is GONE
            # Only X-Custom is sent
        }
    }
}

在你的服务器上验证这一点:

curl -I https://example.com/api/

检查响应头。如果你的安全header在/api/的响应中缺失,但在其他路径上存在,原因就在这里。

修复方法:在子context中重复所有header。

location /api/ {
    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-Content-Type-Options "nosniff";
    add_header X-Custom "api-response";
}

或者使用snippet:

# /etc/nginx/snippets/security-headers.conf
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Strict-Transport-Security "max-age=63072000" always;
location /api/ {
    include snippets/security-headers.conf;
    add_header X-Custom "api-response";
}

注意: Nginx 1.29.3+引入了add_header_inherit merge;,改变了这一行为。使用merge时,子context会将header追加到父级header而非替换。如果你运行的是Nginx 1.28.x(截至2026年3月的当前稳定版),还无法使用此功能。

同样的清除行为适用于proxy_set_header。如果你在location块中定义了任何proxy_set_headerserverhttp context中的所有proxy_set_header指令都会丢失。官方文档明确说明:"These directives are inherited from the previous configuration level if and only if there are no proxy_set_header directives defined on the current level."

server {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    location /api/ {
        proxy_set_header X-Request-ID $request_id;
        # Host, X-Real-IP, X-Forwarded-For are all GONE
        proxy_pass http://backend;
    }
}

修复方式相同:在location块中重复所有header,或使用include snippet。

动作指令

动作指令如rewritereturn不会继承到嵌套的context中。它们只在定义所在的context中执行。

server {
    rewrite ^/old/(.*)$ /new/$1 permanent;

    location /app {
        # The rewrite above does NOT apply here
        # Requests matching /app are handled by this location
    }
}

try_files指令有一个相关的陷阱。当放在server context中时,Nginx会创建一个优先级最低的隐式伪location。如果任何常规location块匹配了请求,server级别的try_files永远不会运行:

server {
    try_files $uri /index.php;   # Never runs for /app/* requests
    location /app { }            # This catches them first
}

始终将try_files放在特定的location块内部。

如何检查Nginx的有效配置?

两个命令帮助你了解Nginx在解析所有include指令后实际看到的配置。

测试配置是否有语法错误:

sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

重新加载之前务必运行此命令。如果测试失败,Nginx会告诉你文件名和行号。

导出完整的合并配置:

sudo nginx -T

这会输出所有include解析和内联展开后的完整有效配置。通过管道传给less以便阅读:

sudo nginx -T | less

或搜索特定指令:

sudo nginx -T | grep -n "add_header"

这会显示所有包含文件中的每个add_header指令及其在合并输出中的行号。调试继承问题时使用它。如果一个header出现在http块中但没有出现在location块中,而你在该location中没有看到add_header,那么header是被继承的。如果你在location中看到了不同的add_header,所有父级header就被清除了。

当配置修改的表现与预期不符时,使用nginx -T

速查参考:完整图示

/etc/nginx/nginx.conf
│
├─ main context (process-level: user, worker_processes, pid)
│
├─ events { }  (connection handling: worker_connections)
│
└─ http { }    (all HTTP config)
   │
   ├─ include conf.d/*.conf      (global HTTP settings, upstreams, maps)
   ├─ include sites-enabled/*    (virtual host configs)
   │
   └─ server { }                 (one per domain/port)
      │
      ├─ listen, server_name     (routing)
      ├─ root, index             (defaults for this host)
      │
      └─ location { }           (URI pattern matching)
         ├─ try_files, root      (per-path behavior)
         └─ proxy_pass           (reverse proxy target)

Inheritance: parent -> child (down only)
Normal directives: child overrides parent
Array directives: child REPLACES ALL parent values
Action directives: no inheritance

Copyright 2026 Virtua.Cloud. All rights reserved.

准备好亲自尝试了吗?

几秒内部署您自己的服务器。支持 Linux、Windows 或 FreeBSD。

查看 VPS 方案