LEARN X · ЗА 14 МИН

Nginx config

Весь синтаксис конфигов Nginx на одной странице: контексты, location, проксирование, upstream, SSL, gzip, редиректы, логи и лимиты.

Nginx читает конфиг сверху вниз и собирает его из директив (одна строка = одна настройка) и блоков в фигурных скобках (контексты). Ниже — весь синтаксис на одной странице: всё объяснено прямо в комментариях рабочих конфигов.

Структура конфига

Конфиг состоит из директив и блоков. Главный файл обычно — /etc/nginx/nginx.conf.

# Это комментарий — начинается с решётки, до конца строки

# Простая директива: имя, значения через пробел, точка с запятой в конце
worker_processes auto;        # auto = по числу ядер CPU

# Блочная директива (контекст): имя + фигурные скобки
events {
    worker_connections 1024;  # макс. соединений на один worker
}

# Директивы наследуются внутрь вложенных блоков и могут переопределяться
http {
    sendfile on;              # действует во всех server/location ниже
}

# Подключение других файлов (шаблон с * допустим)
include /etc/nginx/conf.d/*.conf;

Контексты (иерархия)

Контексты вложены друг в друга: main → events → http → server → location. Где директива стоит — там и действует.

# main — самый верхний уровень (без обёртки), глобальные настройки
user www-data;                # от какого пользователя работают worker'ы
worker_processes auto;
error_log /var/log/nginx/error.log warn;  # глобальный лог ошибок

events {                      # настройки обработки соединений
    worker_connections 1024;
}

http {                        # всё, что касается HTTP/HTTPS
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    server {                  # один виртуальный хост
        listen 80;
        server_name example.com;

        location / {          # правило для группы URL
            root /var/www/html;
        }
    }
}
# Контекст stream { ... } (TCP/UDP-прокси) — отдельная история, тут не трогаем

Виртуальные хосты

Каждый server — отдельный сайт. Nginx выбирает его по паре listen + server_name.

# Сайт №1
server {
    listen 80;                      # слушаем 80-й порт (HTTP)
    server_name example.com www.example.com;  # несколько имён через пробел
    root /var/www/example;
}

# Сайт №2 на том же порту — различаются по server_name
server {
    listen 80;
    server_name blog.example.com;   # точное имя
    # server_name *.example.com;    # маска: любой поддомен
    # server_name ~^www\d+\.;       # регексп (начинается с ~)
    root /var/www/blog;
}

# Дефолтный сервер: ответит, если ни один server_name не подошёл
server {
    listen 80 default_server;       # пометка default_server
    server_name _;                  # _ = заглушка, не валидное имя
    return 444;                     # закрыть соединение без ответа
}

Раздача статики

Связка root, index и try_files — основа отдачи файлов с диска.

server {
    listen 80;
    server_name static.example.com;

    root /var/www/site;     # корень: URL /img/a.png ищется в /var/www/site/img/a.png
    index index.html index.htm;  # что отдавать при запросе каталога /

    location /downloads/ {
        # alias ЗАМЕНЯЕТ часть пути (в отличие от root, который ДОБАВЛЯЕТ)
        # URL /downloads/file.zip -> /srv/files/file.zip
        alias /srv/files/;
    }

    location / {
        # пробуем по очереди: файл -> каталог -> запасной вариант
        # $uri — текущий путь запроса
        try_files $uri $uri/ =404;   # если ничего нет — отдать 404
    }
}

Location-блоки и приоритеты

Модификатор перед путём задаёт тип совпадения. Это самая частая путаница в Nginx.

server {
    listen 80;
    server_name example.com;
    root /var/www/site;

    # 1) Точное совпадение — высший приоритет, проверяется первым
    location = /favicon.ico {
        log_not_found off;
    }

    # 2) Префикс с ^~ — если совпал, регекспы дальше НЕ проверяются
    location ^~ /assets/ {
        expires 30d;
    }

    # 3) Регексп с учётом регистра (~) — проверяется по порядку в файле
    location ~ \.(jpg|png|gif)$ {
        expires 7d;
    }

    # 4) Регексп без учёта регистра (~*)
    location ~* \.(css|js)$ {
        expires 1d;
    }

    # 5) Обычный префикс — самый низкий приоритет, выбирается самый длинный
    location / {
        try_files $uri $uri/ =404;
    }
}
# Итог приоритетов: =  >  ^~  >  ~ / ~* (по порядку)  >  обычный префикс

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

Главная задача для бэкенда: proxy_pass перебрасывает запрос на приложение, а proxy_set_header передаёт ему правильные заголовки.

# Группа бэкендов (можно указать и один сервер)
upstream backend {
    server 127.0.0.1:8000;   # приложение на локальном порту
}

server {
    listen 80;
    server_name api.example.com;

    location / {
        proxy_pass http://backend;          # куда перенаправить запрос

        # Передаём бэкенду реальные данные клиента
        proxy_set_header Host $host;             # исходный домен
        proxy_set_header X-Real-IP $remote_addr; # IP клиента
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;  # http или https

        proxy_read_timeout 60s;   # сколько ждать ответа бэкенда
    }

    # WebSocket-проксирование требует апгрейда соединения
    location /ws/ {
        proxy_pass http://backend;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

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

Несколько server внутри upstream — и Nginx распределяет запросы между ними.

upstream app_cluster {
    # Метод балансировки (по умолчанию round-robin — по кругу)
    # least_conn;            # отдать туда, где меньше активных соединений
    # ip_hash;               # клиент всегда на один и тот же сервер (по IP)

    server 10.0.0.1:8000 weight=3;   # вес 3: получит втрое больше запросов
    server 10.0.0.2:8000;            # вес 1 по умолчанию
    server 10.0.0.3:8000 backup;     # резервный: только если упали основные
    server 10.0.0.4:8000 down;       # помечен выключенным

    # max_fails / fail_timeout — когда считать сервер сломанным
    # server 10.0.0.5:8000 max_fails=3 fail_timeout=30s;
}

server {
    listen 80;
    server_name example.com;
    location / {
        proxy_pass http://app_cluster;
    }
}

HTTPS / SSL

Слушаем 443-й порт с флагом ssl и указываем сертификат с ключом.

server {
    listen 443 ssl;            # HTTPS-порт с включённым SSL
    listen [::]:443 ssl;       # то же для IPv6
    http2 on;                  # включить HTTP/2 (быстрее)
    server_name example.com;

    # Файлы сертификата (обычно от Let's Encrypt)
    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_prefer_server_ciphers off;

    root /var/www/site;
}

# Отдельный server: весь HTTP-трафик гоним на HTTPS
server {
    listen 80;
    server_name example.com;
    return 301 https://$host$request_uri;   # вечный редирект на https
}

Редиректы

Два инструмента: быстрый return и гибкий rewrite с регекспами.

server {
    listen 80;
    server_name old.example.com www.example.com;

    # return — самый простой и быстрый способ
    # 301 — постоянный редирект, 302 — временный
    return 301 https://example.com$request_uri;
}

server {
    listen 80;
    server_name example.com;

    # rewrite — переписывает URL по регекспу
    # $1 — захваченная группа из скобок (...)
    rewrite ^/old-blog/(.*)$ /blog/$1 permanent;  # permanent = 301

    # флаги rewrite:
    #   last      — остановить и заново подобрать location
    #   break     — остановить, остаться в текущем location
    #   redirect  — внешний 302
    #   permanent — внешний 301
    location /articles/ {
        rewrite ^/articles/(\d+)$ /post?id=$1 last;
    }
}

Заголовки

add_header добавляет HTTP-заголовки в ответ — обычно для безопасности и кэша.

server {
    listen 443 ssl;
    server_name example.com;
    root /var/www/site;

    # Заголовки безопасности
    add_header X-Frame-Options "SAMEORIGIN";          # защита от кликджекинга
    add_header X-Content-Type-Options "nosniff";      # не угадывать MIME-тип
    add_header Referrer-Policy "no-referrer-when-downgrade";

    # HSTS: браузер обязан ходить только по HTTPS (год)
    # always — добавлять даже к ответам с ошибками (4xx/5xx)
    add_header Strict-Transport-Security "max-age=31536000" always;

    location /api/ {
        # CORS: разрешить запросы с другого домена
        add_header Access-Control-Allow-Origin "*";
        proxy_pass http://127.0.0.1:8000;
    }
}

Gzip и кэширование

gzip сжимает ответы, expires велит браузеру держать файлы в кэше.

http {
    # Сжатие текстовых ответов
    gzip on;
    gzip_comp_level 5;          # уровень 1..9 (баланс CPU/размер)
    gzip_min_length 256;        # не сжимать совсем мелочь
    gzip_types text/plain text/css application/json
               application/javascript text/xml;  # какие типы сжимать
    # gzip_vary on;             # добавить заголовок Vary: Accept-Encoding

    server {
        listen 80;
        server_name example.com;
        root /var/www/site;

        # Долгий кэш для статики с хэшем в имени
        location ~* \.(css|js|png|jpg|woff2)$ {
            expires 1y;                       # держать год
            add_header Cache-Control "public, immutable";
        }

        # HTML не кэшируем — должен обновляться сразу
        location = /index.html {
            expires -1;                       # запретить кэш
        }
    }
}

Логи

Два журнала: access_log (все запросы) и error_log (ошибки и отладка).

http {
    # Свой формат строки лога (имя main используем ниже)
    log_format main '$remote_addr - $remote_user [$time_local] '
                    '"$request" $status $body_bytes_sent '
                    '"$http_referer" "$http_user_agent"';

    access_log /var/log/nginx/access.log main;  # файл + формат
    error_log  /var/log/nginx/error.log warn;   # уровень: debug<info<warn<error

    server {
        listen 80;
        server_name example.com;

        # Можно переопределить лог для конкретного сайта/location
        access_log /var/log/nginx/example.access.log main;

        location /health {
            access_log off;     # не засорять лог health-check'ами
            return 200 "ok";
        }
    }
}

Ограничения

Защита от перегрузки и больших запросов: лимит частоты и размера тела.

http {
    # Зона лимита частоты: 10 МБ памяти, не больше 10 запросов/сек с одного IP
    # $binary_remote_addr — IP клиента в компактном виде
    limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;

    server {
        listen 80;
        server_name example.com;

        # Максимальный размер тела запроса (для загрузки файлов)
        client_max_body_size 20m;   # больше 20 МБ -> ошибка 413

        location /login {
            # Применяем лимит; burst=20 — допустимый всплеск очереди
            # nodelay — отдавать всплеск сразу, не растягивая
            limit_req zone=mylimit burst=20 nodelay;
            proxy_pass http://127.0.0.1:8000;
        }

        # Ограничение числа одновременных соединений с IP делается через
        # limit_conn_zone + limit_conn (аналогично limit_req)
    }
}

Типичный конфиг: SPA + API

Боевой шаблон: фронтенд-SPA отдаётся как статика, а запросы /api/ уходят на бэкенд.

upstream api_backend {
    server 127.0.0.1:8000;
}

# HTTP -> HTTPS
server {
    listen 80;
    server_name app.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    http2 on;
    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;

    root /var/www/app/dist;     # собранный фронтенд
    index index.html;

    client_max_body_size 25m;
    gzip on;
    gzip_types text/css application/javascript application/json;

    # API уходит на бэкенд
    location /api/ {
        proxy_pass http://api_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;
    }

    # Статика с хэшем — кэшируем надолго
    location /assets/ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }

    # Главное правило SPA: любой неизвестный путь отдаёт index.html,
    # чтобы клиентский роутер сам разобрал URL
    location / {
        try_files $uri $uri/ /index.html;
    }
}
Поддержать проект