Приложение слушает локальный порт, например 127.0.0.1:3000, а наружу смотрит только Nginx. Так приложение не видно из интернета напрямую, HTTPS настраивается в одном месте, а статику отдаёт Nginx без участия приложения.
Приложение слушает только localhost
Убедитесь, что приложение привязано к 127.0.0.1, а не к 0.0.0.0. Посмотреть, кто какой порт слушает:
$ ss -tlnpКонфигурация Nginx
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 proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_read_timeout 60s; }}Блок map объявляется один раз на весь сервер (в контексте http). Если сайтов с WebSocket несколько, вынесите его в отдельный файл /etc/nginx/conf.d/upgrade.conf.
$ ln -s /etc/nginx/sites-available/app.example.com /etc/nginx/sites-enabled/$ nginx -t && systemctl reload nginx$ certbot --nginx -d app.example.comЗачем эти заголовки
- Host — приложение видит настоящий домен, а не 127.0.0.1.
- X-Real-IP и X-Forwarded-For — реальный IP посетителя для логов и ограничений. В приложении включите доверие к прокси (trust proxy в Express, ProxyFix во Flask).
- X-Forwarded-Proto — приложение понимает, что запрос пришёл по HTTPS, и строит правильные ссылки и cookie.
- Upgrade и Connection — без них WebSocket-соединения обрываются сразу после рукопожатия.
Статику — напрямую
Если у приложения есть каталог со статикой, отдавайте его Nginx-ом — это быстрее и разгружает приложение:
location /static/ { alias /var/www/app/static/; expires 7d; access_log off;}Проверьте результат
$ curl -I http://127.0.0.1:3000$ curl -I https://app.example.comЧастые ошибки
- 502 Bad Gateway — приложение не запущено или слушает другой порт. Смотрите
ss -tlnpи журнал приложения. - 504 Gateway Timeout — приложение отвечает дольше
proxy_read_timeout. Увеличьте таймаут или ускорьте обработку. - 413 Request Entity Too Large — загрузка больше
client_max_body_size(по умолчанию 1 МБ). - Бесконечный редирект на HTTPS — приложение не получает X-Forwarded-Proto и считает, что запрос пришёл по HTTP.