Москва: проверенные б/у ноутбуки с гарантией и проверкой перед оплатой
+7 906 722-94-45

Главная → Статьи → nginx как reverse proxy: настройка

nginx reverse proxy на практике: от первого конфига до продакшн-набора

Рабочая настройка nginx: сервер-блоки и location, проксирование на приложение, HTTPS и HTTP/2, заголовки X-Forwarded, буферы и таймауты, gzip, логи — практический чек-лист.

Прямой ответ

Reverse proxy — слой между интернетом и приложением, и nginx закрывает сразу несколько задач: TLS-терминация (приложение живёт на голом HTTP внутри), один вход на несколько сервисов (роутинг по доменам/путям), статика мимо приложения, сжатие, базовая защита (лимиты, скрытие внутренностей). Минимальный рабочий конфиг — server-блок с listen 443 ssl, server_name, location / { proxy_pass } и набором X-Forwarded-заголовков (без них приложение не знает ни реальный IP, ни https). Дальше конфиг обрастает проверенными блоками: таймауты и размеры тел под нагрузку, gzip для текста, кеш статики, лимиты частоты, отдельные location для WebSocket (Upgrade-заголовки). Правило эксплуатации: правки проверяются nginx -t до reload, а сам reload (не restart) не рвёт соединения.

Структура конфигурации: где что живёт

Иерархия: nginx.conf (глобальное: пользователь, воркеры, events) → http (общее: mime-типы, логи, gzip) → server (виртуальный хост: домен, порты, TLS) → location (маршруты). Дистрибутивная практика — включения: базовые настройки в conf.d/*.conf, сайты в sites-available/ с симлинками в sites-enabled/ (на Debian-семействе), общие куски проксирования — сниппеты (proxy_params). Воркеры — по числу ядер (worker_processes auto), worker_connections — тысячи; эти цифры почти никогда не узкое место — узкие места прячутся в апстримах и таймаутах. Читаемость конфига — часть надёжности: один сервис — один server-файл с именем домена, комментарии на нестандартные решения; «один nginx.conf на всё» через год не читает никто, включая автора.

Проксирование: заголовки, которые нельзя забыть

Классический location /: proxy_pass плюс обязательный набор — proxy_set_header Host $host;, X-Real-IP $remote_addr;, X-Forwarded-For $proxy_add_x_forwarded_for;, X-Forwarded-Proto $scheme;. Без последнего FastAPI/Django за прокси не знают, что запрос был по https (схема, редиректы, secure-cookie ломаются); без X-Forwarded-For в логах приложения все клиенты — «127.0.0.1» (а rate-limit по IP банит всех сразу). Апстрим на 127.0.0.1:порт— дефолт для «прокси и приложение на одной машине»; несколько инстансов — блокupstream` с round-robin/least_conn и health-параметрами. Приложение при этом слушает только localhost (не 0.0.0.0) — наружу смотрит исключительно nginx. Для локальной разработки с https тот же паттерн, но проще — Caddy/mkcert (разбор).

HTTPS, HTTP/2 и WebSocket

TLS: сертификат (Let's Encrypt/certbot с автопродлением) — ssl_certificate/ssl_certificate_key, протоколы — TLSv1.2/1.3 (ssl_protocols), http2 включается в listen ... http2; HTTP→HTTPS — отдельный server-блок на 80 с редиректом 301 (и /.well-known/acme-challenge/ для продления). WebSocket — отдельный location с proxy_http_version 1.1;, proxy_set_header Upgrade $http_upgrade;, Connection "upgrade"; и длинным proxy_read_timeout (дефолтные 60 с рвут молчаливые WS-соединения — механика самих вебсокетов в статье про WebSocket). Таймауты вообще — под нагрузку: proxy_read_timeout под самый долгий легитимный ответ (иначе nginx отдаст 504 раньше приложения), client_max_body_size под загрузки (дефолтный 1 М — источник «413 при загрузке файла»).

Статика, gzip и кеширование

Правило: статику отдаёт nginx, не приложение (location /static/ { alias /var/www/app/static/; expires 30d; } — приложение-фреймворк тратит на файл процессор воркера, nginx — почти нет; для Django это ещё и обязательство collectstatic). Gzip — для текстовых типов (gzip_types text/css application/javascript application/json;, gzip_min_length 1k; JSON-ответы API сжимаются в разы — заметно на больших списках). Кеш-заголовки — по семантике: статика с хешем в имени — Cache-Control: immutable, max-age=31536000, HTML — no-cache (проверка свежести каждый раз). Прокси-кеш (proxy_cache) для кэширования ответов апстрима — мощный инструмент (и способ положить приложение неподготовленным кеш-штормом при инвалидации) — включается осознанно под конкретные GET-маршруты.

Практический сценарий: продакшн-обвязка FastAPI-сервиса

Собираем полный набор для Python-сервиса (uvicorn на 127.0.0.1:8000, systemd-юнит — гайд). Server-блок: TLS от certbot, http2, редирект с 80; location / — проксирование с полным набором заголовков, proxy_read_timeout 60s, client_max_body_size 10m; location /ws/ — websocket-набор с read_timeout 3600; location /static/ — alias с expires; location /healthz — прокс на health-эндпоинт без логирования (access_log off — иначе лог на 90% из healthcheck'ов). Защита минимумом: limit_req_zone $binary_remote_addr zone=api:10m rate=30r/s; с limit_req zone=api burst=50 nodelay; на API-location (защита от долбёжки, не от целевой DDoS), server_tokens off (версию nginx не показываем), add_header X-Content-Type-Options nosniff. Логи — JSON-формат (log_format json escape=json {...}) с request_id (структурированные логи), ротация logrotate'ом. Проверка: nginx -t && systemctl reload nginx; smoke-тест — curl с Host-заголовком и проверка X-Forwarded-Proto в эхо-эндпоинте. Вся обвязка — сто двадцать строк конфига, которые живут в git и раскатываются Ansible-ролью.

FAQ

nginx или Caddy/Traefik для прода? nginx — максимум контроля и производительности, цена — ручной TLS-конфиг и больше строк; Caddy — авто-TLS из коробки (для локальной разработки и простых прод-кейсов удобнее); Traefik — когда конфигурация динамическая из оркестратора (Kubernetes/Docker-лейблы). Для «статичный сервер с nginx-конфигом в git» nginx остаётся стандартом.

Почему 502 Bad Gateway? nginx не достучался до апстрима: приложение не запущено, слушает не тот порт/адрес, упало под запросом, или SELinux запрещает nginx исходящие соединения (setsebool -P httpd_can_network_connect 1 на RHEL-семействе — классика). Диагностика — error.log nginx и статус сервиса приложения.

Нужен ли gzip для API-ответов? Да, для JSON выигрыш типовой 3–10×; включайте gzip_types application/json и gzip_comp_level 4–5 (выше — процессор без заметного выигрыша). Для уже сжатого (картинки, архивы) gzip бесполезен — не добавляйте их в типы.

Как обновить nginx без обрыва соединений? systemctl reload nginx (master перечитывает конфиг, старые воркеры дорабатывают соединения) после обязательного nginx -t. Restart нужен только при смене бинарника/портов master-процесса — и он рвёт соединения.

Источники: документация nginx (proxy module, websocket, gzip) — ссылки в шапке.