Пошук уроків, статей та іншого контенту
Налаштуйте Nginx перед Node.js для TLS, маршрутизації, статичних файлів, тайм-аутів і проксування заголовків.
Node.js може напряму приймати HTTP-запити, але в production-середовищі перед ним часто розміщують Nginx.
Nginx у такій схемі:
приймає зовнішні HTTP- і HTTPS-запити;
завершує TLS-з’єднання;
перенаправляє HTTP на HTTPS;
віддає статичні файли;
маршрутизує запити до Node.js;
передає застосунку інформацію про початковий запит;
контролює тайм-аути та розмір навантаження.
Схема запиту виглядає так:
Клієнт → Nginx:443 → Node.js:3000Node.js при цьому може слухати лише локальний інтерфейс 127.0.0.1, тому зовнішній клієнт не звертається до нього напряму.
Створимо простий HTTP-сервер. Він оброблятиме API-запит і показуватиме заголовки, які передав Nginx.
const http = require('node:http');
const port = 3000;
const server = http.createServer((req, res) => {
if (req.url === '/api/health' && req.method === 'GET') {
const forwardedFor = req.headers['x-forwarded-for'] || 'немає';
const forwardedProto = req.headers['x-forwarded-proto'] || 'невідомо';
const response = {
status: 'ok',
protocolSeenByNode: forwardedProto,
clientIp: forwardedFor,
host: req.headers.host || 'невідомо',
};
res.writeHead(200, {
'Content-Type': 'application/json; charset=utf-8',
});
res.end(JSON.stringify(response));
return;
}
res.writeHead(404, {
'Content-Type': 'application/json; charset=utf-8',
});
res.end(JSON.stringify({
error: 'Маршрут не знайдено',
}));
});
server.listen(port, '127.0.0.1', () => {
console.log(`Node.js слухає http://127.0.0.1:${port}`);
});Запустіть сервер:
node server.jsУ цьому прикладі Node.js доступний лише за адресою:
http://127.0.0.1:3000Це важливо: порт Node.js не повинен бути безпосередньо доступним з Інтернету, якщо всі зовнішні запити мають проходити через Nginx.
Конфігурацію віртуального хоста можна розмістити, наприклад, у файлі:
/etc/nginx/sites-available/exampleПриклад конфігурації:
upstream node_backend {
server 127.0.0.1:3000;
}
server {
listen 80;
server_name example.com www.example.com;
# Усі HTTP-запити перенаправляємо на HTTPS
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name example.com www.example.com;
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;
# Каталог зі статичними файлами
root /var/www/example/public;
location /assets/ {
try_files $uri $uri/ =404;
}
location /api/ {
proxy_pass http://node_backend;
# Передаємо Node.js початковий Host-заголовок
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;
# Вимикаємо непотрібний заголовок Connection
proxy_set_header Connection "";
# Тайм-аут встановлення з'єднання з Node.js
proxy_connect_timeout 5s;
# Тайм-аут передавання запиту до Node.js
proxy_send_timeout 30s;
# Максимальний час очікування відповіді від Node.js
proxy_read_timeout 30s;
}
location / {
return 404;
}
}Після створення конфігурації її потрібно активувати. На системах із каталогами sites-available і sites-enabled це зазвичай роблять символічним посиланням:
sudo ln -s /etc/nginx/sites-available/example /etc/nginx/sites-enabled/exampleПеревірте конфігурацію перед перезавантаженням:
sudo nginx -tЯкщо перевірка успішна, перезавантажте Nginx:
sudo systemctl reload nginxTLS завершується на Nginx. Клієнт встановлює захищене HTTPS-з’єднання з Nginx, а Nginx може передавати запит до Node.js через звичайний HTTP у локальній мережі.
Клієнт ── HTTPS ──> Nginx ── HTTP ──> Node.jsУ конфігурації для цього використовуються:
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;Шляхи мають вказувати на реальні файли сертифіката та приватного ключа.
HTTP-сервер виконує перенаправлення:
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}Змінні в цьому правилі:
$host — доменне ім’я запиту;
$request_uri — шлях і query string, наприклад /api/health?verbose=true;
код 301 — постійне перенаправлення.
У production для тестових або тимчасових перенаправлень можна використовувати 302, але для постійного переходу на HTTPS зазвичай використовують 301.
Директива location визначає, які запити оброблятиме конкретний блок.
location /api/ {
proxy_pass http://node_backend;
}Такий блок передасть до Node.js запити:
/api/health
/api/users
/api/orders/123Оскільки proxy_pass не містить додаткового URI-шляху, початковий шлях зберігається.
Наприклад:
Клієнт: /api/health
Node.js: /api/healthЦе відрізняється від конфігурації з URI в proxy_pass:
location /api/ {
proxy_pass http://node_backend/;
}У такому випадку префікс /api/ буде замінено на /:
Клієнт: /api/health
Node.js: /healthТому потрібно заздалегідь визначити, який шлях очікує застосунок.
Nginx ефективніше віддає статичні файли, ніж Node.js, тому CSS, JavaScript, зображення та шрифти часто не проксують до застосунку.
Якщо конфігурація містить:
root /var/www/example/public;
location /assets/ {
try_files $uri $uri/ =404;
}то запит:
/assets/app.cssбуде шукати файл:
/var/www/example/public/assets/app.csstry_files перевіряє, чи існує файл або каталог:
$uri — шлях поточного запиту;
$uri/ — каталог із таким шляхом;
=404 — повернути помилку 404, якщо нічого не знайдено.
Приклад структури файлів:
/var/www/example/public/
└── assets/
├── app.css
└── app.jsЗапит до /assets/app.css не потрапить у Node.js, а буде оброблений Nginx.
Під час проксування Nginx створює нове з’єднання з Node.js. Через це Node.js може бачити не всі характеристики початкового запиту, якщо явно не передати їх у заголовках.
Hostproxy_set_header Host $host;Передає домен, який запитав клієнт.
Без цього Node.js може отримати внутрішнє значення, пов’язане з upstream-сервером, а не початковий домен.
X-Real-IPproxy_set_header X-Real-IP $remote_addr;Містить IP-адресу клієнта, який підключився до Nginx.
X-Forwarded-Forproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;Зберігає ланцюжок IP-адрес проксі. Якщо запит уже мав X-Forwarded-For, Nginx додає до нього поточну адресу.
Наприклад:
X-Forwarded-For: 203.0.113.10, 192.0.2.20У Node.js цей заголовок може мати список адрес через кому. Не слід автоматично вважати будь-яке його значення довіреним, якщо застосунок доступний напряму з Інтернету. У типовій схемі Node.js приймає запити лише від локального Nginx.
X-Forwarded-Protoproxy_set_header X-Forwarded-Proto $scheme;Передає протокол початкового запиту:
httpабо:
httpsЦе потрібно, коли застосунку необхідно знати, чи звернувся клієнт через HTTPS. Наприклад, застосунок може використовувати це значення для формування абсолютних URL або перевірки безпечного з’єднання.
Reverse proxy не повинен чекати на Node.js без обмежень. Для цього використовують тайм-аути:
proxy_connect_timeout 5s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;proxy_connect_timeoutМаксимальний час встановлення з’єднання між Nginx і Node.js.
Якщо Node.js зупинений або недоступний, Nginx не чекатиме нескінченно.
proxy_send_timeoutМаксимальний інтервал між операціями передавання даних до Node.js.
proxy_read_timeoutМаксимальний інтервал очікування даних від Node.js.
Це не завжди загальний час виконання запиту. Якщо Node.js регулярно передає дані, таймер може поновлюватися. Значення потрібно підбирати відповідно до API:
для звичайних коротких API-запитів — кілька десятків секунд;
для довгих операцій — більше значення, але з обережністю;
для помилкових або завислих запитів — коротший тайм-аут.
Занадто малий proxy_read_timeout може переривати легітимні довгі запити. Занадто великий — довго утримувати з’єднання із завислим застосунком.
Після кожної зміни конфігурації спочатку запускайте:
sudo nginx -tЦя команда перевіряє синтаксис і доступність файлів, на які посилається конфігурація.
Після успішної перевірки:
sudo systemctl reload nginxДля перевірки HTTP-перенаправлення:
curl -I http://example.com/api/healthОчікуваний результат міститиме заголовок:
HTTP/1.1 301 Moved Permanently
Location: https://example.com/api/healthДля перевірки API через HTTPS:
curl https://example.com/api/healthNode.js має отримати запит через Nginx і повернути JSON-відповідь.
Якщо Nginx повертає 502 Bad Gateway, найчастіше це означає, що:
Node.js не запущений;
Node.js слухає інший порт;
Node.js слухає іншу адресу;
у upstream вказано неправильний порт;
локальне з’єднання заблоковане налаштуваннями системи.
Якщо повертається 404, перевірте:
відповідність шляху блоку location;
маршрут у Node.js;
наявність статичного файлу;
поведінку proxy_pass зі шляхом або без нього.
Nginx може бути налаштований на:
server 127.0.0.1:3000;Тоді Node.js повинен бути доступний через IPv4-адресу 127.0.0.1.
proxy_passЦі варіанти мають різну поведінку:
location /api/ {
proxy_pass http://node_backend;
}Початковий /api/ зберігається.
location /api/ {
proxy_pass http://node_backend/;
}Префікс /api/ замінюється на /.
Якщо не передати X-Forwarded-Proto, Node.js може вважати, що клієнт використовував HTTP, навіть коли клієнт підключився через HTTPS.
Якщо не передати X-Forwarded-For, застосунок може бачити адресу Nginx замість адреси клієнта.
Заголовки X-Forwarded-* мають сенс лише тоді, коли їх встановлює довірений проксі. Якщо Node.js доступний напряму з мережі, клієнт може самостійно надіслати такі заголовки.
Зазвичай безпечніша схема така:
Інтернет → Nginx → 127.0.0.1:3000а порт Node.js не відкритий назовні.
Помилка синтаксису може завадити перезавантаженню Nginx. Завжди виконуйте:
sudo nginx -tперед:
sudo systemctl reload nginxNginx може бути reverse proxy перед Node.js.
TLS завершується на Nginx, а HTTP-запити можна перенаправляти на HTTPS.
location визначає маршрутизацію запитів.
Статичні файли доцільно віддавати безпосередньо через Nginx.
proxy_set_header передає Node.js домен, IP клієнта та початковий протокол.
proxy_connect_timeout, proxy_send_timeout і proxy_read_timeout обмежують очікування під час проксування.
Після змін конфігурацію потрібно перевіряти через nginx -t, а потім перезавантажувати Nginx.
Для production Node.js зазвичай слухає локальну адресу, а зовнішні запити приймає Nginx.