Продакшен-конфиг: собираем всё вместе
Финал курса: соберём отдельные кусочки — статику, прокси, балансировку, HTTPS, безопасность — в один цельный продакшен-конфиг и пройдёмся по чек-листу.
«Хороший прод-конфиг скучен и предсказуем. Никакой магии — только проверенные директивы на своих местах.»
Мы прошли все темы по отдельности. Теперь склеим их в один реалистичный конфиг для сайта с приложением: Nginx терминирует HTTPS, балансирует на два бэкенда, раздаёт статику, защищён заголовками и лимитами.
Глобальные настройки
user www-data;
worker_processes auto;
events {
worker_connections 1024;
multi_accept on;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
server_tokens off;
gzip on;
gzip_types text/plain text/css application/json application/javascript;
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
include /etc/nginx/sites-enabled/*;
}
Апстрим бэкендов
upstream app {
least_conn;
server 127.0.0.1:8000 max_fails=3 fail_timeout=30s;
server 127.0.0.1:8001 max_fails=3 fail_timeout=30s;
keepalive 32;
}
Сервер: HTTPS + прокси + статика
server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri; # всё на https
}
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;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_cache shared:SSL:10m;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
add_header X-Content-Type-Options nosniff always;
add_header X-Frame-Options SAMEORIGIN always;
# статика — напрямую с диска, кеш надолго
location ^~ /static/ {
alias /var/www/app/static/;
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
# API — с лимитом запросов
location /api/ {
limit_req zone=api burst=20 nodelay;
proxy_pass http://app;
proxy_http_version 1.1;
proxy_set_header Connection "";
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_connect_timeout 5s;
proxy_read_timeout 60s;
}
# всё остальное — на приложение
location / {
proxy_pass http://app;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Как читается этот конфиг
http://example.com -> 301 -> https://example.com
|
+----------------+----------------+
| | |
/static/ /api/ / (прочее)
alias с диска лимит + proxy proxy
(быстро, на upstream app на upstream app
кеш 1y) (least_conn,2 бэка) (least_conn)
Как работает под капотом
Запрос приходит на 443, Nginx терминирует TLS. По location выбирается ветка: ^~ /static/ (приоритетный префикс, статика мимо прокси), /api/ (с лимитом) или / (всё прочее). Прокси-ветки бьют в upstream app с least_conn, keepalive-пулом и пассивными health-checks. Статика отдаётся через sendfile с годовым кешем. Заголовки безопасности и HSTS навешиваются на все ответы сервера. Это и есть тот самый «скучный, предсказуемый» прод.
Частые ошибки
- Деплой без
nginx -t. Финальная проверка перед reload обязательна — сломанный конфиг лучше поймать заранее. - Статика через прокси. Если
/static/уедет на бэкенд вместо диска — потеря скорости и лишняя нагрузка. Префикс^~защищает. - Забытый
proxy_set_header Connection "". Keepalive к апстриму не заработает.
Чек-лист запуска
- Конфиг:
nginx -tзелёный, затемnginx -s reload. - TLS: http редиректит на https, HSTS на месте, оценка A на внешнем тесте.
- Бэкенд: слушает только 127.0.0.1, keepalive работает, health-checks заданы.
- Статика: отдаётся напрямую, кеш-заголовки на месте, не логируется.
- Безопасность: заголовки с
always,server_tokens off, rate limit на чувствительных маршрутах. - Наблюдаемость: логи пишутся и ротируются, мониторинг видит метрики.
Что отличает учебный конфиг от боевого
Конфиг выше уже близок к боевому, но настоящий продакшен — это ещё и то, чего в одном файле не видно. Во-первых, разбивка на части: глобальные настройки в nginx.conf, повторяющиеся блоки (ssl-параметры, набор proxy_set_header) в snippet'ах, каждый сайт — отдельным файлом, и всё под контролем версий в git, чтобы любую правку можно было откатить и отследить, кто и зачем её внёс. Во-вторых, тестируемость: перед выкатом не только nginx -t, но и реальная проверка ключевых сценариев — открылся ли сайт, редиректит ли http на https, отдаётся ли статика с правильными заголовками, переживает ли система падение одного бэкенда.
В-третьих, эксплуатация. Боевой Nginx живёт в окружении: системный лимит файловых дескрипторов поднят под ожидаемое число соединений; логи ротируются и стекаются в мониторинг; сертификаты автопродлеваются и за их сроком следит алерт; есть процедура graceful reload без простоя. И, наконец, безопасность сквозит во всём: бэкенды слушают только localhost, приватные ключи доступны лишь root, версия Nginx скрыта, чувствительные маршруты под лимитом, заголовки безопасности на месте. Хороший прод-конфиг и правда «скучный» — в нём нет хитрых трюков, только проверенные директивы на своих местах, понятная структура и предсказуемое поведение под нагрузкой и при сбоях. Именно к такому состоянию ты теперь умеешь приводить Nginx: от первой строки listen до системы, которой можно доверить реальный трафик.
Итоги
Продакшен-конфиг Nginx объединяет всё изученное: HTTPS с современным TLS и редиректом, проксирование на сбалансированный keepalive-апстрим с health-checks, быструю раздачу статики через приоритетный ^~ /static/, защитные заголовки и rate limiting. Перед запуском — nginx -t и чек-лист. Поздравляю: теперь ты умеешь ставить Nginx перед приложением по-взрослому, от первого listen до боевого конфига.