Nginx как reverse proxy: базовая настройка с нуля
Привет! Я Евгений Сергеевич Семёнов, директор АйТи Фреш. Можете себе представить: за 15+ лет в IT я ставил Nginx как reverse proxy, наверное, уже сотни раз? И делал это для самых разных систем: от 1С-Битрикс и WordPress до Nextcloud, GitLab, Mattermost, а также внутренних API на Node.js и Django. Но каждый раз, когда я приступал к новой настройке, я сталкивался с одной и той же проблемой. Типовые конфиги, которые гуляют по интернету, либо давно устарели, либо просто ломают половину приложений. Вот почему мы в ITFresh решили: хватит это терпеть! Я собрал и публикую свой проверенный, реально рабочий конфиг. Он отработан и надёжен в продакшене, мы можем это гарантировать.
Что такое reverse proxy и когда он нужен
Что такое reverse proxy? Это, по сути, сервер-посредник. Он встаёт между клиентами и вашими приложениями. Клиент «видит» только Nginx, а Nginx уже сам общается с приложением. Зачем это нужно? Плюсы такой схемы просты и абсолютно понятны:
- SSL-терминация. Сертификат живёт на одном Nginx, приложения по HTTP по внутренней сети — проще обновлять.
- Один публичный IP. На одном сервере десятки сайтов через server_name.
- Кеширование статики и API-ответов. Снимает нагрузку с бэкенда.
- Rate limiting и защита. Ограничение запросов, бан бот-ферм.
- Zero-downtime deploy. Переключение upstream без разрыва соединений.
- Единая точка логов. Все обращения в одном access_log.
У нас в ITFresh это аксиома: reverse proxy мы ставим перед 100% всех веб-приложений наших клиентов. Честно говоря, после многих лет опыта, никаких других рабочих вариантов я просто не вижу.
Установка Nginx под Debian/Ubuntu
В репозитории Ubuntu версия Nginx, к сожалению, часто отстаёт. А зачем нам устаревший софт, правда? Поэтому я всегда предпочитаю ставить его из официального репозитория nginx.org. Там всегда доступны актуальные stable или mainline версии.
apt update && apt install -y curl gnupg2 ca-certificates
curl https://nginx.org/keys/nginx_signing.key | \
gpg --dearmor -o /usr/share/keyrings/nginx-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
http://nginx.org/packages/ubuntu $(lsb_release -cs) nginx" > /etc/apt/sources.list.d/nginx.list
apt update && apt install -y nginx
systemctl enable --now nginx
Проверяем:
nginx -v
nginx -T | head -50
Структура файлов и правильная организация
Вот важный принцип: я ни разу не правлю nginx.conf напрямую для сайтов. Там должны быть только общие параметры. Конфиги для каждого сайта — это всегда отдельные файлы, и они живут в /etc/nginx/conf.d/ или в классических sites-available/sites-enabled. Вот, к примеру, минимальный /etc/nginx/nginx.conf, с которого я обычно начинаю:
user nginx;
worker_processes auto;
worker_rlimit_nofile 65535;
events {
worker_connections 16384;
use epoll;
multi_accept on;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
server_tokens off;
client_max_body_size 256M;
client_body_timeout 60s;
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" rt=$request_time ut=$upstream_response_time';
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log warn;
include /etc/nginx/conf.d/*.conf;
}
Базовый конфиг обратного прокси
Итак, создаём файл /etc/nginx/conf.d/app.example.ru.conf. Представьте, ваше приложение работает где-то на 127.0.0.1:3000 — это может быть что угодно, например, Node.js. Nginx, в свою очередь, будет слушать 80-й порт и автоматически перенаправлять все запросы на безопасный 443-й.
upstream app_backend {
server 127.0.0.1:3000 max_fails=3 fail_timeout=30s;
keepalive 32;
}
server {
listen 80;
server_name app.example.ru;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl 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;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_session_cache shared:SSL:20m;
ssl_session_timeout 1d;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options SAMEORIGIN always;
add_header X-Content-Type-Options nosniff always;
access_log /var/log/nginx/app.access.log main;
error_log /var/log/nginx/app.error.log;
location / {
proxy_pass http://app_backend;
proxy_http_version 1.1;
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_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
proxy_connect_timeout 10s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
proxy_buffers 16 16k;
proxy_buffer_size 32k;
}
}
Этот конфиг — всего лишь стартовый шаблон. От него я уже отталкиваюсь, добавляя специфические настройки под каждое конкретное приложение.
Сертификат через Let's Encrypt
Certbot можно установить либо из snap-пакета, либо через обычный пакетный менеджер. Лично я всегда выбираю snap. Причина проста: там всегда актуальнее версии плагинов.
apt install -y certbot python3-certbot-nginx
certbot --nginx -d app.example.ru --redirect --agree-tos -m admin@example.ru
А про автообновление сертификатов можно вообще забыть! Оно уже настроено в cron: система проверяет срок действия каждые 12 часов. Если до истечения осталось меньше 30 дней, она сама всё обновит.
WebSocket и long-polling
Случалось ли вам сталкиваться с тем, что Nginx внезапно "рвёт" WebSocket-соединение ровно через 60 секунд? Это частая проблема, когда приложение активно использует веб-сокеты: будь то Mattermost, Rocket.Chat, онлайн-чаты или админки на Node.js. Чтобы избежать таких неприятностей и досадных обрывов связи, я всегда добавляю либо отдельный location, либо включаю глобальную поддержку WebSocket.
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
location /ws/ {
proxy_pass http://app_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
}
Важно помнить: блок `map` пишется в `http {}`, а `location /ws/` — уже внутри `server {}`. Кстати, для Mattermost или Rocket.Chat это просто обязательная конфигурация. Не поставите — и пользователи будут получать ненавистное «disconnected» каждую минуту.
Кеширование бэкенда
Ваше приложение отдаёт "тяжёлые" ответы, которые при этом редко меняются? Например, это списки товаров, RSS-ленты или API курсов валют. Тогда обязательно кешируем их в Nginx! Это заметно снижает нагрузку и ускоряет загрузку для пользователя.
# В http {}
proxy_cache_path /var/cache/nginx/app_cache levels=1:2 keys_zone=app_cache:100m max_size=5g
inactive=7d use_temp_path=off;
# В location
location /api/catalog/ {
proxy_cache app_cache;
proxy_cache_key "$scheme$request_method$host$request_uri";
proxy_cache_valid 200 5m;
proxy_cache_valid 404 30s;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
proxy_cache_background_update on;
proxy_cache_lock on;
add_header X-Cache-Status $upstream_cache_status;
proxy_pass http://app_backend;
}
Кстати, для отладки есть полезный индикатор: флаг `X-Cache-Status` в заголовке ответа. Он покажет вам статус кеша: `HIT`, `MISS` или `UPDATING`. Сразу становится понятно, что там с кешированием.
Реальный кейс: портал и внутренний Helpdesk в одной коробке
Помню, как-то в августе 2025 года (да, именно так, даты важны!) к нам обратился один клиент. Крупный дистрибьютор электронных компонентов, у них — 45 рабочих мест. Их просьба звучала просто: «Сделайте нам, пожалуйста, всё на одном сервере». А картина у них была такая: внешний Битрикс24-портал, внутренний Helpdesk GLPI, Kanboard, да ещё и система документооборота. И что самое удивительное: каждое из этих приложений сидело на своей отдельной виртуалке, и каждая такая виртуалка зачем-то занимала по ТРИ публичных IP-адреса! Вы можете представить этот хаос? Это был настоящий бардак, по-другому не скажешь!
Что мы сделали, чтобы решить этот бардак? Собрали один, но очень мощный сервер Dell: установили Xeon Platinum 8280, 128 ГБ RAM, а для скорости — 40G Mellanox до свича в дата-центре МТС. На нём подняли Nginx reverse proxy, настроили сертификаты Let's Encrypt сразу на 7 поддоменов. Да, все приложения остались на своих виртуалках во внутренней сети, но публично теперь был один IP и один Nginx. Мы не забыли добавить кеш статики по /static/, включили WebSocket для GLPI и настроили rate limiting на /api/. В итоге? Ошибок стало куда меньше, администрирование упростилось, а SSL-сертификаты? Их я один раз настроил и просто забыл. Вся эта магия заняла 4 дня, а обошлась клиенту в 72 000 рублей.
Мониторинг и типичные ошибки
Ну вот, всё настроено, и что дальше? Мы всегда сразу включаем `stub_status`. Это ведь фундамент для любого адекватного мониторинга, без него никуда.
server {
listen 127.0.0.1:8080;
server_name _;
location /nginx_status {
stub_status;
allow 127.0.0.1;
deny all;
}
}
Для чего это нужно? Именно к этой странице затем подключается `Prometheus nginx_exporter`. С его помощью мы получаем подробную картину: сколько `Active connections` сейчас работает, сколько запросов в статусе `reading`, `writing` и, конечно, `waiting`.
Типовые грабли:
- 502 Bad Gateway. Бэкенд не запущен или слушает не тот порт. Проверить
ss -tlnpна сервере приложения. - 413 Request Entity Too Large. client_max_body_size слишком маленький. Поставить 100М и больше.
- 504 Gateway Timeout. Бэкенд тормозит. Увеличить proxy_read_timeout или чинить бэкенд.
- Битая кодировка в логах. Добавить proxy_set_header Accept-Charset utf-8 и charset utf-8 в server {}.
- Пустые X-Real-IP на бэкенде. Приложение не читает X-Forwarded-For или не настроен trust_proxy.
Настроим Nginx reverse proxy под ваши приложения
Я устанавливаю Nginx перед любыми веб-сервисами, будь то 1С-Битрикс, WordPress, Node.js, Python-бэкенды или корпоративные порталы. Настраиваю всё под ключ: HTTPS, кеш, WebSocket, rate limiting. Работаю с проектами от 1 до 30 доменов, а срок выполнения — от 1 рабочего дня.
Телефон: +7 903 729-62-41
Telegram: @ITfresh_Boss
Семёнов Евгений Сергеевич, директор АйТи Фреш
FAQ — частые вопросы по Nginx proxy
- Зачем нужен reverse proxy перед веб-приложением?
- Reverse proxy закрывает приложение от прямого доступа, терминирует SSL, раздаёт статику, кеширует ответы, балансирует нагрузку между бэкендами и унифицирует логи. Приложению не надо думать про TLS и сертификаты.
- Чем отличается forward от reverse proxy?
- Forward proxy защищает клиентов от прямого общения с внешними ресурсами (корпоративный squid). Reverse proxy защищает серверы: принимает клиентские запросы извне и направляет их к внутренним приложениям.
- Какие заголовки обязательно прокидывать?
- Host, X-Real-IP, X-Forwarded-For, X-Forwarded-Proto. Без них бэкенд видит запросы от 127.0.0.1 и считает их небезопасными, логи теряют реальные адреса клиентов, а генерация ссылок ломается.
- Надо ли отдельно настраивать WebSocket?
- Да, нужно явно включить проксирование заголовков Upgrade и Connection, иначе Nginx разрывает соединение через 60 секунд. Ещё увеличить proxy_read_timeout до нужного значения.
- Какой буфер использовать под тяжёлые запросы?
- client_max_body_size должен покрывать максимальный размер загружаемого файла. По умолчанию 1 МБ — этого мало для большинства приложений. Ставлю 100М–1Г в зависимости от задачи.
