Apache ты уже знаешь по прошлому уроку. Теперь возьмём второй столп веб-стека - Nginx. Задача администратора тут двоякая: раздать статику быстро и дёшево, а главное - поставить Nginx фронтом перед бэкендами (PHP-FPM, Node, Python, тем же Apache) как обратный прокси, балансировщик и точку терминации TLS. Разберём архитектуру, блоки server и location, proxy_pass, upstream и сравним с Apache, чтобы ты понимал, когда какой инструмент брать.

Как это работает
Главное отличие от классического Apache (prefork/worker) - модель обработки соединений. Apache по умолчанию выделяет на запрос поток или процесс, и тысяча медленных клиентов съедает тысячу потоков. Nginx работает событийно: небольшое число рабочих процессов (обычно по одному на ядро) внутри цикла epoll обслуживают тысячи соединений неблокирующе. Один воркер не ждёт медленного клиента, а переключается на готовые сокеты. Отсюда крошечное потребление памяти на простаивающее keepalive-соединение и устойчивость под нагрузкой со множеством коннектов.
Конфиг устроен как дерево директив в контекстах. Корень - nginx.conf, в нём контекст http, внутри - блоки server (виртуальные хосты), а внутри server - блоки location (правила по URI). Запрос проходит две фазы выбора. Сначала по паре слушающего адреса/порта и заголовку Host подбирается нужный server (директивы listen и server_name). Если совпадения по server_name нет - берётся default_server для этого listen. Затем внутри server по пути URI выбирается location.
Порядок выбора location не сверху вниз, а по приоритету префиксов: сначала точное совпадение (= /path), потом самый длинный префикс с ^~ (стоп без regex), затем регулярки (~ с учётом регистра, ~* без) в порядке их записи, и в конце - обычный самый длинный префикс. Это частый источник путаницы, поэтому держи правило в голове.
Reverse proxy - сердце темы. Директива proxy_pass перенаправляет запрос на бэкенд. Nginx принимает клиентское соединение, сам открывает соединение к апстриму, отдаёт ответ обратно. Заодно он терминирует TLS (расшифровывает HTTPS на себе, к бэкенду идёт обычный HTTP), кеширует, сжимает, раздаёт статику напрямую, а динамику отдаёт приложению. Блок upstream описывает пул бэкендов и метод балансировки: round-robin по умолчанию, least_conn по числу активных соединений, ip_hash для липкой сессии по IP клиента.
Команды и примеры
Установка и расположение конфигов различаются по семействам.
Код: Выделить всё
# Debian 13 / Ubuntu 24.04
sudo apt update && sudo apt install nginx
# конфиги: /etc/nginx/nginx.conf, сайты в /etc/nginx/sites-available + симлинки в sites-enabled
# RHEL 10 / Fedora 41+
sudo dnf install nginx
# конфиги: /etc/nginx/nginx.conf, дропины в /etc/nginx/conf.d/*.conf (схемы sites-* нет)
sudo systemctl enable --now nginx
nginx -t # проверить синтаксис ДО перезагрузки
sudo systemctl reload nginx # мягко перечитать конфиг без обрыва соединенийКод: Выделить всё
server {
listen 80;
listen [::]:80;
server_name shop.example.ru;
root /var/www/shop;
index index.html;
location / {
try_files $uri $uri/ =404;
}
location /static/ {
expires 30d; # заголовки кеширования для браузера
access_log off;
}
}Код: Выделить всё
upstream backend {
least_conn;
server 10.0.0.11:8080 weight=3;
server 10.0.0.12:8080;
server 10.0.0.13:8080 backup; # включится, когда основные лягут
}
server {
listen 443 ssl;
http2 on; # в Nginx 1.25+ http2 отдельной директивой
server_name app.example.ru;
ssl_certificate /etc/letsencrypt/live/app.example.ru/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app.example.ru/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
server {
listen 80;
server_name app.example.ru;
return 301 https://$host$request_uri; # редирект на HTTPS
}Код: Выделить всё
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}- Слеш в proxy_pass меняет поведение. http://backend (без пути) шлёт URI как есть; http://backend/ (со слешем) отрезает префикс location. Перепутал - получил 404 на бэкенде.
- Забыл proxy_set_header Host - бэкенд видит Host: backend и отдаёт не тот виртуальный хост. X-Forwarded-For нужен, чтобы приложение знало реальный IP клиента, а не IP прокси.
- Правишь конфиг и делаешь restart вместо reload. reload не рвёт активные соединения, restart рвёт. И всегда nginx -t перед перезагрузкой.
- В Debian создал файл в sites-available, но забыл симлинк в sites-enabled - сайт не подхватился. В RHEL этой схемы нет, кладёшь сразу в conf.d.
- Регулярочный location перехватил запрос раньше префиксного, потому что забыл про приоритеты. Для статики, которую нельзя отдать в regex, используй ^~.
- SELinux на RHEL по умолчанию запрещает Nginx ходить по сети к апстриму - reverse proxy выдаёт 502. Лечится setsebool -P httpd_can_network_connect 1.
- TLS терминируется на Nginx, но приложение генерирует http:// ссылки, потому что не передан X-Forwarded-Proto - получаешь mixed content и циклы редиректов.
- Поставь Nginx на свой стенд и подними простой бэкенд: два контейнера или два процесса, отдающих разный текст на портах 8081 и 8082.
- Опиши блок upstream с этими двумя серверами и методом least_conn, проксируй на него корневой location.
- Проверь балансировку: curl в цикле 10 раз и убедись, что ответы чередуются между бэкендами.
- Добавь второй server для раздачи статики из /var/www с заголовком expires 7d, проверь заголовок Cache-Control в curl -I.
- Выпусти самоподписанный сертификат (openssl req -x509), включи listen 443 ssl и редирект с 80 на 443.
- Погаси один бэкенд и убедись, что Nginx сам перестал слать на него запросы, а сайт продолжает работать.
- Каждое изменение проверяй через nginx -t и применяй через systemctl reload, а не restart.
- Чем событийная модель Nginx принципиально отличается от prefork-модели Apache и как это влияет на потребление памяти под нагрузкой?
- В каком порядке Nginx выбирает блок location при совпадении нескольких правил (точное, ^~, regex, префикс)?
- Как наличие или отсутствие завершающего слеша в proxy_pass меняет итоговый URI на бэкенде?
- Какие proxy_set_header обязательны при reverse proxy и зачем нужен каждый из них?
- Чем отличаются методы балансировки round-robin, least_conn и ip_hash и когда выбирать каждый?
- Что такое TLS-терминация на прокси и какой заголовок нужно передать бэкенду, чтобы он знал об исходном HTTPS?