Новинка Виртуальный VDS сервер в Нидерландах от 390р
Выберите продукт

TLS termination за прокси: схема, реальный IP и защита от редирект-лупов (X-Forwarded-Proto, Forwarded, PROXY protocol)

При TLS termination на CDN/балансировщике бэкенд часто видит HTTP и IP прокси — из-за этого ломаются редиректы, cookie и логи. Разберём X-Forwarded-Proto/Forwarded и PROXY protocol, настройку realip и доверие только к своим прокси без лупов и сюрпризов HSTS.
TLS termination за прокси: схема, реальный IP и защита от редирект-лупов (X-Forwarded-Proto, Forwarded, PROXY protocol)

TLS termination — это схема, когда шифрование TLS заканчивается (терминируется) не на приложении, а на внешнем узле: балансировщике, reverse proxy, ingress-контроллере, CDN. Дальше до бэкенда трафик часто идёт обычным HTTP (или повторно шифруется внутри сети). Подход удобный: проще управлять сертификатами и разгружать приложение, но появляются два типовых класса проблем.

  • Приложение не понимает, что исходный запрос был HTTPS: делает неверные редиректы, генерирует неправильные абсолютные URL, выставляет «не те» cookie-флаги.
  • Бэкенд и логи видят не реальный IP клиента, а адрес прокси, из-за чего ломаются rate-limit, аудит, геоблок, антибот и расследование инцидентов.

Ниже — практическая памятка: какие заголовки использовать (X-Forwarded-Proto, Forwarded), когда нужен PROXY protocol, и как собрать цепочку так, чтобы не получить циклические редиректы, «вечный» 301 и путаницу в логах.

Как выглядит цепочка при TLS termination

Базовый сценарий:

Client (HTTPS) → CDN/LB/Reverse Proxy (terminates TLS) → Backend (HTTP) → App

С точки зрения приложения соединение до него — HTTP, хотя пользователь пришёл по HTTPS. Если ничего не делать, приложение может:

  • редиректить на http:// вместо https://;
  • ставить cookie без Secure;
  • генерировать канонические ссылки/OG-теги с неправильной схемой;
  • непредсказуемо вести себя с HSTS и «принудительным HTTPS» в браузере.

Ключевая идея: прокси обязан донести до бэкенда исходные параметры запроса (схема, хост, порт, IP), а бэкенд должен доверять этим данным только от доверенных прокси.

X-Forwarded-Proto и Forwarded: что выбрать

X-Forwarded-Proto: де-факто стандарт

X-Forwarded-Proto — самый распространённый способ сообщить приложению исходную схему клиента. Обычно прокси выставляет:

X-Forwarded-Proto: https

Часто рядом используются:

  • X-Forwarded-For — цепочка IP (клиент, прокси1, прокси2…);
  • X-Forwarded-Host — исходный Host;
  • X-Forwarded-Port — исходный порт (обычно 443).

Forwarded (RFC 7239): стандартизирован, но поддержка разная

Forwarded может передавать сразу несколько параметров одним заголовком:

Forwarded: for=203.0.113.10;proto=https;host=example.com

Плюсы: единый формат и явные атрибуты (for, proto, host). Минусы: не все приложения и middleware учитывают его «из коробки», поэтому на практике часто оставляют совместимость через X-Forwarded-*.

Практический выбор

  • Если стек современный и вы контролируете приложение — можно включать Forwarded, но обычно всё равно оставляют X-Forwarded-Proto для совместимости.
  • Если приложение «коробочное» (CMS, панель, старый фреймворк) — почти наверняка ему нужен X-Forwarded-Proto.

Схема reverse proxy: передача X-Forwarded-Proto/Forwarded от edge к бэкенду

Если терминируете TLS на edge и хотите централизованно управлять сертификатами и сроками, проще держать выпуск и ротацию в одном месте. Для проектов с требованиями к валидации и поддержке обычно выбирают коммерческие SSL-сертификаты.

FastFox SSL
Надежные SSL-сертификаты
Мы предлагаем широкий спектр SSL-сертификатов от GlobalSign по самым низким ценам. Поможем с покупкой и установкой SSL бесплатно!

PROXY protocol: когда заголовков недостаточно

PROXY protocol — это не HTTP-заголовок. Это строка (v1) или бинарный префикс (v2), который прокси добавляет перед полезными данными TCP-соединения. Так бэкенд на транспортном уровне узнаёт реальный IP и порт клиента, даже если дальше идёт не HTTP (например, TCP-прокси до TLS-терминации на другом узле, SMTP/IMAP и т.д.).

Когда он нужен:

  • у вас L4-балансировка (TCP), и до приложения «не добираются» HTTP-заголовки;
  • вы хотите получать реальный IP в Nginx/Apache на более «жёсткой» модели, чем доверие к X-Forwarded-For;
  • нужно одинаковое решение для HTTP и не-HTTP сервисов за одним балансировщиком;
  • вы строите цепочку из нескольких прокси и хотите строгую модель доверия на уровне транспорта.

Когда достаточно заголовков:

  • у вас обычный reverse proxy (HTTP), и приложение корректно доверяет X-Forwarded-Proto и X-Forwarded-For от доверенных адресов.

Главные грабли: доверие к заголовкам и подмена клиентом

Самая частая ошибка — «просто включить» обработку X-Forwarded-For/X-Forwarded-Proto на бэкенде, не ограничив список доверенных прокси. Тогда любой клиент сможет прислать фальшивые заголовки и:

  • подделать IP в логах (и обойти rate-limit по IP);
  • подделать https, чтобы приложение построило URL иначе или выставило cookie-флаги иначе;
  • сломать безопасность, если на этих данных завязаны ACL, аудит, блокировки и корреляция.

Правило простое: бэкенд должен доверять X-Forwarded-* и Forwarded только от явно заданных IP ваших прокси/CDN/LB.

Неправильные редиректы и «редирект-луп» при TLS termination

Классический симптом: «слишком много перенаправлений» и бесконечный 301/302 между HTTP и HTTPS. Обычно цепочка такая:

  1. клиент приходит по HTTPS на прокси;
  2. прокси идёт на бэкенд по HTTP;
  3. приложение не видит, что исходно был HTTPS, и редиректит на HTTPS;
  4. прокси снова терминирует TLS и идёт на бэкенд по HTTP — цикл повторяется.

Лечение предсказуемое: на прокси выставляем X-Forwarded-Proto (или Forwarded с proto=https), на приложении включаем режим доверия к proxy headers (название зависит от фреймворка), и выбираем одну точку принудительного HTTPS: либо edge, либо приложение.

Если нужно глубже разобрать миграции и влияние HSTS, пригодится материал: миграция домена без ловушек 301 и HSTS.

HSTS за прокси: кто должен добавлять Strict-Transport-Security

Strict-Transport-Security (HSTS) — заголовок, который браузер запоминает и затем принудительно открывает сайт по HTTPS. В контексте TLS termination есть две рабочие модели:

  • HSTS на edge (прокси/CDN/LB): логично, потому что именно edge обслуживает HTTPS «наружу» и гарантирует корректную доставку заголовка.
  • HSTS на бэкенде: допустимо, если вы уверены, что наружу никогда не уходит HTTP-ответ без TLS, и заголовок не будет добавлен/потерян/задублирован.

Практика: начинайте с небольшого max-age, а затем увеличивайте. Помните, что HSTS «откатывается» не сразу — браузер кеширует политику на срок max-age.

Подробнее про связку HTTPS, HSTS и автоматизацию выпуска — в статье: практическая настройка HTTPS, Certbot и HSTS.

Если проект крутится на отдельном сервере и вы хотите без ограничений управлять цепочкой прокси, логами и политиками безопасности, удобный вариант — вынести edge/балансировщик на VDS и настроить всё под свою топологию.

FastFox VDS
Облачный VDS-сервер
Виртуальные серверы с быстрым запуском и гибкой конфигурацией от 390₽ / мес
Доступные локации
Россия Нидерланды

Рекомендуемые конфигурации: прокси задаёт, бэкенд проверяет

Ниже — рабочие примеры, которые чаще всего закрывают 80% проблем: корректная схема (HTTPS), корректный real ip, и минимальные «дыры» в доверии.

Nginx как reverse proxy (TLS termination) → HTTP backend

На прокси (Nginx, который принимает HTTPS) важно передать Host, схему, порт и цепочку адресов:

server {
  listen 443 ssl http2;
  server_name example.com;

  location / {
    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 X-Forwarded-Host $host;
    proxy_set_header X-Forwarded-Port $server_port;

    proxy_pass http://127.0.0.1:8080;
  }
}

Так как подключение к прокси по 443, переменная $scheme будет https — приложение сможет корректно определить исходную схему (если настроено доверие к заголовку).

Nginx backend: real ip через X-Forwarded-For (только от доверенных прокси)

Если вы хотите, чтобы в логах бэкенда был реальный IP, используйте модуль realip и ограничьте доверие через set_real_ip_from:

http {
  set_real_ip_from 192.0.2.10;
  set_real_ip_from 192.0.2.11;

  real_ip_header X-Forwarded-For;
  real_ip_recursive on;

  log_format main '$remote_addr - $host [$time_local] "$request" $status '
                  'proto=$http_x_forwarded_proto xfwd=$http_x_forwarded_for';

  access_log /var/log/nginx/access.log main;
}

Ключевое: в set_real_ip_from должны быть только ваши прокси/балансировщики. Если добавить «всё подряд», клиент сможет подменить X-Forwarded-For.

PROXY protocol: HAProxy отправляет, Nginx принимает

Если балансировщик общается с бэкендом по PROXY protocol, Nginx должен слушать порт с флагом proxy_protocol, а realip должен брать адрес из транспорта:

server {
  listen 80 proxy_protocol;

  set_real_ip_from 192.0.2.10;
  real_ip_header proxy_protocol;

  location / {
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-Proto https;
    proxy_pass http://127.0.0.1:8080;
  }
}

Фрагмент HAProxy, который отправляет PROXY protocol в backend:

frontend fe_https
  bind :443 ssl crt /etc/haproxy/certs/example.pem
  default_backend be_app

backend be_app
  server app1 127.0.0.1:8080 send-proxy

Важно: если включили send-proxy, бэкенд обязан ожидать PROXY protocol. И наоборот: если Nginx слушает с proxy_protocol, а upstream его не присылает, вы получите ошибки на уровне протокола (обычно «битый» первый запрос).

Apache за прокси: схема и реальный IP

В Apache схема обычно берётся из заголовков, а real ip — через модуль remoteip. Концептуально выглядит так: доверяем заголовку только от адресов прокси и указываем, какой именно заголовок считать источником адреса клиента.

RemoteIPHeader X-Forwarded-For
RemoteIPTrustedProxy 192.0.2.10
RemoteIPTrustedProxy 192.0.2.11

Дальше уже на уровне приложения/виртуального хоста вы включаете «доверие к HTTPS за прокси» (механика зависит от приложения и связки модулей).

Проверка real ip: в логах бэкенда отображается IP клиента, а не адрес прокси

Проверка: как быстро понять, что всё работает

Проверяем схему и редиректы

С клиента удобно посмотреть цепочку редиректов и финальные заголовки:

curl -I -L https://example.com/

Если есть редирект на канонический URL, он должен вести на https://. Если видите прыжки на http://, значит где-то потерян X-Forwarded-Proto (или приложение ему не доверяет).

Проверяем real ip в логах

Сделайте запрос и проверьте, какой IP попадает в access log бэкенда. При корректной настройке realip вы увидите IP клиента, а не адрес прокси. Если везде адрес прокси — значит realip не включён или не добавили нужные адреса в set_real_ip_from/RemoteIPTrustedProxy.

Чеклист безопасной эксплуатации

  • Одна точка принудительного HTTPS: либо edge, либо приложение. Смешанный режим часто даёт редирект-луп.
  • Доверие только доверенным прокси: ограничьте источники для X-Forwarded-For/X-Forwarded-Proto и/или используйте PROXY protocol на L4.
  • Схема и порт: передавайте X-Forwarded-Proto и при необходимости X-Forwarded-Port, иначе приложения иногда строят ссылки на 80.
  • HSTS включайте осознанно: начните с малого max-age, затем увеличивайте. Убедитесь, что весь сайт реально обслуживается по HTTPS.
  • Логи для расследований: полезно логировать и $remote_addr, и форвард-цепочку (для корреляции), но доверять им можно только от своих прокси.

Что выбрать в реальной инфраструктуре

Если у вас типичный веб-проект и reverse proxy на уровне HTTP — чаще всего достаточно X-Forwarded-Proto + корректная настройка доверия к прокси и real ip. Если у вас L4-балансировка, TCP-прокси, сервисы не-HTTP или нужна более «жёсткая» модель передачи адреса — используйте PROXY protocol.

В любом случае относитесь к forwarded-данным как к небезопасному вводу: пока вы явно не ограничили доверенные источники, клиент может подделать и IP, и схему. Правильная связка TLS termination + X-Forwarded-Proto/Forwarded + real ip даёт предсказуемые редиректы, корректную работу cookie и понятные логи, а HSTS становится управляемой политикой, а не сюрпризом.

Поделиться статьей

Вам будет интересно

NFSv4 на Linux VDS: exports, idmapd, firewall и systemd automount OpenAI Статья написана AI (GPT 5)

NFSv4 на Linux VDS: exports, idmapd, firewall и systemd automount

Разберём, как поднять NFSv4 Linux на VDS для общих каталогов: подготовить сервер и клиентов, описать exports, настроить idmapd, от ...
PostgreSQL pg_dump и pg_restore на VDS: custom format и parallel restore OpenAI Статья написана AI (GPT 5)

PostgreSQL pg_dump и pg_restore на VDS: custom format и parallel restore

Разбираем, как безопасно сделать логический бэкап PostgreSQL на VDS через pg_dump в custom format и восстановить его pg_restore с ...
PostgreSQL roles: GRANT, REVOKE, default privileges и least privilege на практике OpenAI Статья написана AI (GPT 5)

PostgreSQL roles: GRANT, REVOKE, default privileges и least privilege на практике

Разбираем, как построить безопасную модель доступа в PostgreSQL: отдельные роли для владельца, приложения, чтения и миграций, прав ...