Как настроить Nginx reverse proxy на Ubuntu

Nginx reverse proxy, он же обратный прокси, принимает запросы на портах 80 и 443 и передаёт их вашему приложению, которое слушает только сам сервер. Так обычно публикуют приложения на Node.js, Python или Go: Nginx берёт на себя HTTPS, медленных клиентов и статику, приложение занимается своим делом. Инструкция для Ubuntu 24.04 с Nginx 1.24, на 22.04 всё так же.

Как направить домен на приложение на порту 3000

Запустите приложение так, чтобы оно слушало 127.0.0.1:3000, а не все адреса: тогда снаружи к нему не попасть в обход Nginx. В Node.js это server.listen(3000, '127.0.0.1'), в Gunicorn и Uvicorn — --bind 127.0.0.1:3000 или --host 127.0.0.1 --port 3000. Если Nginx ещё не стоит, начните с установки Nginx на Ubuntu.

Создайте файл сайта. Вместо app.example.com подставьте свой домен:

sudo nano /etc/nginx/sites-available/app.example.com
/etc/nginx/sites-available/app.example.com
server { listen 80; listen [::]:80; server_name app.example.com; 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; } }

Включите сайт, проверьте настройки, перечитайте их и запросите страницу:

sudo ln -s /etc/nginx/sites-available/app.example.com /etc/nginx/sites-enabled/ sudo nginx -t sudo systemctl reload nginx curl -I http://app.example.com/

В ответе должны быть статус и заголовки вашего приложения, а сверху — Server: nginx. В proxy_pass пишите 127.0.0.1, а не localhost: имя localhost ведёт ещё и на IPv6-адрес [::1], а приложение, которое слушает только IPv4, такие соединения не примет.

Зачем нужны строки proxy_set_header

Без них приложение видит каждый запрос как пришедший с 127.0.0.1 и не знает ни настоящего домена, ни того, зашёл ли посетитель по HTTPS.

  • Host $host — домен, который набрал посетитель. Без этой строки Nginx передаёт адрес из proxy_pass, и ссылки, которые строит приложение, ведут на 127.0.0.1.
  • X-Real-IP $remote_addr — IP-адрес посетителя.
  • X-Forwarded-For $proxy_add_x_forwarded_for — цепочка адресов, через которые прошёл запрос, с адресом посетителя в конце.
  • X-Forwarded-Proto $scheme — http или https. По нему приложение строит правильные ссылки и ставит защищённые cookie, когда появится HTTPS.

Доверять этим заголовкам приложение должно только от прокси. В большинстве фреймворков для этого есть настройка: trust proxy в Express, --forwarded-allow-ips в Gunicorn и Uvicorn. В пакете Nginx из Ubuntu есть файл /etc/nginx/proxy_params с теми же четырьмя заголовками, так что их можно заменить строкой include proxy_params;.

Как пропустить WebSocket

WebSocket начинается с обычного запроса с заголовком Upgrade, а его Nginx сам дальше не передаёт. К тому же Nginx 1.24 по умолчанию говорит с приложением по HTTP/1.0, поэтому строка proxy_http_version 1.1 нужна. Сначала добавьте map, который выставит заголовок Connection. Он должен стоять внутри блока http, а все файлы из /etc/nginx/conf.d/ Ubuntu читает как раз там:

sudo nano /etc/nginx/conf.d/websocket.conf
/etc/nginx/conf.d/websocket.conf
map $http_upgrade $connection_upgrade { default upgrade; '' close; }

Потом добавьте две строки в location / файла сайта. Целиком блок теперь такой:

/etc/nginx/sites-available/app.example.com
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; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; }
sudo nginx -t sudo systemctl reload nginx

Если по соединению 60 секунд ничего не идёт, Nginx его закрывает (proxy_read_timeout). Для чата или живых обновлений пусть приложение чаще шлёт WebSocket-пинги, либо поднимите proxy_read_timeout в этом location.

Таймауты и ошибка «413 Request Entity Too Large»

По умолчанию Nginx принимает тело запроса до 1 МБ. На файл побольше он отвечает 413 Request Entity Too Large, а в error.log появляется client intended to send too large body. Поднимите client_max_body_size в блоке server до самого большого файла, который ждёте. Три таймаута прокси по умолчанию — по 60 секунд, и считают они паузу между двумя чтениями или записями, а не весь запрос. proxy_read_timeout стоит поднимать, только если приложению правда нужно дольше думать над ответом, например над отчётом или выгрузкой.

/etc/nginx/sites-available/app.example.com
client_max_body_size 50m; location / { # строки proxy_pass и proxy_set_header из примера выше proxy_connect_timeout 5s; proxy_send_timeout 60s; proxy_read_timeout 120s; }
sudo nginx -t sudo systemctl reload nginx

Короткий proxy_connect_timeout подходит приложению на том же сервере: если за 5 секунд соединиться не вышло, приложение лежит, и посетитель сразу видит ошибку, а не ждёт минуту. Если приложение на PHP, у него свои ограничения на загрузку: как поднять memory_limit и лимиты загрузки в PHP.

Почему Nginx отвечает 502 Bad Gateway

502 значит, что Nginx не дождался ответа от приложения. Причина всегда записана в журнале ошибок, в строке рядом с запросом:

sudo tail -n 20 /var/log/nginx/error.log
  • connect() failed (111: Connection refused) while connecting to upstream — на этом порту никто не слушает. Проверьте, что приложение запущено и на каком адресе: sudo ss -tlnp | grep 3000 Если в журнале upstream: "http://[::1]:3000/", напишите в proxy_pass 127.0.0.1 вместо localhost.
  • upstream prematurely closed connection while reading response header from upstream — приложение упало или оборвало соединение посреди ответа. Смотрите журнал самого приложения, например: journalctl -u <служба приложения> -n 50
  • upstream sent too big header while reading response header from upstream — заголовки ответа не влезают в буфер, обычно из-за большого числа cookie или их размера. Добавьте в location proxy_buffer_size 16k; и proxy_buffers 8 16k;.
  • upstream timed out (110: Connection timed out) while reading response header from upstream — это уже 504 Gateway Time-out, а не 502. Приложение думает дольше, чем proxy_read_timeout. Ускорьте медленный запрос или поднимите таймаут для него.

Если вы работаете и на Mac

MacWashing, раздел «Сборка мусора»: найдены мусор Xcode, пользовательский кэш, установщики и журналы, к очистке 22,83 ГБ

Сборка мусора в MacWashing находит кэш всех знакомых ей программ, в том числе спрятанный внутри их данных, а ещё журналы, мусор Xcode и остатки удалённых программ. Она показывает, сколько места занимает каждая находка, и очищает всё за один раз. Всё спорное остаётся без галочки, пока вы не решите.

Бесплатная пробная версия · macOS 14 и новее

Вопросы и ответы

Что такое Nginx reverse proxy?

Это Nginx перед вашим приложением: посетители подключаются к Nginx на портах 80 и 443, а он передаёт каждый запрос приложению на локальный порт и возвращает ответ. Само приложение снаружи закрыто.

Как добавить HTTPS к прокси?

Направьте домен на сервер и запустите Certbot с плагином для Nginx. Он допишет сертификат в тот же файл сайта и не тронет proxy_pass: как получить сертификат Let’s Encrypt через Certbot.

Может ли один Nginx проксировать несколько приложений?

Да. Дайте каждому приложению свой порт и свой файл сайта со своим server_name. Или один домен с несколькими блоками location, у каждого свой proxy_pass.