Зачем вообще настраивать real IP в Nginx
Когда перед вашим Nginx стоит CDN, балансировщик или обратный прокси, то в $remote_addr по умолчанию будет IP ближайшего прокси (например, узла CDN), а не реальный адрес посетителя. Это ломает сразу несколько вещей:
- логи доступа: аналитика и расследования инцидентов становятся неточными;
- ограничения по IP:
limit_req,limit_conn, allow/deny начинают работать «по прокси»; - приложения: антифрод, гео, персонализация, аудит действий пользователя «видят» не того клиента.
Модуль ngx_http_realip_module решает задачу: он меняет «видимый» адрес клиента на значение из доверенного заголовка, но только если запрос пришёл от доверенного прокси. В этом условии и спрятаны типовые ошибки и риски.
Ключевая идея: доверяйте заголовкам только от доверенных прокси
CDN и reverse proxy обычно добавляют заголовок с исходным IP (или цепочкой прокси). Чаще всего это X-Forwarded-For, иногда — провайдерский заголовок (например, у Cloudflare популярен CF-Connecting-IP).
Проблема: заголовки вроде X-Forwarded-For подделываются. Если Nginx начнёт брать IP из заголовка для любого запроса, злоумышленник сможет подменить адрес клиента. Это классический сценарий x-forwarded-for spoofing.
Если не ограничить доверенные источники через
set_real_ip_from(или поставить слишком широкие сети вроде0.0.0.0/0), вы фактически говорите: «верю любому заголовку от любого клиента».
Директивы real IP: что делает каждая
set_real_ip_from
Список доверенных источников (IP/подсети), которые имеют право «сообщать» вам реальный IP клиента через заголовки. Это должны быть адреса ваших прокси/CDN/LB. Если запрос пришёл не от них — заголовки игнорируются, а $remote_addr остаётся прежним.
real_ip_header
Указывает, из какого заголовка брать реальный IP. Частые варианты:
X-Forwarded-For— стандартная цепочка прокси;X-Real-IP— часто выставляют Nginx/балансировщики;CF-Connecting-IP— Cloudflare.
real_ip_recursive
Определяет, как обрабатывать цепочку адресов в X-Forwarded-For (когда прокси несколько). При real_ip_recursive on; Nginx будет «раскручивать» список и искать реальный клиентский IP, пропуская доверенные прокси в цепочке. Это полезно в многослойных схемах: CDN → L7-балансировщик → Nginx.
Если ваш проект размещён на VDS, real IP почти всегда нужен для корректных лимитов и разборов инцидентов: на выделенной машине вы сами отвечаете за сетевую модель (прокси, VPN, балансировщики) и качество логов.

Минимальная безопасная конфигурация: один доверенный прокси
Если перед вами один обратный прокси (например, локальный балансировщик внутри сети), самый простой вариант:
http {
set_real_ip_from 10.0.0.0/8;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
# ... остальная конфигурация
}
Здесь доверяем заголовкам только от подсети 10.0.0.0/8. Если клиент с интернета попробует прислать свой X-Forwarded-For — Nginx его проигнорирует, потому что источник не из доверенной подсети.
Сценарий «client IP behind proxy»: CDN → ваш Nginx
Когда вы ставите CDN (в том числе в режиме reverse proxy), ваш сервер видит IP узлов CDN. Чтобы восстановить клиентский адрес, нужно:
- выбрать заголовок с клиентским IP, который гарантированно выставляет CDN;
- внести в
set_real_ip_fromвсе подсети CDN; - включить
real_ip_recursive, если между CDN и Nginx есть дополнительные прокси.
Если CDN используется ради скорости, посмотрите также материал про связку CDN и WordPress на виртуальном хостинге: как ускорить WordPress на shared-хостинге с CDN.
Cloudflare real IP: как настроить правильно
Для Cloudflare часто выбирают заголовок CF-Connecting-IP: в нём Cloudflare передаёт один адрес клиента (не цепочку). Тогда обработка проще и меньше шансов ошибиться с рекурсией.
Базовый шаблон для Nginx (диапазоны ниже — пример; в рабочей конфигурации используйте актуальные подсети провайдера):
http {
real_ip_header CF-Connecting-IP;
# Cloudflare IPv4 (пример)
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
# Cloudflare IPv6 (пример)
set_real_ip_from 2400:cb00::/32;
set_real_ip_from 2606:4700::/32;
real_ip_recursive off;
}
Важно: подсети Cloudflare нужно держать актуальными. Если пропустить часть диапазонов, часть трафика будет приходить «как будто» от IP Cloudflare, и вы получите смешанные логи и странное поведение лимитов.
Если вы всё же хотите использовать X-Forwarded-For (например, из-за совместимости с приложением), делайте это аккуратно и с доверенными диапазонами Cloudflare в set_real_ip_from. В таком варианте чаще включают real_ip_recursive on;, особенно если есть промежуточные прокси.
Где размещать директивы: http, server или location
Директивы set_real_ip_from, real_ip_header и real_ip_recursive можно располагать на уровне http или server. Практически:
- если все ваши сайты за одним и тем же CDN/прокси — удобнее задать в
http; - если часть доменов идёт напрямую, а часть через CDN — разделяйте по
server, чтобы не трогать «прямые» проекты.
На уровне location это обычно не нужно: real IP важен для логов и лимитов в целом, а не для одного URL.
Как проверить, что real IP реально работает
1) Посмотреть переменные в access_log
Сделайте временный лог-формат, чтобы увидеть «до/после»:
http {
log_format realip_debug 'remote_addr=$remote_addr realip_remote_addr=$realip_remote_addr xff="$http_x_forwarded_for" cf="$http_cf_connecting_ip"';
access_log /var/log/nginx/realip_debug.log realip_debug;
}
Подсказка по переменным:
$remote_addr— адрес, который Nginx использует как IP клиента (после realip-модуля он изменится);$realip_remote_addr— «оригинальный» адрес источника до подмены (часто это IP прокси/CDN).
2) Сравнить поведение rate limit
Если у вас включён limit_req, то до настройки real IP лимиты «склеивают» всех посетителей в один IP (узел CDN). После корректной настройки каждый клиент будет ограничиваться отдельно.
3) Тест на подмену заголовка (X-Forwarded-For spoofing)
Идея: отправить запрос напрямую на origin (в обход CDN) и подставить фейковый X-Forwarded-For. При корректной настройке Nginx не должен принимать этот IP за реальный, если источник запроса не из set_real_ip_from.
Пример проверки (подставьте ваш origin IP или прямой хост):
curl -I -H 'X-Forwarded-For: 1.2.3.4' -H 'X-Real-IP: 1.2.3.4' http://YOUR_ORIGIN_IP/
Дальше смотрите realip_debug.log: если $remote_addr стал 1.2.3.4, значит вы где-то переоткрыли доверие слишком широко.

Типовые ошибки и как их быстро диагностировать
Ошибка 1: доверяют всем
Симптомы: в логах появляются «красивые» IP, но лимиты/бан-листы ведут себя странно; расследования упираются в поддельные адреса.
Проверьте, нет ли у вас:
set_real_ip_from 0.0.0.0/0;set_real_ip_from ::/0;- случайно добавленной слишком широкой подсети, не относящейся к вашему прокси/CDN.
Ошибка 2: выбран не тот заголовок
Симптомы: $remote_addr не меняется, хотя вы уверены, что прокси передаёт IP.
Что делать: в debug-лог добавьте нужные $http_* переменные (как в примере выше) и убедитесь, что заголовок действительно доходит до вашего Nginx и не «обрезается» промежуточным прокси.
Ошибка 3: забыли IPv6
Симптомы: часть запросов показывает real IP корректно, часть — нет; особенно заметно там, где активно используется IPv6.
Решение: добавить IPv6-подсети вашего CDN/прокси в set_real_ip_from.
Ошибка 4: несколько прокси в цепочке, а real_ip_recursive не настроен
Симптомы: $remote_addr становится IP «не того» прокси или берётся крайний адрес из X-Forwarded-For.
Если у вас цепочка вида CDN → внутренний LB → Nginx, то обычно нужно:
- внести в
set_real_ip_fromи CDN, и внутренний LB; - включить
real_ip_recursive on;при использованииX-Forwarded-For.
Практика: как вести логи, чтобы не потерять реальность
Даже при корректной настройке полезно логировать «обе стороны»: и итоговый клиентский адрес, и IP прокси, который к вам пришёл. Это помогает при разборе аномалий и споров «кто на самом деле стучался».
Пример расширенного формата (для постоянного использования, если место в логах позволяет):
http {
log_format main_ext '$remote_addr realip_src=$realip_remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'xff="$http_x_forwarded_for"';
}
Если вы храните логи централизованно, наличие realip_src упрощает фильтрацию «какой узел CDN вёл себя странно» или «с какого LB шёл трафик».
Чек-лист безопасной настройки real IP
- Определите точный список доверенных прокси (CDN, балансировщики, reverse proxy).
- Выберите
real_ip_header(для Cloudflare часто удобнееCF-Connecting-IP). - Заполните
set_real_ip_fromвсеми актуальными IPv4/IPv6 подсетями доверенных прокси. - Если используете
X-Forwarded-Forи прокси несколько — включитеreal_ip_recursive on;. - Добавьте временный debug-лог и убедитесь, что
$remote_addrи$realip_remote_addrведут себя ожидаемо. - Проведите тест на x-forwarded-for spoofing: прямой запрос на origin с подставным заголовком не должен менять
$remote_addr.
Когда лучше не чинить заголовки, а чинить сеть
Самая надёжная схема — когда ваш origin вообще не доступен напрямую из интернета, а принимает трафик только от CDN/балансировщика. Тогда даже если кто-то подделывает заголовки, он не сможет «обойти» доверенную точку входа.
Практически это делается сетевыми ACL/фаерволом на уровне хоста или облака: разрешить входящие на 80/443 только с IP-подсетей CDN и ваших административных адресов. Настройка real IP в Nginx тогда становится не только удобством для логов, но и частью общей модели безопасности.
Если параллельно вы решаете вопросы доменов и подключения CDN на apex-домене, пригодится разбор ALIAS/ANAME для корня домена при работе с CDN.
И отдельно: когда вы корректно восстанавливаете IP клиента и включаете HTTPS, не забывайте про валидный сертификат. Для боевых проектов логичнее использовать проверенные SSL-сертификаты, чтобы браузеры и API-клиенты не резали доверие к вашему трафику.


