Структура конфигурационных файлов Nginx

8 мин чтения·Matthieu|

Полный разбор организации конфигурационных файлов Nginx на диске: как контексты вложены друг в друга, как работают директивы include, и как на самом деле устроено наследование директив.

Структура конфигурационных файлов Nginx

Ты установил Nginx. Открыл /etc/nginx/nginx.conf и увидел файл, который подключает другие файлы, которые подключают ещё файлы. Некоторые директивы стоят внутри фигурных скобок, другие висят на верхнем уровне. Прежде чем настраивать server-блоки, SSL или reverse proxy, нужна карта.

Эта статья даст тебе эту карту. Никаких рецептов. Только ментальная модель того, как устроена конфигурация 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 использует дерево вложенных контекстов (contexts). Каждый контекст — это блочная директива, обёрнутая в фигурные скобки. Директивы внутри контекста действуют только в его рамках.

Иерархия выглядит так:

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

Каждая директива в конфиге живёт на одном из этих уровней. Контекст main — это сам файл. Всё остальное вкладывается внутрь него.

Что контролирует контекст main?

Контекст main — это всё, что находится за пределами любых блочных директив в 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

Вот контекст main из стандартного nginx.conf на Debian 12:

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

Эти пять строк выполняются первыми. Они задают, от какого пользователя работает Nginx, сколько воркеров запускается, и куда пишутся ошибки.

Что содержит блок events?

Блок events настраивает, как Nginx обрабатывает соединения на уровне ОС. Он находится внутри контекста main.

events {
    worker_connections 768;
    # multi_accept on;
}

worker_connections задаёт максимальное число одновременных соединений на один worker-процесс. С worker_processes auto на 4-ядерной машине получается 4 x 768 = 3072 параллельных соединений. В исходниках Nginx значение по умолчанию — 512, но Debian ставит 768.

Для чего нужен контекст http?

Блок http содержит всю конфигурацию, связанную с обработкой HTTP-трафика. Каждый блок server живёт внутри него. Директивы, размещённые здесь, применяются ко всем виртуальным хостам, если их не переопределит блок server или location.

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. Каждый блок 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, сопоставляя server_name с заголовком Host. Если ни один блок не подходит, Nginx использует default_server:

listen 80 default_server;

Обычно для каждого домена создаётся отдельный файл в sites-available/, содержащий один блок server (или два, если есть и HTTP, и HTTPS).

Как Nginx сопоставляет блоки location?

Блок location определяет, как Nginx обрабатывает запросы для конкретных URI-паттернов. Он находится внутри блока server (или внутри другого location).

Nginx проверяет блоки location в определённом порядке:

Модификатор Тип Пример Поведение
= Точное совпадение location = / Совпадает только с /. Поиск прекращается сразу.
^~ Приоритетный префикс location ^~ /images/ Совпадение по префиксу. Regex-проверки пропускаются.
~ Regex (с учётом регистра) location ~ \.php$ Побеждает первое совпадение regex.
~* Regex (без учёта регистра) location ~* \.(jpg|png)$ Побеждает первое совпадение regex.
(нет) Префикс location /api/ Побеждает самый длинный префикс, но regex может переопределить.

Алгоритм сопоставления:

  1. Nginx проверяет все префиксные location и запоминает самое длинное совпадение.
  2. Если это совпадение использует = или ^~, поиск останавливается. Используется оно.
  3. Иначе проверяются regex-location в порядке их появления в конфиге.
  4. Побеждает первый совпавший regex. Если ни один regex не совпал, используется самый длинный префикс из шага 1.

Это значит, что порядок префиксных location в конфиге не важен. А вот порядок regex — важен.

Как работает директива include?

Директива include вставляет содержимое другого файла (или файлов, подходящих под glob-паттерн) в текущую позицию конфига. Nginx разрешает её на этапе загрузки конфига, до того как конфиг будет распарсен целиком.

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

Glob *.conf находит все файлы с расширением .conf в директории. Glob * находит всё. Оба паттерна используются часто.

Директива include работает в любом контексте. Её можно использовать внутри блоков http, server или location:

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

Именно так работает директория snippets/. Ты пишешь переиспользуемый фрагмент один раз и подключаешь его, где нужно.

Один нюанс: Nginx не запустится, если include указывает на несуществующий файл без glob-паттерна. Если написать 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 передаёт директивы из родительских контекстов в дочерние. Это та часть, в которой большинство людей ошибаются, и источник неочевидных багов в продакшене.

Есть три типа директив, и каждый наследуется по-своему.

Обычные директивы

Обычные директивы (normal directives) хранят одно значение. Если дочерний контекст определяет ту же директиву, он полностью заменяет значение родителя. Если не определяет — наследуется родительское значение.

Пример: 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
        }
    }
}

Другие обычные директивы: index, access_log (при однократном использовании), error_log, client_max_body_size.

Массивные директивы

Массивные директивы (array directives) могут встречаться несколько раз в одном контексте, накапливая значения. Но когда дочерний контекст определяет хотя бы одну такую директиву, он заменяет все родительские значения. Не объединяет. Заменяет.

add_header — самая распространённая массивная директива. Это поведение вызывает известный продакшен-баг.

Что происходит при добавлении заголовков и в http, и в location?

Если определить add_header в контексте http, а затем другой add_header в блоке location, блок location уничтожит все заголовки из контекста http. Не только тот, который ты переопределяешь. Все.

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/

Проверь заголовки ответа. Если заголовки безопасности отсутствуют в ответах /api/, но есть на других путях — вот почему.

Исправление: повтори все заголовки в дочернем контексте.

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

Или используй сниппет:

# /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 дочерние контексты дополняют родительские заголовки, а не заменяют их. Если ты используешь Nginx 1.28.x (текущая стабильная версия на март 2026), этой возможности у тебя ещё нет.

То же поведение с затиранием относится и к proxy_set_header. Если определить любой proxy_set_header в блоке location, все директивы proxy_set_header из контекста server или http будут потеряны. Официальная документация говорит об этом явно: "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 или используй include-сниппет.

Директивы действий

Директивы действий (action directives), такие как rewrite и return, не наследуются во вложенные контексты. Они выполняются только в том контексте, где определены.

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 Nginx создаёт неявный псевдо-location с наименьшим приоритетом. Если любой обычный блок location совпадёт с запросом, try_files на уровне server никогда не выполнится:

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 во всех подключённых файлах с номерами строк в объединённом выводе. Используй это при отладке проблем с наследованием. Если заголовок присутствует в блоке http, но не в блоке location, и ты не видишь add_header в этом location — заголовок наследуется. Если видишь другой add_header в location — все родительские заголовки затёрты.

Используй 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. Vse prava zashchishcheny.

Готовы попробовать?

Разверните свой сервер за секунды. Linux, Windows или FreeBSD.

Смотреть тарифы VPS