Продакшен-конфиг: собираем всё вместе

Финал курса: соберём отдельные кусочки — статику, прокси, балансировку, 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 до боевого конфига.

Проверьте себя
1. Зачем в продакшен-конфиге статика отдаётся через `location ^~ /static/`, а не обычный префикс?
AДля сжатия
BМодификатор ^~ при совпадении префикса отменяет проверку регэкспов, гарантируя, что статика не уйдёт случайно на прокси
CЧтобы включить HTTPS
DЭто синтаксическая необходимость
2. Что обязательно сделать перед `nginx -s reload` при выкатке прод-конфига?
AПерезагрузить сервер
BВыполнить `nginx -t` для проверки синтаксиса — сломанный конфиг лучше поймать до применения
CУдалить логи
DОтключить HTTPS