Структура конфигурационных файлов Nginx
Полный разбор организации конфигурационных файлов 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 может переопределить. |
Алгоритм сопоставления:
- Nginx проверяет все префиксные location и запоминает самое длинное совпадение.
- Если это совпадение использует
=или^~, поиск останавливается. Используется оно. - Иначе проверяются regex-location в порядке их появления в конфиге.
- Побеждает первый совпавший 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