Настройка Nginx как обратного прокси

9 мин чтения·Matthieu|

Настройте Nginx как reverse proxy для Node.js, Ollama и других бэкендов. Разбираем proxy_pass, проброс заголовков, WebSocket, upstream-балансировку и таймауты для продакшена с проверкой на каждом шагу.

Обратный прокси (reverse proxy) стоит между клиентами и вашим бэкенд-приложением. Nginx принимает входящие запросы, пересылает их на бэкенд-сервер (Node.js, Python, Go, Ollama) и возвращает ответ клиенту. Это позволяет добавить TLS-терминацию, балансировку нагрузки, кеширование и контроль доступа без изменения приложения.

В этом руководстве разбираем настройку reverse proxy в Nginx: от базового proxy_pass до WebSocket-проксирования, upstream-балансировки и продакшен-конфигурации для Ollama.

Необходимые условия

Перед началом вам понадобится:

  • VPS на Debian 12 или Ubuntu 24.04 с root или sudo-доступом
  • Установленный и запущенный Nginx (Установка Nginx на Debian и Ubuntu)
  • Бэкенд-приложение, слушающее локальный порт (в руководстве есть тестовые примеры)
  • Базовое понимание структуры конфигурационных файлов Nginx (Структура конфиг-файлов Nginx)

Убедитесь, что Nginx запущен:

sudo systemctl status nginx

В выводе должно быть active (running).

Как настроить proxy_pass в Nginx?

Директива proxy_pass указывает, куда Nginx должен пересылать запросы. Она размещается внутри блока location, который находится в блоке server. Nginx отправляет запрос клиента на указанный URL бэкенда и передает ответ обратно. Директива принимает URL по HTTP и HTTPS, может указывать на IP-адрес, доменное имя или Unix-сокет.

Создайте новый файл серверного блока:

sudo nano /etc/nginx/sites-available/app.conf

Добавьте минимальную конфигурацию reverse proxy:

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

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

Включите сайт и проверьте конфигурацию:

sudo ln -s /etc/nginx/sites-available/app.conf /etc/nginx/sites-enabled/
sudo nginx -t

Если nginx -t вернул syntax is ok и test is successful, перезагрузите:

sudo systemctl reload nginx

Проверьте работу прокси:

curl -I http://app.example.com

В заголовках ответа должны быть данные от вашего бэкенда. Если видите 502 Bad Gateway, значит бэкенд не запущен на порту 3000.

Что происходит с trailing slash в proxy_pass?

Завершающий слеш (trailing slash) в proxy_pass управляет перезаписью URI. Это одна из самых частых причин ошибок в конфигурации.

Конфигурация Запрос Передается на бэкенд
proxy_pass http://127.0.0.1:3000 /app/users http://127.0.0.1:3000/app/users
proxy_pass http://127.0.0.1:3000/ /app/users http://127.0.0.1:3000/users
proxy_pass http://127.0.0.1:3000/v2/ /app/users http://127.0.0.1:3000/v2/users
proxy_pass http://127.0.0.1:3000/v2 /app/users http://127.0.0.1:3000/v2users

Правило: когда proxy_pass содержит URI (что угодно после host:port, даже просто /), Nginx отрезает совпадающий префикс location из URI запроса и добавляет остаток к URI в proxy_pass. Когда URI в proxy_pass нет, исходный путь запроса передается без изменений.

Для блока location /app/ применимы примеры выше. Обратите внимание на четвертую строку: без trailing slash после /v2 путь становится /v2users вместо /v2/users. Всегда добавляйте завершающий слеш, когда указываете путь.

Какие заголовки нужно пробрасывать на бэкенд?

Без явной настройки заголовков бэкенд не видит реальный IP клиента, исходный протокол и запрошенное имя хоста. Nginx подменяет их по умолчанию. Пробрасывайте вручную.

Заголовок Значение Назначение
Host $host Сохраняет оригинальный заголовок Host, чтобы бэкенд знал, какой домен запрошен
X-Real-IP $remote_addr Передает IP-адрес клиента на бэкенд
X-Forwarded-For $proxy_add_x_forwarded_for Добавляет IP клиента в цепочку прокси
X-Forwarded-Proto $scheme Сообщает бэкенду, использовался HTTP или HTTPS

Добавьте заголовки в блок location:

location / {
    proxy_pass http://127.0.0.1:3000;
    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;
}

После перезагрузки Nginx проверьте, что заголовки доходят до бэкенда. Если приложение логирует входящие заголовки, посмотрите:

sudo systemctl reload nginx
curl -s http://app.example.com/headers

Бэкенд должен видеть X-Real-IP с IP клиента, а не 127.0.0.1. Если показывает 127.0.0.1, директивы proxy_set_header отсутствуют или указаны в неправильном контексте.

Про безопасность: используйте $proxy_add_x_forwarded_for вместо $remote_addr для X-Forwarded-For. Он добавляет IP клиента к существующему заголовку X-Forwarded-For. Если поставить только $remote_addr, вы потеряете цепочку прокси, что сломает отслеживание IP за несколькими прокси. Если Nginx -- единственный прокси, оба варианта эквивалентны.

Как проксировать приложение на Node.js?

Вот полный серверный блок для проксирования Node.js-приложения на порту 3000. Пример включает проброс заголовков, поддержку WebSocket и скрытие версии.

Создайте серверный блок:

sudo nano /etc/nginx/sites-available/nodeapp.conf
server {
    listen 80;
    server_name nodeapp.example.com;

    # Hide Nginx version in responses
    server_tokens off;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;

        # Header forwarding
        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;

        # WebSocket support
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        # Timeouts
        proxy_connect_timeout 60s;
        proxy_read_timeout 60s;
        proxy_send_timeout 60s;
    }
}

Включите, протестируйте и перезагрузите:

sudo ln -s /etc/nginx/sites-available/nodeapp.conf /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

Проверьте работу прокси:

curl -I http://nodeapp.example.com

Обратите внимание: в ответе не должно быть строки Server: nginx/1.x.x. Директива server_tokens off скрывает номер версии. Раскрытие версии помогает атакующим нацеливаться на известные уязвимости.

Для TLS-терминации с Let's Encrypt смотрите Nginx SSL/TLS с Let's Encrypt.

Как проксировать WebSocket-соединения через Nginx?

WebSocket-соединения начинаются как HTTP-запрос с заголовком Upgrade, затем переключаются на постоянное двунаправленное соединение. По умолчанию Nginx использует HTTP/1.0 для upstream-соединений, что не поддерживает механизм Upgrade. Нужны три директивы: proxy_http_version в 1.1, проброс заголовка Upgrade и установка Connection в "upgrade".

Для location-блоков, обслуживающих и обычный HTTP, и WebSocket-трафик, используйте директиву map для условной установки заголовка Connection. Добавьте в блок http в /etc/nginx/nginx.conf:

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

Затем используйте $connection_upgrade в серверном блоке:

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

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        # Prevent idle timeout killing WebSocket connections
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
    }
}

По умолчанию proxy_read_timeout равен 60 секундам. Если за это время данные не проходят, Nginx закрывает соединение. Для WebSocket ставьте значение выше. Ваше приложение должно отправлять WebSocket ping-фреймы с интервалом меньше этого таймаута.

Проверьте конфигурацию:

sudo nginx -t && sudo systemctl reload nginx

Для проверки WebSocket-подключения установите wscat:

npm install -g wscat
wscat -c ws://ws.example.com/

Если соединение открылось, WebSocket-проксирование работает. Если получили unexpected server response (200), заголовки Upgrade не пробрасываются. Проверьте, что директива map находится внутри блока http, а не внутри блока server.

Как настроить reverse proxy для Ollama?

Ollama по умолчанию обслуживает LLM-инференс на порту 11434 и слушает только 127.0.0.1. Проксирование через Nginx позволяет добавить TLS, аутентификацию и контроль доступа без изменения конфигурации Ollama. Главное отличие от стандартного проксирования: инференс LLM может занимать минуты, а для стриминговых ответов нужно отключить буферизацию.

Создайте серверный блок:

sudo nano /etc/nginx/sites-available/ollama.conf
server {
    listen 80;
    server_name ollama.example.com;

    server_tokens off;

    # Restrict access to specific IPs
    allow 192.168.1.0/24;
    allow 10.0.0.0/8;
    deny all;

    location / {
        proxy_pass http://127.0.0.1:11434;
        proxy_http_version 1.1;

        # Header forwarding
        proxy_set_header Host localhost:11434;
        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 Connection '';

        # Disable buffering for streaming responses
        proxy_buffering off;
        proxy_cache off;
        chunked_transfer_encoding on;

        # Extended timeouts for LLM inference
        proxy_connect_timeout 300s;
        proxy_read_timeout 600s;
        proxy_send_timeout 600s;
    }
}

Включите и проверьте:

sudo ln -s /etc/nginx/sites-available/ollama.conf /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

Убедитесь, что Ollama отвечает через прокси:

curl http://ollama.example.com/api/tags

Должен вернуться JSON со списком доступных моделей. Если получили 403 Forbidden, IP клиента не в списке allow. Если 502 Bad Gateway, Ollama не запущен:

sudo systemctl status ollama

Протестируйте стриминговую генерацию:

curl -N http://ollama.example.com/api/generate -d '{
  "model": "llama3.2",
  "prompt": "Hello",
  "stream": true
}'

Обратите внимание: флаг -N отключает буферизацию вывода curl. Токены должны приходить по одному. Если весь ответ приходит разом, proxy_buffering off не установлен или перезаписан.

Почему эти настройки отличаются от стандартного прокси:

  • proxy_buffering off: обычно Nginx буферизует ответы бэкенда и отправляет их пакетом. Для LLM-стриминга каждый токен должен отправляться клиенту немедленно.
  • proxy_read_timeout 600s: инференс на больших моделях может занимать несколько минут. Дефолтный таймаут в 60 секунд оборвет соединение посреди генерации.
  • proxy_set_header Host localhost:11434: Ollama проверяет заголовок Host и отклоняет запросы, не совпадающие с его bind-адресом.
  • proxy_set_header Connection '': очищает заголовок Connection, чтобы предотвратить проблемы с keep-alive при chunked-стриминге.

Для TLS-терминации и безопасного доступа к Ollama через интернет смотрите Nginx SSL/TLS с Let's Encrypt. Никогда не открывайте Ollama без контроля доступа. Для продакшена комбинируйте IP-ограничения с HTTP Basic Auth или валидацией API-ключей.

Чтобы добавить HTTP Basic Auth поверх IP-ограничений:

sudo apt install apache2-utils
sudo htpasswd -c /etc/nginx/.ollama_htpasswd apiuser
sudo chmod 640 /etc/nginx/.ollama_htpasswd
sudo chown root:www-data /etc/nginx/.ollama_htpasswd

Затем добавьте в блок location в ollama.conf:

    auth_basic "Ollama API";
    auth_basic_user_file /etc/nginx/.ollama_htpasswd;

Проверьте права доступа к файлу:

ls -la /etc/nginx/.ollama_htpasswd

Файл должен показывать -rw-r----- с владельцем root и группой www-data. Ограничение прав не дает другим пользователям на сервере читать хеши паролей.

Как настроить upstream-балансировку нагрузки?

Блок upstream определяет группу бэкенд-серверов, между которыми Nginx распределяет запросы. По умолчанию используется взвешенный round-robin. Каждый сервер получает долю запросов пропорционально своему весу (вес по умолчанию: 1).

Добавьте блок upstream перед блоком server:

upstream app_backends {
    server 127.0.0.1:3000;
    server 127.0.0.1:3001;
    server 127.0.0.1:3002;
}

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

    location / {
        proxy_pass http://app_backends;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

Методы балансировки

Round-robin (по умолчанию): запросы распределяются равномерно между серверами. Дополнительная директива не нужна.

Least connections (наименьшее количество соединений): запросы идут на сервер с наименьшим числом активных соединений. Лучше подходит для бэкендов с разным временем ответа:

upstream app_backends {
    least_conn;
    server 127.0.0.1:3000;
    server 127.0.0.1:3001;
}

IP hash: привязывает каждый IP клиента к конкретному бэкенду. Полезно для сохранения сессий без sticky cookies:

upstream app_backends {
    ip_hash;
    server 127.0.0.1:3000;
    server 127.0.0.1:3001;
}

Проверки состояния: max_fails и fail_timeout

Nginx пассивно следит за здоровьем бэкендов. Если сервер не отвечает, Nginx помечает его как недоступный и прекращает отправку запросов на определенный период.

upstream app_backends {
    server 127.0.0.1:3000 max_fails=3 fail_timeout=30s;
    server 127.0.0.1:3001 max_fails=3 fail_timeout=30s;
    server 127.0.0.1:3002 backup;
}
Параметр По умолчанию Описание
max_fails 1 Количество неудачных попыток за fail_timeout до пометки сервера как недоступного
fail_timeout 10s Окно подсчета ошибок и период недоступности сервера
backup - Сервер получает запросы только когда все основные серверы недоступны
weight 1 Относительная доля запросов при round-robin

После настройки upstream протестируйте и перезагрузите:

sudo nginx -t && sudo systemctl reload nginx

Проверьте работу балансировки, отправив несколько запросов:

for i in $(seq 1 6); do curl -s http://app.example.com/health; echo; done

Если бэкенды возвращают идентификатор в ответе, вы увидите распределение запросов между ними.

Как настроить буферизацию и таймауты прокси?

По умолчанию Nginx буферизует ответы бэкенда. Он считывает весь ответ в память (или на диск, если превышен буфер) и затем отправляет клиенту. Это эффективно для большинства приложений, но не подходит для стриминга, Server-Sent Events (SSE) или long-polling.

Директивы таймаутов

Директива По умолчанию Рекомендуется Назначение
proxy_connect_timeout 60s 5-10s Время установки соединения с бэкендом. Ставьте короткое для быстрого отказа.
proxy_read_timeout 60s 60-300s Ожидание ответа от бэкенда. Увеличьте для медленных API.
proxy_send_timeout 60s 60s Время отправки тела запроса на бэкенд. Увеличьте для больших загрузок.
location / {
    proxy_pass http://127.0.0.1:3000;

    proxy_connect_timeout 10s;
    proxy_read_timeout 120s;
    proxy_send_timeout 60s;
}

Эти таймауты действуют между двумя последовательными операциями чтения/записи, а не на весь запрос целиком. Ответ, отправляющий данные каждые 30 секунд, никогда не вызовет срабатывание 60-секундного proxy_read_timeout.

Управление буферизацией

Директива По умолчанию Описание
proxy_buffering on Буферизовать полный ответ бэкенда перед отправкой клиенту
proxy_buffer_size 4k или 8k Буфер для первой части ответа (заголовки)
proxy_buffers 8 4k или 8 8k Количество и размер буферов для тела ответа
proxy_busy_buffers_size 8k или 16k Максимальный размер буферов, занятых отправкой клиенту

Когда отключать буферизацию?

Отключайте буферизацию, когда бэкенд стримит данные: LLM-инференс (Ollama, vLLM), Server-Sent Events, длинные WebSocket-подобные ответы или любое API с инкрементальной отправкой chunked-данных.

location /stream/ {
    proxy_pass http://127.0.0.1:8080;
    proxy_buffering off;
}

Оставляйте буферизацию включенной для обычных запросов-ответов. Буферизация защищает бэкенд от медленных клиентов: Nginx быстро забирает ответ и отдает клиенту в его темпе. Без буферизации медленный клиент держит соединение с бэкендом открытым.

Для продвинутой оптимизации производительности смотрите .

Как диагностировать ошибки 502 и 504?

502 Bad Gateway означает, что Nginx не смог подключиться к бэкенду или бэкенд вернул некорректный ответ. 504 Gateway Timeout означает, что бэкенд не ответил за время proxy_read_timeout.

Чеклист для 502 Bad Gateway

  1. Бэкенд запущен?
ss -tlnp | grep 3000

Если вывод пустой, бэкенд не слушает порт 3000. Запустите его.

  1. URL в proxy_pass правильный? Проверьте опечатки в номере порта или IP-адресе. Частая ошибка: использование https:// для бэкенда, который работает только по HTTP.

  2. SELinux блокирует соединение? (RHEL/CentOS)

sudo setsebool -P httpd_can_network_connect 1
  1. Проверьте лог ошибок Nginx:
sudo tail -20 /var/log/nginx/error.log

Ищите connect() failed (111: Connection refused) или no live upstreams.

Чеклист для 504 Gateway Timeout

  1. Увеличьте proxy_read_timeout:
proxy_read_timeout 300s;
  1. Бэкенд работает медленно?
time curl http://127.0.0.1:3000/slow-endpoint

Если запрос длится дольше proxy_read_timeout, увеличьте таймаут или оптимизируйте бэкенд.

  1. Проверьте состояние upstream: если используются upstream-блоки, все серверы могут быть помечены как недоступные. Поищите в логе ошибок no live upstreams while connecting to upstream.

Чтение логов Nginx

Nginx пишет ошибки в /var/log/nginx/error.log. Для мониторинга в реальном времени:

sudo journalctl -u nginx -f

Или смотрите лог напрямую:

sudo tail -f /var/log/nginx/error.log

Для отдельных логов по сайтам добавьте директивы access_log и error_log в серверный блок:

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

    access_log /var/log/nginx/app-access.log;
    error_log /var/log/nginx/app-error.log;

    location / {
        proxy_pass http://127.0.0.1:3000;
    }
}

Это разделяет логи по приложениям и упрощает отладку проблем конкретного бэкенда.

Частые проблемы с заголовками

Если бэкенд получает 127.0.0.1 вместо реального IP клиента, не хватает директивы proxy_set_header X-Real-IP $remote_addr. Если приложение генерирует ссылки с http:// вместо https://, заголовок X-Forwarded-Proto не пробрасывается. Некоторые фреймворки (Express, Django, Rails) требуют явной настройки для доверия прокси-заголовкам. В Express:

app.set('trust proxy', 1);

В Django установите SECURE_PROXY_SSL_HEADER:

SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')

Без этих настроек приложение игнорирует проброшенные заголовки, даже если Nginx отправляет их корректно.

Справочник: директивы проксирования

Для быстрого обращения -- минимальный набор директив для разных сценариев проксирования:

# Standard HTTP proxy
location / {
    proxy_pass http://127.0.0.1:3000;
    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;
}

# WebSocket proxy
location /ws/ {
    proxy_pass http://127.0.0.1:8080;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
    proxy_read_timeout 3600s;
}

# Streaming proxy (SSE, LLM)
location /stream/ {
    proxy_pass http://127.0.0.1:11434;
    proxy_http_version 1.1;
    proxy_buffering off;
    proxy_cache off;
    proxy_read_timeout 600s;
}

Для серверных блоков с несколькими доменами смотрите Серверные блоки Nginx. Для усиления безопасности, включая rate limiting и контроль доступа, смотрите .


Copyright 2026 Virtua.Cloud. Vse prava zashchishcheny.

Готовы попробовать?

Разверните свой сервер за секунды. Linux, Windows или FreeBSD.

Смотреть тарифы VPS
Настройка Nginx как обратного прокси (2026)