Що таке reverse proxy у Nginx і навіщо він потрібен
Reverse proxy — сервер, який приймає запити від клієнтів і передає їх на один або декілька бекендів: застосунок на Node.js, Python, Java чи інший інстанс Nginx. Клієнт звертається лише до проксі і не бачить внутрішню структуру сервісу. Nginx уміє працювати як reverse proxy з коробки — для цього достатньо директиви proxy_pass усередині блоку location.
Reverse proxy на Nginx вирішує декілька задач одразу: термінує TLS перед застосунком, віддає статику напряму в обхід повільного бекенду, приховує внутрішні порти і IP-адреси, додає заголовки безпеки. На VDS ZevsHost.net таку схему часто ставлять перед Node.js чи Python-застосунком: Nginx слухає порт 80 і 443, а сам сервіс працює на localhost і назовні не дивиться.
- Балансування навантаження між декількома копіями застосунку.
- Єдиний SSL-сертифікат на весь набір внутрішніх сервісів.
- Кешування відповідей бекенду без зміни коду застосунку.
Налаштування proxy_pass: базовий приклад
Мінімальний конфіг — блок server з одним location, який пересилає всі запити на локальний застосунок по HTTP.
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
}
}
Якщо location заданий із завершальним слешем, а в proxy_pass вказано шлях, Nginx замінює частину URI, що збіглася, на цей шлях. Без шляху в proxy_pass вихідний URI передається бекенду без змін — це найпередбачуваніший варіант, особливо коли застосунок сам розбирає маршрути. Детальніше про правила збігу шляхів — у статті про пріоритет location.
Передача заголовків клієнта на бекенд
За замовчуванням Nginx замінює заголовок Host на адресу бекенду і не повідомляє застосунку реальний IP відвідувача. Якщо це не виправити, у логах застосунку буде видно лише IP самого Nginx, а посилання в листах і редиректах можуть вказувати на localhost замість справжнього домену.
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, інакше клієнт зможе підробити X-Forwarded-For напряму.
Таймаути і буфери: за що відповідає кожен параметр
Якщо бекенд відповідає повільно або віддає велику відповідь, стандартні таймаути і буфери Nginx можуть обірвати з'єднання зарано. Значення нижче — робоча точка відліку для звичайного веб-застосунку, не для стрімінгу.
| Параметр | Призначення | Типове значення |
|---|---|---|
| proxy_connect_timeout | очікування встановлення TCP-з'єднання з бекендом | 5s |
| proxy_send_timeout | час між записами даних на бекенд | 60s |
| proxy_read_timeout | час між читаннями відповіді від бекенду | 60s |
| proxy_buffer_size | буфер для першої частини відповіді (заголовки) | 4k |
| proxy_buffers | кількість і розмір буферів для тіла відповіді | 8 4k |
Занадто короткий proxy_read_timeout — часта причина 504 Gateway Timeout на важких запитах на кшталт вивантаження звіту чи завантаження файлу. Замалі буфери змушують Nginx писати тимчасові файли на диск, що помітно на VDS з повільним диском.
Reverse proxy до декількох серверів через upstream
Якщо застосунок працює в декількох копіях, замість однієї адреси в proxy_pass використовують блок upstream зі списком серверів.
upstream backend {
server 127.0.0.1:3000;
server 127.0.0.1:3001;
server 127.0.0.1:3002 backup;
}
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://backend;
}
}
Nginx за замовчуванням розподіляє запити по колу (round robin) і пропускає сервер, позначений backup, доки решта живі. Про алгоритми розподілу навантаження і перевірку стану бекендів — в окремій статті про балансувальник Nginx. Якщо перед reverse proxy потрібен ще й HTTPS, сертифікат налаштовується на зовнішньому сервері — див. статтю про SSL/TLS у Nginx.
Перелік перевірок: reverse proxy перед продакшеном
- Заголовки Host, X-Real-IP і X-Forwarded-For передані бекенду і видимі в його логах.
- Команда
nginx -tпроходить без помилок після кожної правки конфігу. - Таймаути підібрані під реальний час відповіді застосунку, а не залишені за замовчуванням.
- При декількох бекендах в upstream налаштований хоча б один запасний сервер.
- Відповідь перевірена командою
curl -I http://app.example.com/— заголовки і код статусу збігаються з очікуваними.