Настройка обратного прокси Nginx: эффективное управление трафиком
Настройте обратный прокси Nginx с четкой маршрутизацией, правильными заголовками, поддержкой WebSocket, тайм-аутами, буферизацией и шагами по устранению неполадок.
Настройка обратного прокси Nginx: эффективное управление трафиком
Настройка обратного прокси Nginx позволяет Nginx принимать публичный веб-трафик и перенаправлять его на одно или несколько внутренних приложений. Это полезно, когда ваше приложение работает на Node.js, Python, Go, Java или другом сервисе, который не должен быть напрямую доступен из интернета.
Вместо того чтобы пользователи подключались к порту вашего приложения, они подключаются к Nginx на стандартных портах HTTP или HTTPS. Nginx обрабатывает публичный край, а затем эффективно направляет трафик к нужному внутреннему сервису.
Что делает обратный прокси
Обратный прокси располагается перед серверами ваших приложений. Клиент общается с Nginx, а Nginx общается с бэкендом. Для браузера Nginx является веб-сайтом. Для приложения Nginx является вышестоящим клиентом, если вы не передаете заголовки, сохраняющие исходные детали запроса.
Этот шаблон дает несколько преимуществ:
- Вы можете запускать приложения на частных портах, таких как
3000,5000или8080. - Вы можете завершать TLS на Nginx.
- Вы можете маршрутизировать разные имена хостов или пути к разным сервисам.
- Вы можете добавить буферизацию, тайм-ауты, сжатие и кэширование.
- Вы можете скрыть детали реализации бэкенда от публичной сети.
Базовый обратный прокси для приложения, работающего на 127.0.0.1:3000, выглядит так:
server {
listen 80;
server_name 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_pass указывает Nginx, куда отправлять запрос. Строки proxy_set_header сохраняют полезный контекст запроса. Без них ваше приложение может регистрировать каждый запрос как исходящий от Nginx и может не знать, использовался ли исходный запрос HTTP или HTTPS.
Если вы новичок в структуре виртуальных хостов, ознакомьтесь с серверными блоками Nginx перед разделением трафика между несколькими доменами.
Маршрутизация трафика по хосту или пути
Правила обратного прокси обычно маршрутизируют по имени хоста, пути или обоим. Маршрутизация на основе хоста распространена, когда отдельные приложения используют разные домены:
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://127.0.0.1:4000;
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;
}
}
Маршрутизация на основе пути полезна, когда один домен обслуживает несколько сервисов:
server {
listen 80;
server_name example.com;
location /api/ {
proxy_pass http://127.0.0.1:4000/;
}
location / {
proxy_pass http://127.0.0.1:3000;
}
}
Будьте осторожны с косыми чертами в конце proxy_pass. В Nginx proxy_pass http://backend; и proxy_pass http://backend/; могут по-разному переписывать перенаправленный URI при использовании внутри блока location. Проверьте точные пути URL, которые ожидает ваше приложение.
Например, если /api/users неожиданно достигает вашего бэкенда как /users или /api/api/users, сначала проверьте комбинацию префикса location и косой черты. Это одна из самых распространенных ошибок обратного прокси.
Заголовки, тайм-ауты и WebSockets
Заголовки делают бэкенд осведомленным об исходном запросе. Заголовок Host важен, когда приложение создает абсолютные URL, проверяет разрешенные хосты или поддерживает несколько арендаторов. X-Forwarded-For помогает сохранить исходный IP-адрес клиента. X-Forwarded-Proto помогает приложениям генерировать безопасные ссылки после завершения TLS.
Если ваш бэкенд использует WebSockets, добавьте заголовки обновления:
location /socket/ {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
Тайм-ауты должны соответствовать поведению вашего приложения. Обычный веб-запрос должен завершаться быстро. Экспорт отчета, потоковая конечная точка или запрос с длинным опросом могут потребовать больше времени:
proxy_connect_timeout 5s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
Избегайте установки огромных тайм-аутов везде только для того, чтобы скрыть одну медленную конечную точку. Длинные тайм-ауты могут занять ресурсы и затруднить обнаружение реальных сбоев. Настройте location, который в этом нуждается.
Буферизация — еще один важный параметр. По умолчанию Nginx может буферизовать ответы вышестоящего сервера перед отправкой их клиенту. Это полезно для многих веб-приложений, но потоковые конечные точки могут потребовать отключения буферизации:
proxy_buffering off;
Используйте это только там, где требуется потоковое поведение. Для стандартных HTML и API ответов буферизация часто повышает стабильность.
Завершение TLS и перенаправление на HTTPS
Во многих конфигурациях Nginx также обрабатывает HTTPS. Это позволяет внутреннему приложению работать на частном HTTP-порту, в то время как пользователи получают обычный безопасный сайт на порту 443.
Распространенная форма выглядит так:
server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
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;
}
}
Блок сервера перенаправления намеренно мал. Он выполняет одну задачу: перемещает обычный HTTP-трафик на HTTPS. Блок HTTPS-сервера обрабатывает проксирование.
Если ваше приложение находится за Nginx и все еще генерирует ссылки http://, проверьте, доверяет ли оно X-Forwarded-Proto. Многие фреймворки требуют настройки, такой как "trust proxy" или список разрешенных прокси, прежде чем они будут использовать перенаправленные заголовки. Не доверяйте слепо перенаправленным заголовкам из публичного интернета на уровне приложения; убедитесь, что только Nginx может достичь порта приложения.
Группы вышестоящих серверов и простая балансировка нагрузки
Когда одного бэкенда недостаточно, определите группу вышестоящих серверов:
upstream app_backend {
server 10.0.1.10:3000;
server 10.0.1.11:3000;
keepalive 32;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://app_backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
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 по умолчанию использует циклическую балансировку нагрузки. Вы также можете использовать такие опции, как least_conn, когда длинные запросы делают один бэкенд более загруженным, чем другие. Проверка работоспособности в открытом исходном коде Nginx в основном пассивна: если бэкенд выходит из строя, Nginx может пометить его как недоступный на определенный период на основе настроек сбоя. Nginx Plus имеет активные проверки работоспособности, но не предполагайте, что эти функции существуют в каждой установке.
Keepalive в блоке вышестоящих серверов поддерживает открытые соединения с бэкендом для повторного использования. Это помогает при большом количестве маленьких запросов, но бэкенд должен быть способен обрабатывать количество бездействующих и активных соединений, которые Nginx может поддерживать.
Контейнеры и частные сети
Настройка обратного прокси часто вызывает путаницу в Docker или Kubernetes, потому что localhost меняет значение. Если Nginx работает внутри одного контейнера, 127.0.0.1:3000 указывает на сам контейнер Nginx, а не на отдельный контейнер приложения.
В Docker Compose проксируйте к имени сервиса:
location / {
proxy_pass http://app:3000;
}
В Kubernetes вы обычно проксируете к DNS-имени сервиса, хотя многие развертывания Kubernetes используют контроллер Ingress вместо написанных вручную серверных блоков Nginx.
Простое правило: проверьте связь оттуда, где работает Nginx, а не с вашего ноутбука и не из контейнера бэкенда. Если это не удается, Nginx тоже потерпит неудачу:
curl -v http://app:3000/
Запустите это внутри контейнера Nginx или на хосте Nginx, в зависимости от вашего развертывания.
Границы безопасности, которые стоит проверить
Обратный прокси должен уменьшить публичное воздействие, а не случайно создать его больше. Внутреннее приложение должно обычно прослушивать частный интерфейс, частную подсеть или сеть контейнеров. Если ваше приложение прослушивает 0.0.0.0:3000 на публичной виртуальной машине, пользователи могут обойти Nginx, посетив http://example.com:3000.
Проверьте прослушиваемые порты на хосте:
sudo ss -ltnp
Если бэкенд должен прослушивать все интерфейсы внутри контейнера, используйте правила брандмауэра, группы безопасности или настройки сети контейнеров, чтобы только Nginx мог получить к нему доступ извне. Это важно, потому что приложения часто полагаются на Nginx для TLS, ограничений размера запросов, ограничений скорости, шлюзов аутентификации или списков разрешенных IP.
Также будьте осторожны с перенаправленными заголовками. Заголовки, такие как X-Forwarded-For, легко подделать клиентам, если Nginx не перезаписывает их, и приложение доверяет только прокси. Распространенный шаблон Nginx:
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
Это добавляет адрес клиента в цепочку. Ваше приложение или конвейер журналирования должны знать, каким адресам прокси доверять. В противном случае ограничение скорости или журналы аудита могут записывать неправильный IP-адрес "клиента".
Ограничения размера запросов также относятся к этому разговору. Если ваше приложение принимает загрузку файлов, установите client_max_body_size намеренно:
client_max_body_size 25m;
Не увеличивайте его глобально до огромного значения, если только каждый маршрут в этом не нуждается. Конечная точка загрузки фото профиля и конечная точка входа в систему JSON не должны иметь одинаковый лимит тела запроса.
Практический контрольный список развертывания
Прежде чем считать обратный прокси завершенным, протестируйте его как пользователь и как оператор:
curl -I http://example.com/должен показывать ожидаемое перенаправление или ответ.curl -I https://example.com/должен показывать ожидаемый статус и заголовки.- Журналы приложения должны показывать исходный хост и полезный IP-адрес клиента.
- Конечные точки WebSocket или потоковой передачи должны быть протестированы отдельно.
- Неправильный путь, такой как
/api/does-not-exist, должен завершаться ошибкой так, как ожидает ваше приложение. - Журналы ошибок Nginx должны быть тихими во время обычных запросов.
Для маршрутизации по путям я люблю тестировать три URL для каждого location: голый префикс, один обычный вложенный путь и один путь со строкой запроса. Например:
curl -i http://example.com/api/
curl -i http://example.com/api/users
curl -i 'http://example.com/api/users?page=2'
Эти простые проверки выявляют многие ошибки с косыми чертами до того, как это сделают пользователи.
При перезагрузке используйте одну и ту же безопасную последовательность каждый раз:
sudo nginx -t
sudo systemctl reload nginx
sudo tail -n 50 /var/log/nginx/error.log
Если приложение находится за завершением TLS, также проверьте, что сгенерированные ссылки, перенаправления, куки и URL обратных вызовов используют HTTPS. Потоки входа — это то, где это часто ломается в первую очередь, потому что перенаправления и безопасные куки зависят от понимания приложением исходной схемы.
Распространенные шаблоны сбоев
502 Bad Gateway обычно означает, что Nginx достиг location обратного прокси, но не смог получить действительный ответ от вышестоящего сервера. Бэкенд может быть отключен, порт может быть неправильным, приложение может прослушивать другой интерфейс или соединение может быть отклонено брандмауэром.
504 Gateway Timeout обычно означает, что Nginx подключился к чему-то, но не получил ответ вовремя. Это может быть медленное приложение, заблокированный запрос к базе данных, перегруженный пул рабочих процессов или тайм-аут, слишком короткий для конечной точки. Увеличение proxy_read_timeout может быть уместным для известной долго работающей конечной точки экспорта. Это не исправление для в целом медленного приложения.
Циклы перенаправления часто возникают из-за несоответствия между завершением TLS и настройками доверия приложения. Браузер достигает Nginx через HTTPS, Nginx проксирует приложение через HTTP, и приложение думает, что исходный запрос был обычным HTTP. Приложение перенаправляет на HTTPS, но то же самое происходит снова. Передача X-Forwarded-Proto — это только половина исправления; приложение также должно доверять ему от прокси.
Отсутствующие IP-адреса клиентов обычно проявляются как каждый запрос, приходящий с 127.0.0.1, адреса моста Docker или частного адреса балансировщика нагрузки. Передайте X-Real-IP и X-Forwarded-For, затем настройте приложение и уровень журналирования для безопасного чтения.
Сломанные статические ресурсы после маршрутизации по путям часто возникают из-за приложений, которые предполагают, что они находятся в /. Если вы смонтируете приложение в /admin/, оно все равно может генерировать ссылки на /assets/app.css. Иногда это можно исправить с помощью настроек базового пути приложения. Попытка переписать каждый путь ресурса в Nginx обычно ненадежна.
Небольшой пример из реального мира
Представьте одну виртуальную машину, на которой работают три сервиса:
- Маркетинговый сайт на
127.0.0.1:3000 - API на
127.0.0.1:4000 - Инструмент администрирования на
127.0.0.1:5000
Вы можете маршрутизировать их так:
server {
listen 443 ssl;
server_name example.com;
location /api/ {
proxy_pass http://127.0.0.1:4000/;
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:5000/;
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 / {
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;
}
}
Это может работать, но имеет компромиссы. API и приложение администрирования должны правильно вести себя под своими префиксами. Если нет, отдельные имена хостов, такие как api.example.com и admin.example.com, могут быть чище. Хороший дизайн обратного прокси заключается не только в том, чтобы заставить Nginx принять конфигурацию; речь идет о выборе маршрутизации, с которой ваши приложения могут жить.
Тестирование и устранение неполадок настройки
Всегда проверяйте конфигурацию перед перезагрузкой:
nginx -t
Затем перезагрузите Nginx и сделайте запрос через публичное имя хоста. Проверьте как браузер, так и журналы. Журналы доступа Nginx показывают, достиг ли запрос Nginx. Журналы ошибок показывают сбои соединения, тайм-ауты вышестоящих серверов и детали плохих шлюзов.
Практический пример: ваше приложение Node.js отлично работает на curl http://127.0.0.1:3000, но публичный сайт показывает 502 Bad Gateway. Это означает, что Nginx доступен, но не может успешно связаться с вышестоящим сервером. Проверьте, прослушивает ли приложение ожидаемый адрес, правильный ли порт и не блокирует ли локальный брандмауэр соединение.
Распространенные проблемы обратного прокси включают:
- Неправильный порт или адрес вышестоящего сервера.
- Бэкенд привязан к
localhost, когда Nginx работает в другом контейнере. - Отсутствие заголовков обновления WebSocket.
- Приложение отклоняет запросы, потому что заголовок
Hostнеожиданный. - Неправильная перезапись URI, вызванная косой чертой.
- Тайм-ауты, слишком короткие для медленной конечной точки.
Для более глубоких сбоев вышестоящих серверов используйте устранение неполадок Nginx 502.
Когда обращаться за помощью
Обратитесь за помощью к DevOps-инженеру, если обратный прокси охватывает несколько контейнеров, частные сети, сертификаты TLS или балансируемые вышестоящие серверы. Эти настройки могут выйти из строя таким образом, что выглядят как проблемы Nginx, но на самом деле являются проблемами DNS, брандмауэра, сети контейнеров или работоспособности приложения.
Вам также следует обратиться за помощью перед тем, как выставлять панели администрирования, внутренние API или промежуточные сервисы через публичный обратный прокси. Небольшие ошибки маршрутизации могут создать серьезные проблемы с доступом.
Настройка обратного прокси Nginx — один из самых полезных шаблонов в веб-инфраструктуре. Держите маршрутизацию четкой, передавайте правильные заголовки, тщательно тестируйте поведение пути и позвольте Nginx быть стабильной публичной точкой входа для ваших внутренних сервисов.