Nginx: продвинутая конфигурация для боевых серверов

nginxweb-serverlinuxsecurity

Дашборд мониторинга Nginx: метрики запросов, upstream и кеша

Nginx давно вышел за рамки «просто веб-сервера». Сегодня это reverse proxy, load balancer, кеширующий слой и SSL terminator в одном лице. Большинство руководств останавливаются на базовой настройке с server_name и proxy_pass, но в продакшене этого недостаточно. В этой статье разберём продвинутые техники, которые я использую на боевых серверах.

Upstream и балансировка нагрузки

Если у вас несколько инстансов приложения, Nginx может распределять трафик между ними через блок upstream. Три основных алгоритма: round-robin (по умолчанию, запросы по очереди), least_conn (запрос уходит серверу с наименьшим числом активных соединений), ip_hash (клиент всегда попадает на один сервер, полезно для sticky sessions).

В реальности серверы не всегда одинаковы. Можно задать вес, health checks и параметры отказоустойчивости:

upstream app_backend {
    least_conn;
    server 127.0.0.1:3001 weight=3;
    server 127.0.0.1:3002 weight=1;
    server 127.0.0.1:3003 weight=1 max_fails=3 fail_timeout=30s;
    server 127.0.0.1:3004 backup;  # только если основные недоступны
}

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

    location / {
        proxy_pass http://app_backend;
        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_next_upstream error timeout http_502 http_503 http_504;
        proxy_next_upstream_tries 2;
    }
}

Кеширование с proxy_cache

Кеширование ответов от backend — один из самых эффективных способов снизить нагрузку. Сначала определяем зону кеша в блоке http: proxy_cache_path /var/cache/nginx/app_cache levels=1:2 keys_zone=app_cache:10m max_size=1g inactive=60m use_temp_path=off;. Затем используем в location:

location /api/ {
    proxy_pass http://app_backend;
    proxy_cache app_cache;
    proxy_cache_key "$scheme$request_method$host$request_uri";
    proxy_cache_valid 200 10m;
    proxy_cache_valid 404 1m;
    add_header X-Cache-Status $upstream_cache_status;
    proxy_cache_bypass $cookie_session;
    proxy_no_cache $cookie_session;
    proxy_cache_use_stale error timeout updating http_500 http_502 http_503;
}

Не всё нужно кешировать. POST/PUT/DELETE и запросы авторизованных пользователей следует пропускать через map и proxy_no_cache.

Заголовки безопасности

Правильные HTTP-заголовки — простой способ защитить пользователей:

server {
    # HSTS — браузер запомнит, что сайт работает только по HTTPS
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

    # Защита от clickjacking
    add_header X-Frame-Options "SAMEORIGIN" always;

    # Запретить MIME-sniffing
    add_header X-Content-Type-Options "nosniff" always;

    # Referrer Policy
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;

    # Ограничить доступ к API браузера
    add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;

    # Content Security Policy
    add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:;" always;
}

Важный нюанс: add_header в дочернем location перезаписывает заголовки из родительского server. Если добавляете заголовок в location — продублируйте остальные.

Rate limiting

Nginx имеет два механизма: limit_req (частота запросов) и limit_conn (число соединений).

http {
    limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
    limit_req_zone $binary_remote_addr zone=login_limit:5m rate=1r/s;
    limit_req_status 429;
    limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
}

server {
    limit_conn conn_limit 50;

    location /api/ {
        limit_req zone=api_limit burst=20 nodelay;
        proxy_pass http://app_backend;
    }

    location /api/auth/login {
        limit_req zone=login_limit burst=5 nodelay;
        proxy_pass http://app_backend;
    }
}

Конфигурация Nginx: rate limiting и заголовки безопасности

Сжатие: gzip и Brotli

Сжатие уменьшает объём передаваемых данных на 60-80%:

http {
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_min_length 1024;
    gzip_types text/plain text/css text/javascript application/javascript
               application/json application/xml image/svg+xml font/woff2;

    # Brotli (нужен модуль ngx_brotli)
    brotli on;
    brotli_comp_level 6;
    brotli_min_length 1024;
    brotli_types text/plain text/css text/javascript application/javascript
                 application/json application/xml image/svg+xml font/woff2;
}

Brotli и gzip можно включить одновременно. Nginx выберет Brotli, если браузер поддерживает его (заголовок Accept-Encoding: br).

Проксирование WebSocket

WebSocket начинается как HTTP-запрос с заголовком Upgrade, затем переключается на двунаправленный протокол. Nginx нужно явно передать заголовки апгрейда:

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

server {
    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 3600s;
        proxy_send_timeout 3600s;
    }
}

Без этих заголовков WebSocket-соединения будут обрываться сразу после handshake — одна из самых частых ошибок при настройке reverse proxy.

SSL/TLS: best practices

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

    ssl_certificate /etc/letsencrypt/live/app.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 1d;
    ssl_session_tickets off;
    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 1.1.1.1 8.8.8.8 valid=300s;
    ssl_dhparam /etc/nginx/dhparam.pem;
}

Генерация DH-параметров: sudo openssl dhparam -out /etc/nginx/dhparam.pem 4096. Редирект HTTP на HTTPS: server { listen 80; return 301 https://$host$request_uri; }.

Server hardening

Несколько важных настроек для укрепления сервера:

http {
    server_tokens off;                   # скрыть версию Nginx
    client_max_body_size 10m;            # ограничить размер запроса
    client_header_timeout 10s;           # защита от Slowloris
    client_body_timeout 10s;
    send_timeout 10s;
    reset_timedout_connection on;
    keepalive_timeout 30s;
}

Блокировка нежелательных ботов:

map $http_user_agent $bad_bot {
    default 0;
    "~*sqlmap"  1;
    "~*nikto"   1;
    "~*nmap"    1;
    "~*masscan" 1;
}

server {
    if ($bad_bot) { return 403; }
}

Проверка и применение

Перед применением всегда проверяйте конфигурацию:

sudo nginx -t
sudo systemctl reload nginx
curl -I https://app.example.com

Для проверки SSL используйте SSL Labs — цельтесь в оценку A+.

Заключение

Балансировка нагрузки, кеширование, rate limiting и заголовки безопасности — это не опциональные «улучшения», а необходимый минимум для продакшена. Начните с заголовков безопасности и TLS, затем добавьте rate limiting на критичные эндпоинты, а кеширование и балансировку подключайте по мере роста нагрузки. И главное правило: после каждого изменения — nginx -t и reload, никогда не restart.

© 2026 Terminal Notes. Built with SvelteKit.