Что такое nginx и пять его задач
nginx (читается «энджин-экс») — open-source HTTP-сервер и прокси. Игорь Сысоев написал его, чтобы держать на одной машине десять тысяч одновременных соединений в те времена, когда это укладывало большинство серверов, и выпустил в 2004 году. Сегодня это один из двух самых распространённых веб-серверов наряду с Apache и движок внутри многих балансировщиков нагрузки, Kubernetes ingress-контроллеров и CDN. Open-source версия живёт на nginx.org; F5, купившая компанию в 2019 году, продаёт коммерческую редакцию NGINX Plus.
Имя покрывает пять ролей, и одна конфигурация может сочетать их все:
Веб-сервер. Читает файлы с диска и отдаёт их: HTML, CSS, JavaScript, картинки, файлы для скачивания. Это он делает быстрее всего, передавая копирование ядру через sendfile.
Reverse proxy. Принимает запрос и передаёт его приложению, которое не должно само смотреть в интернет: PHP-FPM, Node.js, Python, Go, Java, контейнер. Полная тема, включая заголовки, которые нужно пробрасывать, — в нашем руководстве про reverse proxy.
Балансировщик нагрузки. Распределяет запросы по нескольким копиям приложения и прекращает отправлять трафик на ту, что упала.
Кэш. Хранит ответы upstream на диске и отвечает на повторные запросы, не спрашивая приложение снова.
Терминатор TLS. Держит сертификаты, говорит с браузером на HTTPS, HTTP/2 и HTTP/3, а с приложением — на обычном HTTP по приватному адресу.
Чем он не является: сервером приложений. nginx не исполняет ваш код на PHP, Python или Ruby. Он передаёт такие запросы процессу, который это делает, и возвращает ответ обратно.
Событийная модель: почему nginx держит так много соединений
Когда nginx стартует, master-процесс читает конфигурацию, открывает слушающие порты и запускает несколько worker-процессов, обычно по одному на ядро CPU (worker_processes auto). Master никогда не обслуживает трафик. Он управляет worker'ами, и именно поэтому reload проходит плавно.
Каждый worker однопоточный и выполняет event loop. Он спрашивает ядро, через epoll на Linux или kqueue на BSD и macOS, у каких его соединений что-то готово: новый запрос, клиент, который может принять ещё байты, upstream, который ответил. Он делает небольшой кусок работы на каждом готовом соединении и идёт дальше; ничто не ждёт впустую. Keep-alive соединение, простаивающее между запросами, или медленный мобильный клиент, присылающий запрос по капле, стоят worker'у пару килобайт памяти и ноль CPU.
Классическая модель Apache — противоположность. MPM prefork выделяет каждому соединению свой процесс, а MPM worker — свой поток, и этот процесс или поток занят всё время, пока длится соединение, занят он работой или простаивает. Десять тысяч открытых keep-alive соединений означают десять тысяч процессов или потоков с соответствующей памятью и переключениями контекста. Более новый MPM event у Apache паркует простаивающие keep-alive соединения на отдельном потоке-слушателе, что сужает разрыв, но каждый активный запрос всё равно держит поток.
Отсюда правило: worker никогда не должен блокироваться. Работа, которая действительно занимает время, например выполнение PHP или запрос к базе данных, происходит в другом процессе, а nginx воспринимает ответ как ещё одно событие. Суммарная ёмкость равна worker_processes × worker_connections, и когда nginx проксирует, каждый клиент занимает два таких слота: один к браузеру, один к upstream.
Верх nginx.conf: один master, worker на ядро, event loop в каждом
user www-data;
worker_processes auto; # one worker per CPU core
error_log /var/log/nginx/error.log warn;
events {
worker_connections 1024; # per worker: clients + upstream connections
}
http {
include mime.types;
sendfile on;
keepalive_timeout 65s;
include /etc/nginx/conf.d/*.conf; # one file per site
}
Для чего используется nginx
На практике nginx занимает одну из этих позиций, часто несколько сразу.
Отдаёт статический сайт или сборку фронтенда. Сборка React, Vue или Astro, документация, лендинг: файлы на диске и nginx перед ними, больше ничего.
Перед PHP. WordPress, Laravel и большинство PHP-приложений работают как nginx плюс PHP-FPM. nginx сам отдаёт картинки, CSS и JavaScript, а запросы к .php передаёт в FPM через Unix-сокет директивой fastcgi_pass.
Перед сервером приложений. Node.js, Python (Gunicorn, Uvicorn), Ruby (Puma) и сервисы на Go слушают локальный порт. nginx терминирует HTTPS, поглощает медленных клиентов, буферизуя запрос, затем передаёт его дальше через proxy_pass, так что приложение видит только быстрые и завершённые запросы.
Балансировка нагрузки по нескольким экземплярам приложения — кратко разобрана ниже.
TLS и современные протоколы для приложения, у которого их нет: сертификаты (большинство получает их через certbot и Let's Encrypt), HTTP/2 через http2 on; и HTTP/3 через listen 443 quic; в nginx 1.25 и новее. Что меняют эти протоколы — в HTTP/2 против HTTP/3.
Правила на входе: редиректы, ограничение частоты запросов через limit_req, списки разрешённых и запрещённых IP, gzip-сжатие и заголовки ответа. Какие заголовки отправлять и почему — в HTTP-заголовках безопасности.
Минимальный server block, строка за строкой
Блок server — это один сайт. nginx выбирает его по порту, на который пришёл запрос, и по заголовку Host, сравниваемому с server_name. Внутри блоки location сопоставляются с путями URL. Блок ниже отдаёт статический сайт и передаёт /api/ приложению на порту 3000.
listen — это порт, по одной строке для IPv4 и для IPv6.
server_name перечисляет имена хостов, на которые отвечает этот блок. Запрос, чей Host не совпал ни с одним блоком, уходит на сервер по умолчанию для этого порта: блок, помеченный default_server, либо первый, который nginx прочитал. Именно поэтому неизвестное имя хоста, указывающее на ваш IP, показывает какой-то другой сайт. Блок-ловушка, отвечающий return 444; (закрыть соединение), останавливает это.
root — где искать файлы: /about.html превращается в /var/www/example/about.html.
try_files пробует файл, затем директорию, затем запасной вариант. Для одностраничного приложения замените =404 на /index.html, чтобы клиентские маршруты загружали приложение.
Сопоставление location устроено так, что это удивляет людей. Точное совпадение (=) выигрывает безусловно. Иначе nginx запоминает самый длинный совпавший префикс, затем пробует регулярные выражения (~, ~*) в порядке их записи в файле и берёт первое совпавшее; только если ни одно не совпало, используется запомненный префикс. Префикс ^~ пропускает шаг с регулярными выражениями. В этом примере /api/logo.png отдаётся регулярным выражением для картинок, а не /api/ — обычный ответ на вопрос «почему мой location игнорируется».
expires задаёт Cache-Control: max-age и Expires для файлов с отпечатком в имени. Выбирайте значения по руководству про Cache-Control.
Этот блок — обычный HTTP. Добавьте сертификат через certbot, который сам отредактирует блок, и перенаправьте порт 80 на 443.
/etc/nginx/conf.d/example.conf: статический сайт с API за ним
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
root /var/www/example;
index index.html;
access_log /var/log/nginx/example.access.log;
error_log /var/log/nginx/example.error.log;
location / {
try_files $uri $uri/ =404;
}
location ~* \.(?:css|js|woff2|png|jpg|webp|avif|svg)$ {
expires 30d;
access_log off;
}
location /api/ {
proxy_pass http://127.0.0.1:3000;
# plus the forwarded headers from the reverse proxy guide
}
}
Установка, проверка, reload: команды на каждый день
Пакеты дистрибутива кладут главный файл в /etc/nginx/nginx.conf. Debian и Ubuntu держат сайты в /etc/nginx/sites-available/ и включают их символической ссылкой в sites-enabled/; RHEL, Rocky, Alpine и пакеты с nginx.org читают /etc/nginx/conf.d/*.conf. Логи лежат в /var/log/nginx/.
Две привычки предотвращают большинство самодельных аварий. Запускайте nginx -t перед каждым reload: он разбирает всю конфигурацию и называет файл и строку любой ошибки. И делайте reload, а не restart. При reload master запускает новые worker'ы с новой конфигурацией и даёт старым закончить их открытые запросы, так что ни одно соединение не обрывается; если новая конфигурация не загрузилась, master продолжает работать со старыми worker'ами и пишет в лог причину.
nginx -T печатает полную конфигурацию так, как её видит nginx, со всеми раскрытыми include. Когда настройка, кажется, не работает, обычно она переопределена в файле, про который вы забыли, и -T показывает, в каком именно.
# Debian / Ubuntu
sudo apt install nginx
nginx -v # version; -V adds build options and modules
# check the syntax, then apply without dropping connections
sudo nginx -t && sudo systemctl reload nginx
# the whole effective configuration, includes expanded
sudo nginx -T | less
# which names and ports are configured
sudo nginx -T | grep -E '^\s*(server_name|listen)'
# watch errors while you test
sudo tail -f /var/log/nginx/error.log
nginx против Apache
Оба зрелые, бесплатные и достаточно быстрые для почти любого сайта. Различия, которые реально влияют на выбор:
Модель соединений. Event loop nginx держит простаивающие и медленные соединения почти бесплатно; Apache привязывает процесс или поток к каждому активному запросу, а за пределами MPM event — и к каждому простаивающему соединению. При большом числе одновременных keep-alive соединений nginx использует намного меньше памяти.
Конфигурация. С включённым AllowOverride Apache читает файлы .htaccess из каждой директории на пути каждого запроса, так что пользователь может менять правила, не трогая конфигурацию сервера. У nginx аналога нет: все правила живут в центральной конфигурации и разбираются один раз, при загрузке. Это быстрее и проще для аудита, но .htaccess из WordPress или Laravel при переезде придётся переписать в правила location и try_files.
PHP. Apache может запускать PHP внутри собственных процессов через mod_php; nginx всегда обращается к отдельному пулу PHP-FPM. В современных установках Apache тоже часто используют PHP-FPM, так что сейчас это скорее разница в настройках по умолчанию.
Модули. Apache загружает модули во время выполнения из очень большого каталога. nginx поддерживает динамические модули, но каждый должен быть собран под точную версию nginx, которую вы используете, так что многие дополнения означают компиляцию.
Статические файлы и проксирование — вот где nginx сильнее всего. Поэтому частая гибридная схема ставит nginx спереди, отдающим файлы и терминирующим TLS, а Apache позади него запускает приложение, зависящее от .htaccess.
Честный итог: начиная с нуля, nginx — выбор по умолчанию для большинства команд. Но замена работающего Apache не сделает медленный сайт быстрым. Медленная часть почти всегда — приложение или расстояние до посетителя, а не веб-сервер.
Reverse proxy, балансировщик и кэш: короткая версия
Проксирование — это одна директива, proxy_pass, плюс заголовки, которые сообщают приложению, кто на самом деле клиент. Эти заголовки, поддержка WebSocket и proxy_cache разобраны в руководстве про reverse proxy, поэтому эта страница их не повторяет.
Балансировка нагрузки добавляет блок upstream, в котором названы серверы. По умолчанию используется round robin. least_conn отправляет каждый запрос серверу с наименьшим числом активных соединений, что подходит для запросов разной длины, а ip_hash или hash закрепляет клиента за одним сервером. Проверка состояния в open-source nginx пассивная: после max_fails неудач в течение fail_timeout сервер пропускается на это время, а proxy_next_upstream повторяет запрос на другом. Активные проверки, опрашивающие URL по расписанию, — функция NGINX Plus. Более широкую картину см. в что делает балансировщик нагрузки.
Кэширование работает хорошо, но есть один пробел, о котором стоит знать сразу: в open-source nginx нет команды, чтобы очистить закэшированный URL. Приходится ждать, пока запись устареет, или удалять файлы из директории кэша на каждом сервере. Этот пробел — одна из самых частых причин, по которым команды переносят публичный кэш на CDN.
Три сервера приложений за одним nginx
upstream app {
least_conn;
server 10.0.0.11:3000 max_fails=3 fail_timeout=10s;
server 10.0.0.12:3000 max_fails=3 fail_timeout=10s;
server 10.0.0.13:3000 backup; # used only when the others are down
keepalive 32; # reuse connections to the app
}
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://app;
proxy_http_version 1.1;
proxy_set_header Connection ""; # required for upstream keepalive
proxy_next_upstream error timeout http_502;
# plus the forwarded headers from the reverse proxy guide
}
}
502, 504 и 413: что вам говорит nginx
Код статуса, который видит посетитель, — это резюме; лог ошибок — это объяснение. Каждая из этих ошибок оставляет в нём характерную строку.
502 Bad Gateway означает, что nginx не получил от upstream пригодного ответа. connect() failed (111: Connection refused) говорит, что по адресу из proxy_pass никто не слушает: приложение упало или работает на другом порту. connect() to unix:/run/php/php8.3-fpm.sock failed (2: No such file or directory) — это путь к сокету PHP-FPM, не совпадающий с установленной версией PHP, а (13: Permission denied) в той же строке означает сокет, который пользователь nginx не может открыть. upstream prematurely closed connection означает, что приложение упало или было убито посреди запроса; читайте его собственный лог и сообщения ядра об исчерпании памяти. upstream sent too big header означает, что заголовки ответа, часто куча cookie, не помещаются в буфер: увеличьте proxy_buffer_size или fastcgi_buffer_size для PHP. Полная диагностика, включая случай с CDN спереди, — в 502 Bad Gateway.
504 Gateway Timeout означает, что upstream принял запрос и не ответил за proxy_read_timeout (или fastcgi_read_timeout), по умолчанию 60 секунд. В логе написано upstream timed out (110: Connection timed out) while reading response header from upstream. Больший таймаут оправдан для известного медленного экспорта или отчёта; для обычных страниц он лишь скрывает медленный запрос или переполненный пул worker'ов. У каждого прокси в цепочке свой лимит, так что балансировщик или CDN спереди может сдаться раньше, чем nginx.
413 Request Entity Too Large означает, что тело запроса больше client_max_body_size, который по умолчанию равен 1 МБ: классическая неудачная загрузка картинки или бэкапа. В логе написано client intended to send too large body. Поднимайте лимит в server или location, принимающем загрузки, а не глобально, а для PHP заодно поднимите upload_max_filesize и post_max_size, иначе PHP отвергнет то, что nginx уже пропустил.
503 от nginx обычно означает, что его собственный limit_req или limit_conn отклонил запрос; это и другие причины разобраны в 503 Service Unavailable. А directory index of "/var/www/..." is forbidden — это 403 для директории без индексного файла, разобрано в 403 Forbidden.
Строки лога ошибок и директива, на которую указывает каждая
# /var/log/nginx/error.log (abridged)
connect() failed (111: Connection refused) while connecting to upstream -> 502
upstream prematurely closed connection while reading response header -> 502
upstream sent too big header while reading response header from upstream -> 502
upstream timed out (110: Connection timed out) while reading response header -> 504
client intended to send too large body: 52428800 bytes -> 413
# the matching fixes, in the server or location that needs them
client_max_body_size 64m; # PHP: raise upload_max_filesize and post_max_size too
proxy_read_timeout 300s; # only for a known slow endpoint
proxy_buffer_size 16k; # large response headers and cookies
proxy_buffers 8 16k;
Когда ставить CDN перед nginx
Один nginx обслуживает много трафика. Чего он не может изменить — это своё местоположение. Посетитель из другой страны ждёт на каждом обмене пакетами с вашим единственным дата-центром; всплеск трафика, по большей части состоящий из повторных запросов, всё равно падает на вашу единственную машину; атака, направленная на ваш IP, достигает сервера, на котором работает ваш сайт. Это те моменты, когда стоит добавить CDN: аудитория далеко от сервера, кэшируемый трафик составляет большую долю нагрузки, нужна очистка кэша сразу на нескольких машинах, или origin-сервер должен прекратить быть напрямую доступным.
Вы сохраняете nginx; меняется его роль. CDN становится публичной входной дверью, а nginx становится origin-сервером, отдающим файлы и маршрутизирующим к приложению, пока edge занимается расстоянием, кэшированием и фильтрацией. Три вещи нужно подправить на стороне nginx. Отправляйте корректные заголовки Cache-Control, потому что edge следует им. Восстановите IP посетителя модулем realip (set_real_ip_from для адресов CDN, real_ip_header для заголовка, который он присылает), иначе каждая строка лога и каждая зона limit_req будет видеть edge вместо посетителя. И разрешите доступ к origin-серверу только с CDN, чтобы атаки не могли его обойти.
Edge CDN.com.tr сам работает на nginx: его конфигурация применяет размер файла из вашего тарифа как client_max_body_size, так что загрузке, идущей через CDN, этот лимит тоже нужен достаточно большим, как и ваш собственный. Перед вашим origin-сервером сеть edge-серверов, с машинами в Турции и за рубежом, кэширует по правилам доставки для каждого пути и очищает кэш мгновенно по точному пути или папке из панели, командной строки cdnctl или REST API. WAF, защита от DDoS, защита от ботов с JavaScript-проверкой, правила по странам и IP и ограничение частоты запросов работают до того, как запрос достигнет вашего сервера, а сертификаты Let's Encrypt выпускаются и продлеваются для каждого подключённого домена.
Две детали облегчают проверку переключения. Страницы, уже находящиеся в кэше edge, продолжают отдаваться, пока ваш origin-сервер лежит, а заголовок ответа X-Proxy-Cache-MT показывает HIT или MISS, так что можно понять, добрался ли запрос до вашего nginx вообще. Edge также отправляет origin-серверу SNI, так что nginx с несколькими HTTPS-блоками server предъявляет правильный сертификат.
Частые вопросы про nginx
Как произносится nginx?
«Энджин-экс». Используются оба варианта написания, nginx и NGINX; написание с маленькой буквы — имя open-source проекта и его бинарника.
nginx бесплатный?
Open-source nginx с nginx.org бесплатен по лицензии BSD с двумя условиями, в том числе для коммерческого использования. NGINX Plus — платная подписка от F5, добавляющая активные проверки состояния, API для очистки кэша и изменения upstream во время работы, живой дашборд состояния и поддержку.
nginx — это веб-сервер или reverse proxy?
И то, и другое, часто в одной конфигурации. location с root отдаёт файлы с диска, а с proxy_pass или fastcgi_pass передаёт запрос приложению. Большинство сайтов делают и то, и другое: статику напрямую, всё динамическое — через прокси.
nginx лучше, чем Apache?
Для статических файлов, проксирования и большого числа одновременных соединений он использует меньше памяти и обычно является выбором по умолчанию для новых проектов. Apache подходит лучше, если вы зависите от .htaccess или модуля, который есть только у Apache. Для типичного сайта узким местом не является ни один из веб-серверов — это приложение и расстояние до посетителей.
Почему мой домен показывает другой сайт на nginx?
Ни один server_name не совпал с заголовком Host запроса, так что nginx использовал сервер по умолчанию для этого порта: блок, помеченный default_server, либо первый загруженный. Проверьте имена командой nginx -T | grep server_name и добавьте блок-ловушку, возвращающий 444, чтобы неизвестные имена хостов не получали ничего.
Нужен ли мне nginx, если я использую CDN?
Обычно да, в роли origin-сервера: что-то всё равно должно отдавать файлы и маршрутизировать запросы к вашему приложению, а CDN забирает данные у него. В контейнерных приложениях CDN.com.tr можно обойтись без него для публикации, потому что edge обращается к каждому приложению через внутренний маршрут платформы без контейнера-reverse-proxy посередине.