Пошук уроків, статей та іншого контенту
Чому реальні застосунки майже завжди стоять за Nginx, а не приймають трафік напряму — і що саме робить reverse proxy.
Коли користувач відкриває сайт, його браузер надсилає HTTP-запит на сервер. Найпростіша архітектура виглядає так:
Браузер → застосунокНаприклад, Node.js-застосунок може слухати порт 3000, Django — 8000, а Go-сервіс — 8080. Технічно користувач може звертатися до такого сервісу напряму:
https://example.com:3000Але в реальних системах зазвичай використовують іншу схему:
Браузер → Nginx → застосунокУ цьому випадку Nginx є reverse proxy, тобто зворотним проксі-сервером.
Він приймає запит від клієнта, вирішує, куди його передати, отримує відповідь від внутрішнього сервісу та повертає її клієнту.
Користувач при цьому не знає:
на якому порту працює застосунок;
на якій внутрішній IP-адресі він запущений;
чи є за Nginx один сервіс, чи кілька;
як саме побудована внутрішня інфраструктура.
Щоб зрозуміти різницю, варто порівняти два типи проксі.
Forward proxy працює від імені клієнта:
Клієнт → Forward proxy → ІнтернетНаприклад, у корпоративній мережі всі комп’ютери можуть виходити в інтернет через один проксі-сервер. Цей сервер приховує клієнтів від зовнішніх ресурсів і може контролювати їхні запити.
Reverse proxy працює від імені сервера:
Клієнт → Reverse proxy → Внутрішній серверКлієнт думає, що спілкується безпосередньо із сайтом, але насправді запит спочатку потрапляє до Nginx.
Саме reverse proxy найчастіше використовують перед:
вебзастосунками;
API;
мікросервісами;
контейнерами;
кількома копіями одного сервісу.
Застосунок може самостійно приймати HTTP-запити, але це не означає, що він має бути безпосередньо доступним з інтернету.
Якщо назовні відкритий лише Nginx, внутрішній застосунок можна залишити доступним тільки через локальну мережу або localhost.
Наприклад:
Інтернет → Nginx:443 → застосунок:3000Порт 3000 не потрібно відкривати у фаєрволі. Зовнішній трафік потрапляє лише на порти 80 і 443.
Це зменшує кількість доступних з інтернету компонентів і спрощує контроль доступу.
Nginx може приймати HTTPS-з’єднання, працювати із сертифікатами та передавати запити до застосунку через звичайний HTTP у внутрішній мережі.
Клієнт ── HTTPS ──> Nginx ── HTTP ──> ЗастосунокЗастосунку не потрібно самостійно:
зберігати TLS-сертифікати;
оновлювати їх;
налаштовувати шифрування;
обробляти HTTP та HTTPS окремо.
У складніших або особливо чутливих системах HTTPS можуть використовувати і між Nginx та внутрішніми сервісами. Але завершення TLS на reverse proxy — поширений варіант.
У системі може бути кілька сервісів:
/api → API-сервіс
/admin → адміністративна панель
/ → фронтендДля користувача це один домен, але Nginx маршрутизує запити до різних внутрішніх компонентів.
Застосунок не обов’язково має віддавати CSS, JavaScript, зображення та шрифти самостійно.
Nginx добре підходить для роздавання статичних файлів і може робити це ефективніше, ніж універсальний application server.
Якщо одного екземпляра застосунку недостатньо, можна запустити кілька копій:
┌──> app-1:3000
Клієнт → Nginx ──┼──> app-2:3000
└──> app-3:3000Nginx розподіляє запити між ними. Це називається load balancing.
Розглянемо типовий сценарій.
Користувач відкриває:
https://example.com/productsВідбуваються такі кроки:
DNS визначає IP-адресу сервера.
Браузер встановлює HTTPS-з’єднання з Nginx.
Nginx приймає запит.
Nginx визначає відповідний блок конфігурації.
Запит передається внутрішньому застосунку.
Застосунок формує відповідь.
Nginx повертає відповідь браузеру.
Для клієнта весь процес виглядає як спілкування з одним сайтом.
Припустімо, застосунок працює на тому самому сервері та слухає localhost:3000.
Приклад конфігурації:
server {
listen 80;
server_name example.com;
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;
}
}Тут:
listen 80 — Nginx слухає HTTP-порт 80;
server_name — домен, для якого використовується конфігурація;
location / — правило для всіх шляхів;
proxy_pass — адреса внутрішнього застосунку;
proxy_set_header — HTTP-заголовки, які Nginx передає далі.
Після встановлення reverse proxy застосунок часто бачить не оригінальний запит клієнта, а запит від Nginx.
Без додаткових заголовків застосунок може вважати, що:
клієнт має IP-адресу 127.0.0.1;
запит був HTTP, навіть якщо користувач підключився через HTTPS;
домен запиту відрізняється від реального.
Саме тому передають:
Host — початковий домен;
X-Real-IP — IP-адресу клієнта;
X-Forwarded-For — ланцюжок проксі та IP клієнта;
X-Forwarded-Proto — початковий протокол, наприклад http або https.
Застосунок має бути правильно налаштований для довіри до цих заголовків. Не слід безумовно довіряти їм, якщо запити можуть приходити безпосередньо з недовірених джерел.
Nginx може направляти різні URL до різних застосунків.
server {
listen 80;
server_name example.com;
location /api/ {
proxy_pass http://127.0.0.1:8000;
# Передаємо інформацію про клієнта внутрішньому API
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 /admin/ {
proxy_pass http://127.0.0.1:9000;
# Зберігаємо початкові параметри запиту
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;
}
}Тепер:
https://example.com/api/users потрапить на сервіс із портом 8000;
https://example.com/admin/ потрапить на сервіс із портом 9000.
Особливості proxy_pass, зокрема наявність або відсутність завершального /, можуть впливати на те, який шлях отримає upstream-сервіс. Тому маршрутизацію за префіксами варто перевіряти на реальних URL, а не лише візуально читати конфігурацію.
Внутрішній сервіс, до якого Nginx передає запит, часто називають upstream.
Upstream можна описати окремо:
upstream app_servers {
server 127.0.0.1:3001;
server 127.0.0.1:3002;
server 127.0.0.1:3003;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://app_servers;
# Передаємо upstream інформацію про початковий запит
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 розподіляє запити між трьома серверами.
За замовчуванням використовується проста схема розподілу, але Nginx підтримує й інші підходи, зокрема:
weight — різна вага серверів;
backup — резервний сервер;
max_fails та fail_timeout — реакція на невдалі спроби;
least_conn — вибір сервера з найменшою кількістю активних з’єднань.
Балансування не замінює повноцінний моніторинг і перевірку працездатності застосунків. Сервер може приймати TCP-з’єднання, але повертати помилки на рівні програми.
Для статичних файлів reverse proxy не завжди потрібен. Nginx може віддавати їх безпосередньо:
server {
listen 80;
server_name example.com;
location /assets/ {
root /var/www/site;
}
location /api/ {
proxy_pass http://127.0.0.1:8000;
# Передаємо проксійованому сервісу дані клієнта
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}Якщо запитується /assets/app.css, Nginx шукатиме файл у каталозі, що відповідає налаштуванню root.
На практиці для статичних ресурсів також застосовують:
кешування в браузері;
стиснення;
правильні заголовки Cache-Control;
версіювання імен файлів.
Часто HTTP використовують лише для перенаправлення на HTTPS:
server {
listen 80;
server_name example.com www.example.com;
return 301 https://example.com$request_uri;
}Окремий серверний блок приймає HTTPS:
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/ssl/example/fullchain.pem;
ssl_certificate_key /etc/ssl/example/privkey.pem;
location / {
proxy_pass http://127.0.0.1:3000;
# Повідомляємо застосунку, що клієнт використовував HTTPS
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;
}
}У production-конфігурації налаштування TLS мають відповідати актуальним рекомендаціям і політикам безпеки. Самого факту наявності сертифіката недостатньо: важливі також термін його дії, ланцюжок сертифікації та коректне перенаправлення.
У контейнерному середовищі схема може виглядати так:
Інтернет → Nginx-контейнер → application-контейнерУ такому разі proxy_pass зазвичай посилається не на localhost, а на ім’я сервісу в Docker-мережі.
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://app: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;
}
}Важливо пам’ятати: localhost усередині контейнера означає саме цей контейнер, а не хост-машину і не інший контейнер.
Звичайні HTTP-запити мають короткий життєвий цикл: клієнт надсилає запит і отримує відповідь. WebSocket підтримує довготривале двонапрямлене з’єднання.
Для WebSocket Nginx потрібно налаштувати окремо:
location /socket/ {
proxy_pass http://127.0.0.1:3000;
# Передаємо службові заголовки для переходу на WebSocket
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Передаємо дані про початкове з'єднання
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}Без таких налаштувань HTTP API може працювати, а WebSocket-з’єднання — ні.
До довготривалих з’єднань також належать:
Server-Sent Events;
потокове передавання відповідей;
деякі довгі HTTP-запити.
Для них можуть знадобитися додаткові налаштування тайм-аутів і буферизації.
Nginx — це не лише маршрутизатор запитів. На його рівні часто реалізують:
обмеження кількості запитів;
базову фільтрацію небажаного трафіку;
перенаправлення;
кешування;
стиснення відповідей;
обмеження розміру завантажень;
додавання HTTP-заголовків безпеки;
доступ до окремих шляхів лише з певних IP;
логування запитів;
балансування між кількома upstream-серверами.
Проте Nginx не є автоматично повноцінним Web Application Firewall або захистом від усіх DDoS-атак. Для серйозних загроз потрібні додаткові рівні захисту та інфраструктура.
Спрощена схема вебзастосунку може виглядати так:
Користувач
│
▼
DNS
│
▼
Nginx
├── HTTPS
├── статичні файли
├── логування
└── балансування
│
├── application-1
├── application-2
└── application-3У більшій системі перед Nginx можуть бути CDN, хмарний балансувальник або зовнішній захист. Після Nginx — кілька сервісів, черги повідомлень і бази даних.
Важливо розуміти роль кожного компонента: reverse proxy не робить усю систему надійною сам по собі. Він лише створює контрольовану точку входу та бере на себе частину мережевих завдань.
localhost, але Nginx працює в іншому контейнеріУ цьому випадку localhost для Nginx і localhost для застосунку — різні середовища.
Потрібно використовувати адресу або DNS-ім’я, доступне в мережі контейнерів.
Якщо не налаштувати X-Forwarded-For або неправильно обробити його в застосунку, у логах можна бачити лише IP Nginx.
Водночас не можна безумовно довіряти цим заголовкам від будь-якого клієнта. Довірені проксі мають бути визначені явно.
Через це можуть виникати:
неправильні редиректи;
нескінченний цикл перенаправлень;
помилки secure-cookie;
неправильне формування абсолютних URL.
Перевірте X-Forwarded-Proto і налаштування довіри до reverse proxy у самому фреймворку.
Звичайний HTTP може працювати коректно, а WebSocket — завершуватися помилкою підключення. Перевірте Upgrade, Connection, версію HTTP та тайм-аути.
Nginx може відхиляти великі файли ще до того, як запит потрапить до застосунку. Причиною часто є недостатнє значення client_max_body_size.
proxy_passВідмінність між такими варіантами може бути важливою:
proxy_pass http://app;і:
proxy_pass http://app/;Поведінка шляху залежить від контексту location. Якщо API отримує неправильний URL, перш за все варто перевірити саме це правило.
Перед застосуванням змін конфігурацію потрібно перевірити:
nginx -tПісля успішної перевірки можна перезавантажити конфігурацію без повної зупинки сервера:
nginx -s reloadКонкретний спосіб керування також залежить від того, як Nginx запущений: через systemd, Docker або інший менеджер процесів.
Якщо reverse proxy налаштований без логів і метрик, складно зрозуміти:
де виникла затримка;
який upstream повертає помилку;
скільки запитів отримує сервер;
чи є тайм-аути;
які відповіді найчастіше мають статус 4xx або 5xx.
Логи Nginx та застосунку мають доповнювати одне одного.
Корисно перевіряти систему пошарово:
Чи вказує DNS на правильну IP-адресу.
Чи відкриті потрібні порти у фаєрволі.
Чи працює Nginx і чи успішна перевірка конфігурації.
Чи доступний upstream безпосередньо із середовища Nginx.
Чи збігаються очікувані шляхи в location і застосунку.
Чи передаються потрібні заголовки.
Чи не спрацьовують обмеження розміру або тайм-ауту.
Чи є помилка в access- або error-логах.
Для локальної перевірки можна виконати запит до внутрішнього сервісу:
curl -i http://127.0.0.1:3000/healthА потім перевірити той самий endpoint через Nginx:
curl -i http://example.com/healthЯкщо перший запит працює, а другий — ні, проблема, найімовірніше, у Nginx, мережі, маршрутизації або заголовках.
Reverse proxy — це сервер-посередник, який приймає зовнішні запити та передає їх внутрішнім сервісам. Nginx часто використовують у цій ролі, тому що він:
приховує внутрішню структуру системи;
створює єдину точку входу;
завершує HTTPS-з’єднання;
віддає статичні файли;
маршрутизує запити;
розподіляє навантаження;
допомагає централізувати логування та мережеві обмеження.
Типова схема має вигляд:
Користувач → Nginx → застосунокЗавдяки цьому application server може зосередитися на бізнес-логіці, а Nginx — на прийманні, маршрутизації та базовій обробці мережевого трафіку.