Start typing to search across invoices, services, domains, tickets, and more...
Приложения на Node.js, Python (Flask, Django, FastAPI) или Go обычно слушают локальный порт вроде 127.0.0.1:3000, и открывать его напрямую в интернет не стоит. В этой статье показано, как настроить обратный прокси Nginx для домена, правильно пробросить WebSocket, передать приложению реальный IP клиента, подключить бесплатный HTTPS через certbot и найти причину ошибки 502 Bad Gateway. Команды подходят для Debian/Ubuntu и Rocky Linux/AlmaLinux.
Сначала убедитесь, что приложение отвечает локально, иначе прокси будет возвращать только 502. Привязывайте приложение к 127.0.0.1, чтобы наружу смотрел только Nginx, и запускайте его через systemd или pm2 для автоматического перезапуска после сбоя.
# Debian / Ubuntu
apt update && apt install -y nginx
# Rocky Linux / AlmaLinux
dnf install -y nginx
systemctl enable --now nginx
# Make sure the backend app is listening locally (example: port 3000)
ss -lntp | grep 3000
curl -I http://127.0.0.1:3000
Направьте DNS домена на IP сервера и создайте /etc/nginx/conf.d/app.conf. Блок map корректно обрабатывает и обычные запросы, и апгрейд до WebSocket; заголовки X-Forwarded-* передают приложению IP клиента и исходный протокол; увеличенный proxy_read_timeout не даёт обрывать долгие соединения. Для загрузки больших файлов увеличьте client_max_body_size.
# /etc/nginx/conf.d/app.conf (included on Debian/Ubuntu and Rocky/AlmaLinux)
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
listen [::]:80;
server_name app.example.com;
client_max_body_size 20m;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
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 $connection_upgrade;
proxy_connect_timeout 10s;
proxy_read_timeout 300s;
}
}
Перед каждой перезагрузкой проверяйте синтаксис командой nginx -t. На Rocky/AlmaLinux SELinux запрещает Nginx подключаться к backend-портам, пока не включён httpd_can_network_connect. Откройте порты 80 и 443 (подробнее — в статье про файрвол Linux).
nginx -t && systemctl reload nginx
curl -I -H "Host: app.example.com" http://127.0.0.1
# Rocky/AlmaLinux (SELinux): allow Nginx to connect to backend ports
setsebool -P httpd_can_network_connect 1
# Open HTTP/HTTPS in the firewall
ufw allow 'Nginx Full' # Debian/Ubuntu with ufw
firewall-cmd --permanent --add-service=http --add-service=https
firewall-cmd --reload # Rocky/AlmaLinux
За обратным прокси все запросы выглядят так, будто приходят с 127.0.0.1. Nginx уже передаёт настоящий адрес в X-Real-IP и X-Forwarded-For, осталось научить фреймворк доверять локальному прокси. Если перед Nginx стоит CDN или балансировщик, восстановите адрес модулем real_ip и доверяйте только диапазонам CDN, чтобы заголовок нельзя было подделать.
// Node.js / Express: trust the proxy on localhost
app.set('trust proxy', 'loopback');
// req.ip now returns the real client IP
# Python Flask / any WSGI app
from werkzeug.middleware.proxy_fix import ProxyFix
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1)
# Gunicorn
gunicorn --bind 127.0.0.1:8000 --forwarded-allow-ips="127.0.0.1" app:app
# Only if Nginx itself sits behind a CDN or load balancer (http or server block):
set_real_ip_from 203.0.113.0/24; # the CDN / LB address ranges
real_ip_header X-Forwarded-For;
real_ip_recursive on;
Плагин certbot для Nginx получает сертификат Let's Encrypt, добавляет прослушивание 443 в server-блок и с ключом --redirect перенаправляет HTTP на HTTPS. Домен уже должен указывать на сервер, а порт 80 — быть доступен из интернета. Сертификат действует 90 дней и продлевается автоматически.
# Debian / Ubuntu
apt install -y certbot python3-certbot-nginx
# Rocky Linux / AlmaLinux (EPEL)
dnf install -y epel-release && dnf install -y certbot python3-certbot-nginx
certbot --nginx -d app.example.com --redirect -m [email protected] --agree-tos --no-eff-email
# Renewal runs from a systemd timer; test it:
certbot renew --dry-run
systemctl list-timers | grep -i certbot
X-Forwarded-Proto. Настройте доверие прокси, как в шаге 4.502 означает, что Nginx не получил корректного ответа от backend. Начните с журнала ошибок Nginx: Connection refused — приложение остановлено или слушает другой порт, Permission denied — чаще всего SELinux. Учтите, что в Node 17+ «localhost» может разрешаться в IPv6 ::1: приложение слушает [::1]:3000, а Nginx стучится на 127.0.0.1.
tail -n 50 /var/log/nginx/error.log
# connect() failed (111: Connection refused) -> app stopped or wrong port
# connect() ... (13: Permission denied) -> SELinux: setsebool -P httpd_can_network_connect 1
# upstream prematurely closed connection -> app crashed / restarted
# upstream timed out (110) -> app too slow: raise proxy_read_timeout or fix the app
systemctl status myapp
journalctl -u myapp -n 100 --no-pager
ss -lntp | grep -E ':3000|:8000' # 127.0.0.1:3000 vs [::1]:3000 ?
Проверьте proxy_http_version 1.1 и оба заголовка Upgrade и Connection. Если перед сервером есть CDN, включите WebSocket и там. Неактивные соединения по умолчанию закрываются через 60 секунд — увеличьте proxy_read_timeout или отправляйте heartbeat из приложения.
Да. Создайте по server-блоку на каждый домен с разными портами или распределяйте пути через location в рамках одного домена. Если каждому сайту нужен отдельный IP, смотрите статью про отдельный IP для каждого сайта в Nginx.
504 означает, что backend отвечает слишком долго. Оптимизируйте медленный запрос или увеличьте proxy_read_timeout и проверьте в логах приложения, не зависает ли оно на базе данных или внешних API.
Если проблема не решена, создайте тикет — техническая поддержка IMIDC работает 24/7. Укажите IP сервера, версию ОС, выполненные команды и полный текст ошибки, чтобы мы быстрее нашли причину.