Зачем нужен mod_remoteip и почему «реальный IP» пропадает
Типичный сценарий: сайт работает за reverse proxy (Nginx/HAProxy, балансировщик, CDN, WAF), а на бэкенде Apache в логах и в переменной REMOTE_ADDR вы видите один и тот же адрес — IP прокси. Это нормально: для Apache TCP-соединение приходит именно от прокси.
Проблема начинается дальше: защита по IP (allowlist/denylist), антифрод, rate-limit на уровне приложения, гео-логика, аудит и расследования опираются на адрес клиента. Если вместо клиента везде «адрес прокси», вы теряете смысл этих механизмов.
mod_remoteip решает задачу штатно: он заменяет «адрес клиента» в Apache на значение из заголовка, который добавляет доверенный прокси. В результате:
- в access log появляется real IP клиента;
- в приложении/CGI корректно заполняется
REMOTE_ADDR; - правила, завязанные на IP, начинают работать ожидаемо.
Главное правило безопасности: доверяем не заголовку, а прокси
Заголовок X-Forwarded-For клиент может подделать сам. Если Apache «верит» ему без ограничений, злоумышленник легко подменит IP на любой (включая 127.0.0.1 или адрес из вашей allowlist).
mod_remoteipбезопасен только тогда, когда вы строго ограничили источники, которым доверяете подстановку IP.
Поэтому настройка всегда состоит из двух частей: выбираем RemoteIPHeader и одновременно задаём trusted proxies через RemoteIPTrustedProxy (или список/диапазоны). Без trusted proxies конфигурация почти всегда уязвима.

Как работает цепочка X-Forwarded-For
В большинстве прокси по умолчанию X-Forwarded-For содержит список адресов через запятую. Слева — исходный клиент, дальше — прокси по пути. Пример:
X-Forwarded-For: 203.0.113.10, 198.51.100.23, 192.0.2.5
Смысл такой: клиент 203.0.113.10 пришёл на прокси 198.51.100.23, тот — на следующий 192.0.2.5, а уже он подключился к Apache.
mod_remoteip «разматывает» цепочку справа налево, отбрасывает доверенные прокси и выбирает первый недоверенный адрес как IP клиента. Это обычно надёжнее, чем вручную парсить заголовки в приложении.
Включение модуля mod_remoteip
Debian/Ubuntu
sudo a2enmod remoteip
sudo systemctl reload apache2
RHEL/AlmaLinux/Rocky
Обычно модуль доступен в составе пакета httpd. Проверить загрузку можно так:
sudo httpd -M | grep -i remoteip
Если в выводе есть remoteip_module — модуль активен. Если нет, проверьте установленные пакеты и конфигурацию подключаемых модулей.
Базовая настройка: RemoteIPHeader и trusted proxies
Минимально безопасная конфигурация:
- указываем заголовок, где прокси передаёт адрес клиента (
RemoteIPHeader); - перечисляем доверенные прокси, которые имеют право задавать этот заголовок (
RemoteIPTrustedProxyили список подсетей).
Чаще всего используют X-Forwarded-For. Пример (добавьте в нужный контекст: глобально, в VirtualHost или отдельный include-файл):
RemoteIPHeader X-Forwarded-For
RemoteIPTrustedProxy 192.0.2.10
RemoteIPTrustedProxy 192.0.2.11
RemoteIPTrustedProxy 198.51.100.0/24
Если фронтенд-прокси на той же машине (Apache слушает 127.0.0.1, а Nginx проксирует локально), доверенным прокси будет loopback (и, при необходимости, IPv6):
RemoteIPHeader X-Forwarded-For
RemoteIPTrustedProxy 127.0.0.1
RemoteIPTrustedProxy ::1
Отдельно проверьте сетевую архитектуру: если между вашим «последним прокси» и Apache есть ещё один балансировщик, именно он будет источником подключения, и его адрес тоже должен быть в trusted.
RemoteIPHeader vs Forwarded: что выбрать
Кроме X-Forwarded-For существует стандартизированный заголовок Forwarded (RFC 7239). Он удобен структурой, но на практике чаще встречается именно X-Forwarded-For, а в длинных цепочках легко получить смешанную картину (часть узлов пишет одно, часть — другое).
Рекомендация по эксплуатации:
- если у вас один контролируемый прокси (Nginx/HAProxy) — используйте
X-Forwarded-Forи явно настройте его формирование на прокси; - если цепочка сложная и вы уверены, что все узлы корректно формируют
Forwarded, можно рассмотретьRemoteIPHeader Forwarded, но обязательно тестируйте на реальном трафике; - в смешанных схемах часто проще нормализовать всё в один заголовок на последнем прокси перед Apache.
Если хотите глубже разобраться в общей гигиене заголовков на фронтенде (не только IP), пригодится материал про настройку HTTP security headers в Nginx и Apache.
Как убедиться, что Apache действительно видит real IP
Проверка удобна в три шага: (1) какой IP приходит на сокет, (2) какие заголовки видит Apache, (3) что ушло в лог после обработки mod_remoteip.
1) Проверяем, что Apache получает соединение от прокси
sudo ss -tnp | grep ':80 '
sudo ss -tnp | grep ':443 '
В ESTAB-сессиях вы увидите адрес прокси (или 127.0.0.1), это ожидаемо.
2) Проверяем, что прокси вообще передаёт X-Forwarded-For
Самый быстрый путь — временно залогировать заголовок (см. следующий раздел). Важно: mod_remoteip не «придумает» IP — он использует то, что реально пришло от доверенного узла.
3) Проверяем access log
После включения mod_remoteip стандартный %h (адрес клиента) начнёт показывать итоговый адрес, вычисленный модулем. Это и есть критерий, что настройка работает.
Логи: как записать и real IP, и цепочку прокси
Для расследований полезно хранить две сущности одновременно:
- итоговый адрес клиента по мнению Apache (после
mod_remoteip); - оригинальную цепочку из
X-Forwarded-For(как прислал прокси), чтобы видеть маршрут и быстро ловить аномалии.
Пример формата для Apache (обратите внимание: это конфиг, а не HTML):
LogFormat "%v %h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" xff=\"%{X-Forwarded-For}i\"" vhost_with_xff
CustomLog logs/access_log vhost_with_xff
Здесь %h — уже «исправленный» адрес клиента, а %{X-Forwarded-For}i — значение заголовка как есть.
Если формат хотите подключать внутри виртуального хоста, используйте блок как текст (угловые скобки экранируем):
<VirtualHost *:80>
ServerName example.test
LogFormat "%h %l %u %t \"%r\" %>s %b xff=\"%{X-Forwarded-For}i\"" vhost_with_xff
CustomLog logs/example-access.log vhost_with_xff
</VirtualHost>

Типовые ошибки и как их быстро диагностировать
Ошибка 1: «Включил RemoteIPHeader, но IP не меняется»
Чаще всего причины такие:
- прокси не отправляет
X-Forwarded-For(или отправляет другой заголовок); - не настроены trusted proxies, и модуль игнорирует заголовок;
- между прокси и Apache появился ещё один узел (балансировщик/маршрутизатор), и trusted-адрес указан не тот;
- на фронтенде заголовок затирается (overwrite), а не дополняется (append), из-за чего теряется исходный клиент.
Диагностика: временно добавьте в лог xff=\"%{X-Forwarded-For}i\" и сравните с тем, что стало в %h. Если xff пустой — проблема на прокси. Если xff есть, но %h не меняется — почти всегда вопрос в trusted proxies или в том, что директивы применились не в том контексте.
Ошибка 2: «В логах появился странный IP, которого точно нет»
Это классический симптом доверия к заголовку без ограничений. Проверьте, что у вас задан RemoteIPTrustedProxy и что он не слишком широк. Делать trusted для 0.0.0.0/0 нельзя.
Также помните про NAT и корпоративные сети: «реальный» IP может оказаться публичным адресом организации или адресом внешнего провайдера, а не конкретной рабочей станции — это нормально и объяснимо.
Ошибка 3: «Сломались allowlist/denylist и ограничения по IP»
После включения mod_remoteip Apache начинает использовать новый адрес как «адрес клиента» для многих механизмов. Если раньше вы разрешали доступ по IP прокси, теперь правила нужно пересмотреть.
Практический подход: админские зоны и технические endpoint’ы лучше ограничивать на уровне фронтенд-прокси, а на Apache минимизировать шанс прямого доступа извне (чтобы не смешивать прямые и проксированные запросы).
Сложные схемы: несколько прокси и CDN перед вашим reverse proxy
Если перед вашим Nginx/HAProxy стоит CDN или внешний балансировщик, цепочка становится длиннее. Подход всё равно тот же: доверяем только тем прокси, которые контролируем (или чьи адреса гарантированно принадлежат вашему провайдеру/платформе и документированы).
На практике чаще всего проще так:
- на последнем прокси перед Apache нормализовать заголовок и передавать дальше предсказуемо;
- в Apache доверять только этому последнему прокси (или их кластеру);
- в лог писать и
%h, иX-Forwarded-For, чтобы видеть всю цепочку.
Так вы уменьшаете поверхность ошибок: Apache «разговаривает» только с вашим trusted reverse proxy, а всё внешнее остаётся на границе.
Практический чек-лист перед продом
Убедитесь, что прямого доступа к Apache снаружи нет (или он минимизирован): иначе клиент может обойти прокси и вы получите смесь прямых и проксированных запросов в одном виртуальном хосте.
Проверьте, что прокси всегда добавляет
X-Forwarded-Forкорректно (дописывает цепочку, а не затирает), и отдельно передаётX-Forwarded-Protoпри HTTPS-терминации (важно для приложений).Настройте
RemoteIPTrustedProxyмаксимально узко: только реальные IP/подсети ваших прокси.Обновите формат логов, чтобы фиксировать и итоговый IP, и исходный
X-Forwarded-For.Проверьте правила доступа и лимиты, завязанные на IP — поведение могло измениться после «исправления» адреса клиента.
Сделайте тестовый запрос и сравните: что пришло в заголовке, что записалось в access log, что видит приложение как
REMOTE_ADDR.
Что в итоге
mod_remoteip — правильный способ вернуть real IP клиента за reverse proxy и привести в порядок логи. Ключ к безопасной настройке — не просто включить RemoteIPHeader, а строго определить trusted proxies. Иначе вы рискуете подменой IP и некорректными решениями в безопасности и аналитике.
Если Apache размещён на сервере/VM, где вы сами контролируете сетевую часть, под такие задачи удобно выделять отдельный VDS: проще изолировать фронтенд и бэкенд, закрыть прямой доступ к Apache и предсказуемо управлять цепочкой прокси.


