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

Nginx real IP за CDN и прокси: real_ip_header и set_real_ip_from без ошибок

Если сайт стоит за CDN или обратным прокси, Nginx видит IP прокси вместо клиента. Разберём real_ip_header и set_real_ip_from, как не попасть на X-Forwarded-For spoofing, примеры для Cloudflare и быстрые тесты по логам.
Nginx real IP за CDN и прокси: real_ip_header и set_real_ip_from без ошибок

Зачем вообще настраивать 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, балансировщики) и качество логов.

Схема цепочки прокси: клиент, CDN, балансировщик и origin с заголовками реального IP

Минимальная безопасная конфигурация: один доверенный прокси

Если перед вами один обратный прокси (например, локальный балансировщик внутри сети), самый простой вариант:

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.

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

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, значит вы где-то переоткрыли доверие слишком широко.

Проверка real IP через curl и анализ переменных в debug access_log Nginx

Типовые ошибки и как их быстро диагностировать

Ошибка 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

  1. Определите точный список доверенных прокси (CDN, балансировщики, reverse proxy).
  2. Выберите real_ip_header (для Cloudflare часто удобнее CF-Connecting-IP).
  3. Заполните set_real_ip_from всеми актуальными IPv4/IPv6 подсетями доверенных прокси.
  4. Если используете X-Forwarded-For и прокси несколько — включите real_ip_recursive on;.
  5. Добавьте временный debug-лог и убедитесь, что $remote_addr и $realip_remote_addr ведут себя ожидаемо.
  6. Проведите тест на x-forwarded-for spoofing: прямой запрос на origin с подставным заголовком не должен менять $remote_addr.
FastFox SSL
Надежные SSL-сертификаты
Мы предлагаем широкий спектр SSL-сертификатов от GlobalSign по самым низким ценам. Поможем с покупкой и установкой SSL бесплатно!

Когда лучше не чинить заголовки, а чинить сеть

Самая надёжная схема — когда ваш origin вообще не доступен напрямую из интернета, а принимает трафик только от CDN/балансировщика. Тогда даже если кто-то подделывает заголовки, он не сможет «обойти» доверенную точку входа.

Практически это делается сетевыми ACL/фаерволом на уровне хоста или облака: разрешить входящие на 80/443 только с IP-подсетей CDN и ваших административных адресов. Настройка real IP в Nginx тогда становится не только удобством для логов, но и частью общей модели безопасности.

Если параллельно вы решаете вопросы доменов и подключения CDN на apex-домене, пригодится разбор ALIAS/ANAME для корня домена при работе с CDN.

И отдельно: когда вы корректно восстанавливаете IP клиента и включаете HTTPS, не забывайте про валидный сертификат. Для боевых проектов логичнее использовать проверенные SSL-сертификаты, чтобы браузеры и API-клиенты не резали доверие к вашему трафику.

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

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

Как безопасно выполнить bootstrap MariaDB Galera после полного останова

Как безопасно выполнить bootstrap MariaDB Galera после полного останова

После одновременной остановки всех узлов Galera обычного запуска MariaDB недостаточно. Сначала найдите сервер с последней транзакц ...
CoreDNS в Kubernetes: исправляем CrashLoopBackOff, SERVFAIL и DNS-петлю

CoreDNS в Kubernetes: исправляем CrashLoopBackOff, SERVFAIL и DNS-петлю

Разберём, почему CoreDNS перезапускается или отвечает SERVFAIL, как обнаружить замкнутую пересылку DNS-запросов, проверить доступн ...
Apache Kafka KRaft на VDS: отдельные controller и broker

Apache Kafka KRaft на VDS: отдельные controller и broker

Соберём новый Kafka-кластер без ZooKeeper на шести Linux VDS: три узла controller образуют отказоустойчивый metadata quorum, а три ...