Nginx — веб-сервер и reverse proxy

Nginx (произносится «engine-x») — высокопроизводительный веб-сервер, который обслуживает более трети сайтов в интернете. В этом уроке ты научишься устанавливать Nginx, настраивать виртуальные хосты, раздавать статику, проксировать приложения и включать HTTPS.

Уровень: средний 18 разделов ⏱ 2–3 часа

1. Что такое веб-сервер и зачем Nginx

Веб-сервер — программа, которая принимает HTTP-запросы от браузеров и отвечает файлами или результатами работы приложения. Nginx — один из самых популярных веб-серверов, но его главная особенность в том, что он также отлично работает как reverse proxy: принимает запросы снаружи и передаёт их внутренним приложениям.

Проверить, какой сервер отдаёт сайт, можно через HTTP-заголовок Server:

curl -I https://example.com
# HTTP/2 200
# server: nginx
# date: Sat, 19 Sep 2026 10:00:00 GMT

Классическую альтернативу — Apache — Nginx чаще всего превосходит по производительности на статике благодаря событийной модели обработки соединений.

ХарактеристикаNginxApache
Модель обработкисобытийная, один процесспроцесс на соединение
Статические файлыочень быстробыстро
Гибкие .htaccessнетда
Reverse proxy «из коробки»датребует модулей
Nginx не читает файлы .htaccess как Apache — вся конфигурация живёт в файлах конфигурации сервера.

2. Установка Nginx

Проще всего установить Nginx из репозитория дистрибутива. На Ubuntu и Debian:

sudo apt update
sudo apt install -y nginx
sudo systemctl enable --now nginx
sudo systemctl status nginx

Проверь, что сервер отвечает:

curl http://localhost
# приветственная HTML-страница Nginx

nginx -v
# nginx version: nginx/1.24.0
На Windows можно запустить Nginx из официального zip-архива: start nginx запускает сервер, nginx -s stop останавливает. Однако для практики надёжнее использовать Docker: docker run -d -p 80:80 nginx.
Ключевые команды для каждой смены конфигурации — nginx -t (проверка синтаксиса) и nginx -s reload (перезагрузка без остановки). Забудь про них — и одна опечатка в конфиге уронит весь сайт.

3. Директивы server и location

Рабочая конфигурация Nginx собирается из блоков. Главный файл — /etc/nginx/nginx.conf, а отдельные сайты обычно лежат в /etc/nginx/sites-available и подключаются симлинками из /etc/nginx/sites-enabled.

Минимальный server-блок описывает один домен:

server {
    listen 80;
    server_name example.com www.example.com;

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

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

Каждый location обрабатывает запросы по пути: location /images/ — все пути, начинающиеся с /images/.

ДирективаЧто задаёт
listenпорт и адрес: 80, 443 ssl, 127.0.0.1:8080
server_nameдомен или шаблон: *.example.com
rootбазовая директория для файлов
indexфайл, отдаваемый для каталога: index.html
locationправило маршрутизации по URI
Директива try_files $uri $uri/ =404 сначала ищет точный файл, затем каталог, а если ничего нет — отдаёт ошибку 404.

4. Статическая раздача файлов

Nginx отлично справляется с раздачей картинок, CSS, JS и других статических файлов без участия бэкенда. Для сайта-одностраничника достаточно одного server-блока.

server {
    listen 80;
    server_name static.example.com;

    root /srv/www/static.example.com;

    location /assets/ {
        expires 30d;
        add_header Cache-Control "public";
    }

    location / {
        try_files $uri $uri/ /index.html;
    }
}

Директива expires 30d добавляет браузеру заголовки кэширования: повторные заходы на страницу станут заметно быстрее.

Не путай root и alias. root подставляет путь к корню в начало URI, а alias заменяет совпавшую часть location целиком.
ДирективаURI /assets/img/logo.png
root /srv/www;файл /srv/www/assets/img/logo.png
location /assets/ { alias /srv/files/; }файл /srv/files/img/logo.png
Для сборок фронтенда (Vite, Webpack) достаточно раздать папку dist/ как статику — бэкенд при этом не нужен до появления форм и API.

5. Виртуальные хосты

На одном сервере Nginx может обслуживать много доменов. Каждый домен — отдельный server-блок в отдельном файле, а выбор происходит по server_name.

Создай файл /etc/nginx/sites-available/shop.conf:

server {
    listen 80;
    server_name shop.example.com;
    root /var/www/shop/public;
    index index.php index.html;
}

Затем включи хост симлинком и перезагрузи конфигурацию:

sudo ln -s /etc/nginx/sites-available/shop.conf /etc/nginx/sites-enabled/shop.conf
sudo rm /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl reload nginx
Конфигурации из sites-enabled подключаются в главный файл директивой include. Если в DNS домен ещё не указывает на сервер, проверяй локально через curl -H "Host: shop.example.com" http://localhost.
Симлинк без -s создаст копию, и правки исходного файла не будут подхватываться. Бесплатный способ наделать багов в продакшене.

6. Reverse proxy на приложение

Веб-приложения (Django, FastAPI, Node.js) слушают свой порт, например 8000. Nginx стоит перед ними и передаёт запросы через директиву proxy_pass. Такой сервер называется reverse proxy.

server {
    listen 80;
    server_name app.example.com;

    location / {
        proxy_pass http://127.0.0.1:8000;
        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;
    }
}
ДирективаНазначение
proxy_passкуда передавать запросы
proxy_connect_timeoutвремя ожидания соединения с бэкендом
proxy_read_timeoutвремя ожидания ответа приложения
client_max_body_sizeмаксимальный размер тела запроса (загрузки файлов)
Разница между proxy_pass http://127.0.0.1:8000; и proxy_pass http://127.0.0.1:8000/; — слоб слэш на конце. / отбрасывает совпавшую часть URI location, что часто ломает пути приложения.
Если приложение жалуется на «untrusted» IP или не видит HTTPS — скорее всего, не передаются заголовки X-Forwarded-Proto и X-Forwarded-For.

7. Балансировка нагрузки

Когда одно приложение не справляется, за ним ставят несколько инстансов, а Nginx распределяет трафик между ними. Группа бэкендов описывается блоком upstream.

upstream backend {
    least_conn;
    server 10.0.0.1:8000 weight=3;
    server 10.0.0.2:8000;
    server 10.0.0.3:8000 backup;
}

server {
    listen 80;
    server_name app.example.com;

    location / {
        proxy_pass http://backend;
    }
}

По умолчанию запросы уходят по кругу (round-robin). Дополнительные параметры:

МетодКогда использовать
round-robinравномерное распределение по умолчанию
weightбэкенды разной мощности
least_connзапросы с разной длительностью
ip_hashнужны стабильные сессии пользователя
Nginx не проверяет здоровье бэкендов активно: сломанный сервер получает трафик до первого таймаута. Для настоящего health-check нужны модули вроде nginx-plus или внешние инструменты.

8. HTTPS и переадресация

Современные сайты обязаны работать по HTTPS. Сначала все HTTP-запросы перенаправляются на HTTPS, а затем уже работает защищённый server-блок с сертификатом.

server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    http2 on;
    server_name example.com;

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    root /var/www/example;
}

Самый быстрый способ получить бесплатный сертификат — Certbot, который сам обновляет конфигурацию Nginx:

sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d example.com -d www.example.com
ДирективаНазначение
return 301постоянная переадресация на HTTPS
ssl_certificateпуть к сертификату (цепочка)
ssl_certificate_keyпуть к приватному ключу
add_header Strict-Transport-Securityзапрет доступа по HTTP на уровне браузера
Приватный ключ сертификата — секрет. Храни его только на сервере с правами чтения для root, никогда не клади в Git-репозиторий и не вставляй в логи.
Сертификаты Let's Encrypt живут 90 дней. Certbot автоматически настраивает продление через systemd timer — проверяй, что служба включена.

9. Логирование

Nginx пишет два лога: access.log — каждый запрос и error.log — ошибки и предупреждения. Формат записей настраивается директивой log_format.

http {
    log_format main '$remote_addr - $remote_user [$time_local] '
                    '"$request" $status $body_bytes_sent '
                    '"$http_referer" "$http_user_agent"';

    access_log /var/log/nginx/access.log main;
    error_log  /var/log/nginx/error.log warn;
}

Смотреть логи в реальном времени:

sudo tail -f /var/log/nginx/access.log
sudo tail -f /var/log/nginx/error.log
КодЧто значит в error.log
emergсистема не может работать, конфигурация критически неверна
alertтребуется немедленное вмешательство
errorзапрос не обработан (5xx, таймауты)
warnнештатная, но обработанная ситуация
info / debugотладочные детали (debug включается отдельно)
DEBUG-режим логов очень подробный и медленный — включай его только на время отладки и не забывай выключить.
Ротацию логов в Ubuntu выполняет logrotate: старые файлы сжимаются и удаляются по расписанию, поэтому ручная чистка не нужна.

10. Сжатие gzip и кэширование

Сжатие ответов сокращает трафик и ускоряет загрузку сайта. Директива gzip включается на уровне http:

gzip on;
gzip_comp_level 6;
gzip_min_length 1024;
gzip_types text/plain text/css application/json
           application/javascript application/xml
           image/svg+xml;

Сжатие gzip_types применяется только к типизированным ответам — картинки JPEG и PNG уже сжаты и обрабатывать их незачем.

Кэширование уменьшает нагрузку на бэкенд за счёт сохранения ответов Nginx. Для статики достаточно заголовков браузера:

location /assets/ {
    expires 30d;
    add_header Cache-Control "public, immutable";
}

location ~* \.(css|js|png|jpg|svg|woff2)$ {
    expires 7d;
    add_header Cache-Control "public";
}
ДирективаНазначение
gzip_levelстепень сжатия 1–9 (6 — оптимум)
expiresсрок жизни кэша в браузере
proxy_cache_pathдиск для кэша ответов проксируемых приложений
map $http_upgradeподдержка WebSocket через proxy
Заголовок Cache-Control: immutable означает «не перезапрашивать никогда». Используй его только для хэшированных имён файлов сборщиками, иначе пользователи не увидят обновления.
Для проксируемого приложения (FastAPI, Django) собственный кэш настраивают через proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=app:10m max_size=1g inactive=60m; и proxy_cache app;.

11. WebSocket, SSE и push-уведомления

Nginx отлично проксирует долгоживущие соединения, но для этого ему нужно открыть обновление соединения и снять таймауты. Иначе WebSocket-рукопожатие закроется через 60 секунд.

location /ws/ {
    proxy_pass http://backend_nodes;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_read_timeout 3600s;
}
Для Server-Sent Events (SSE) достаточно proxy_buffering off; — тогда события уходят клиенту сразу, не накапливаясь в буфере.
Заголовок Connection "upgrade" не должен попадать в обычные запросы. Ограничьте location только точками WebSocket (/ws/), иначе сломаете кэширование статики.

12. Ограничение нагрузки и DDoS-защита

Модуль limit_req ограничивает частоту запросов с IP или по любому ключу. Он работает через «дырявое ведро»: избыточные запросы получат 503 вместо выполнения.

http {
    limit_req_zone $binary_remote_addr
      zone=api:10m rate=10r/s;

    server {
      location /api/ {
        limit_req zone=api burst=20 nodelay;
        proxy_pass http://backend;
      }
    }
}
Лимиты по $binary_remote_addr режут и обычных пользователей за общим NAT. Для API используйте ключи на основе токена, а IP-лимиты оставьте грубым щитом.

13. Redirect, rewrite и канонические URL

Корректные редиректы важны для SEO и миграций. rewrite менят URI «на лету», return отвечает кодом напрямую и запрещает перезапись на следующих уровнях.

# HTTP → HTTPS
server {
    listen       80;
    server_name  example.com www.example.com;
    return 301 https://$host$request_uri;
}

# Канонический домен
server {
    listen 443 ssl;
    server_name www.example.com;
    return 301 https://example.com$request_uri;
}
Используйте 301 только для постоянного переезда. Для временных акций и A/B-тестов — 302/307, иначе браузеры и поисковики запомнят постоянную переадресацию.
Правило rewrite внутри location выполняется до проверки следующих локаций — часто это источник неожиданных циклических редиректов.

14. Проверка конфигурации и мониторинг

Ошибка в конфиге оставляет сайт недоступным. Сначала всегда проверяйте синтаксис, потом перезагружайте конфигурацию без остановки сервера.

# Проверка синтаксиса
nginx -t

# Применение изменений без паузы
nginx -s reload

# Активные соединения и статистика
curl http://127.0.0.1/nginx_status
ЛогПуть по умолчанию
Доступ/var/log/nginx/access.log
Ошибки/var/log/nginx/error.log
EC2/VMможно перенаправить в stdout
Автоматизируйте проверку nginx -t в CI перед деплоем и следите за error.log: переполнение лимитов файлов и «Too many open files» — первые признаки пиковой нагрузки.

15. Nginx в Docker: практичная сборка

Контейнер с Nginx удобен для локальной разработки и деплоя. Возьмите официальный образ nginx:alpine, смонтируйте конфиг и статику через volume, а при релизе соберите образ с файлами внутри — так приложение станет самодостаточным артефактом.

# nginx.conf кладём в ./conf
server {
  listen 80;
  root /usr/share/nginx/html;
  location /api/ {
    proxy_pass http://backend:3000;
  }
}
docker run --rm -d -p 8080:80 \
  -v "$PWD/site:/usr/share/nginx/html" \
  -v "$PWD/conf:/etc/nginx/conf.d:ro" \
  nginx:alpine

Внутри compose-сети адрес бэкенда — имя сервиса (backend), а не localhost. Не зашивайте хардкод доменов: передавайте их переменными окружения через шаблон envsubst, чтобы один конфиг работал в dev и prod.

Для многостраничных SPA добавьте try_files $uri /index.html; — иначе клиентские маршруты при обновлении страницы отдают 404.
Проверяйте конфиг внутри контейнера командой nginx -t перед перезапуском: ошибка конфигурации обрушит здоровый контейнер.

16. Тюнинг производительности и microcaching

Базовый деплой легко ускорить без смены архитектуры: включите keepalive, кэш открытых файлов и, главное, microcaching — кэширование составных страниц на доли секунды. Для push-уведомлений и персонализации, где кэш недопустим, добавьте исключения по cookie.

http {
  proxy_cache_path /tmp/nxcache levels=1:2
                   keys_zone=cz:10m max_size=1g;

  server {
    location / {
      proxy_cache cz;
      proxy_cache_valid 200 1s;
      proxy_cache_key "$request_uri$cookie_session";
      proxy_cache_bypass $cookie_session;
    }
  }
}

Ключ кэша должен включать то, что влияет на ответ: параметры запроса, заголовок Accept-Language или идентификатор сессии, если страница персонализирована. Метрика $upstream_cache_status в логе покажет, сколько запросов реально отдано из кэша (HIT/MISS).

Microcaching выручает при пиках трафика: отдавая кэшированную копию 1–5 секунд, вы защищаете бэкенд от волны одновременных запросов.

17. Аутентификация и авторизация через auth_request

Директива auth_request делегирует проверку доступа внешнему сервису: Nginx делает внутренний запрос на онлайн-эндпоинт, и если тот вернул 200 — пропускает пользователя дальше. Так можно закрыть админку, лендинг для партнёров или API.

server {
  location = /auth-check {
    internal;
    proxy_pass http://auth-service:8888/v1/verify;
    proxy_set_header Cookie $http_cookie;
  }

  location /admin/ {
    auth_request /auth-check;
    auth_request_set $user $upstream_http_x_user;
    proxy_set_header X-User $user;
    proxy_pass http://admin-ui:3000;
  }
}

Сервис авторизации возвращает в заголовках данные о пользователе, которые auth_request_set перекладывает в переменную и передаёт бэкенду. Отдельный эндпоинт под токен, отозванный после логаута, обновляет состояние мгновенно — без ждать истечения cookie.

Эндпоинт проверки должен быть быстрым и кэшируемым, иначе он превратится в узкое место. Кэшируйте успешные проверки на 1–5 секунд через proxy_cache внутри location.

18. Медиа и стриминг: mp4, HLS и диапазонные запросы

Nginx отдаёт видео и потоковый контент с минимальной настройкой. Для mp4 включите модуль ngx_http_mp4_module: он поддерживает перемотку через range-запросы и не отдаёт файл целиком при поиске по таймлайну.

location /video/ {
  mp4;
  mp4_buffer_size 1M;
  mp4_max_buffer_size 5M;
  root /srv/media;
  expires 7d;
  add_header Accept-Ranges bytes;
}

Для адаптивного стриминга на любых устройствах укладывайте контент в HLS: сервер раздаёт плейлисты .m3u8 и сегменты .ts как статику, а переключение качества и буферизацию делает плеер. Ограничьте параллельные запросы к сегментам через limit_conn, чтобы один поток не вытеснил остальных зрителей.

Ставьте корректные Content-Type и CORS-заголовки для HLS: браузерные плееры требовательны к типам application/vnd.apple.mpegurl.
Большое видео кэшируйте на диске, а не в памяти: файл, не помещающийся в кэш-зону, сбрасывается и начинает читаться заново на каждый запрос.

19. PHP-FPM и динамические страницы

Nginx не исполняет PHP напрямую: запросы передаются процессу PHP-FPM по протоколу FastCGI. Связка типична для WordPress, Битрикса и большинства CMS: статику отдаёт Nginx, а динамику — пул PHP-воркеров.

location ~ \.php$ {
    include        fastcgi_params;
    fastcgi_pass   unix:/run/php/php-fpm.sock;
    fastcgi_index  index.php;
    fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
}

location / {
    try_files $uri $uri/ /index.php?$query_string;
}

Второй блок закрывает прямой доступ: все запросы, не совпавшие с реальным файлом, уходят в index.php — так фронт-контроллер приложения получает любой маршрут, а файлы загрузки не исполняются даже при верном расширении.

Используйте Unix-сокет вместо TCP: меньше накладных расходов и безопаснее изоляция на одном хосте.
Никогда не ставьте location ~ \.php$ без ограничений по директории загрузки: поддельный шелл в uploads получит право исполнения.

20. Надёжность upstream: таймауты и health-чеки

Когда инстанс приложения падает, Nginx должен быстро переключиться на здоровый. Для этого используют таймауты, счётчики сбоев и проверки доступности: неисправный сервер снимается с ротации, а запрос уходит в резерв.

upstream backend {
    server app1:8080 max_fails=3 fail_timeout=30s;
    server app2:8080 max_fails=3 fail_timeout=30s;
    keepalive 32;                    // пул соединений
}

location / {
    proxy_pass http://backend;
    proxy_connect_timeout 3s;
    proxy_read_timeout 10s;
    proxy_next_upstream error timeout;
    proxy_next_upstream_tries 2;
}
max_fails / fail_timeout
сколько сбоев за период выводит узел из пула
proxy_next_upstream
какие ошибки разрешают переключение на следующий сервер
proxy_read_timeout
максимум ожидания ответа от приложения

Для активных проверок установите health-checks (коммерческая версия) или примените пассивный контроль через счётчики сбоев. Ретраи на POST-запросы включайте осторожно: повторный платёж может улететь дважды.

По умолчанию Nginx ретраит запросы только на «безопасные» по HTTP методы; для неидемпотентных операций повторение ломает бизнес-логику.

21. map, geo и динамические переменные

Директивы map и geo вычисляют переменные по условиям и избавляют от громоздких конструкций с if. Классический пример — поддержка WebSocket: значение заголовка Connection зависит от Upgrade-заголовка клиента.

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

geo $is_allowed {
    default       0;
    192.168.0.0/16 1;
}

location /ws {
    proxy_set_header Upgrade    $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
}

Гео-режимы применяют для региона-контроля и A/B-экспериментов, а map позволяет строить вложенные условия, не засоряя конфиг. Переменные вычисляются один раз на запрос и доступны во всех директивах.

map со значениями «upgrade/close» — стандарт для проксирования WebSocket и SSE-потоков единым блоком.
geo и map зависят от порядка and объединения: проверяйте конфигурацию через nginx -t после каждого изменения.

22. Stream-модуль: TCP и UDP-проксирование

Помимо HTTP Nginx умеет проксировать сырые TCP- и UDP-потоки через модуль stream. Это удобно для балансировки баз данных, Redis, почтовых серверов и DNS-запросов на сетевом уровне, без переписывания протокола.

stream {
    upstream mysql_pool {
        server db1:3306;
        server db2:3306;
    }

    server {
        listen      3306;
        proxy_pass  mysql_pool;
        proxy_timeout 10s;
        proxy_connect_timeout 5s;
    }
}

Конфиг stream пишется в отдельном контексте верхнего уровня и подключается директивой include stream.conf. Варианты: TLS-терминация для безопасного трафика, балансировка по IP-хешу и ограничение числа подключений.

Мостите через stream любой не-HTTP протокол, когда нужны балансировка и отработка отказа без переусложнения стеком.
TCP-прокси не анализирует содержимое: решения «по приложению» сюда не входят — только маршрутизация и мелкие заголовки.

Что дальше

Ты научился настраивать Nginx: статику, виртуальные хосты, reverse proxy, балансировку, HTTPS, логи, сжатие и кэширование. Логичный следующий шаг — завернуть Nginx и приложение в Docker и автоматизировать деплой через GitHub Actions. Если хочешь глубже разобраться в HTTP и DNS — читай урок HTTP и DNS.

← Вернуться к курсам