Выберите продукт

Nginx за Cloudflare: как вернуть реальный IP (realip и PROXY protocol) и не сломать безопасность

Когда сайт работает за Cloudflare, Nginx в логах видит IP узлов Cloudflare вместо адреса клиента. В статье разберём realip через заголовки и через PROXY protocol, как доверять данным безопасно, закрыть origin и что меняется для логов, allow/deny, rate limit, GeoIP и fail2ban.
Nginx за Cloudflare: как вернуть реальный IP (realip и PROXY protocol) и не сломать безопасность

Если вы поставили Cloudflare перед сайтом, то почти сразу сталкиваетесь с эффектом «все посетители с одного IP». В access.log вместо клиента — адреса узлов Cloudflare. Из‑за этого ломаются ограничения по IP (allow/deny), гео‑логика (GeoIP), лимиты (rate limit), анти‑брутфорс (fail2ban), а иногда и аналитика.

Ниже — практический гайд для Linux + Nginx: как корректно восстановить реальный адрес клиента двумя способами: через заголовки (CF-Connecting-IP/X-Forwarded-For) и через PROXY protocol. Параллельно разберём, как не открыть дыру, когда вы «доверяете» данным от прокси.

Почему Nginx видит не тот IP и чем это опасно

В типовой схеме Cloudflare работает как reverse proxy: клиент подключается к Cloudflare, а Cloudflare ходит на ваш origin (Nginx). На origin соединение приходит с IP Cloudflare. Реальный IP клиента Cloudflare передаёт отдельно:

  • в HTTP‑заголовках: CF-Connecting-IP, X-Forwarded-For (иногда Forwarded);
  • или на уровне TCP: PROXY protocol (если он доступен и включён в Cloudflare/балансировщике и поддержан на входе в Nginx).

Если Nginx не «распакует» эти данные, то $remote_addr будет равен Cloudflare‑IP. Это влияет на:

  • allow/deny — можно случайно разрешить доступ всем через Cloudflare или, наоборот, заблокировать легитимных клиентов;
  • GeoIP — география определяется по узлам Cloudflare;
  • rate limit — вы лимитируете весь трафик «как один клиент»;
  • fail2ban — «бан» уходит в Cloudflare‑IP, что бесполезно и может навредить;
  • аудит/расследования — логи теряют ценность.

Базовая идея realip в Nginx: кому можно доверять

Модуль realip в Nginx делает простую вещь: если запрос пришёл от доверенного прокси, то можно заменить «видимый» IP ($remote_addr) на IP из заголовка или из PROXY protocol.

Ключевое правило: никогда не принимайте X-Forwarded-For/CF-Connecting-IP/Forwarded «от всех». Эти заголовки легко подделывает любой клиент, если он может ходить к вам напрямую, минуя Cloudflare.

Сначала закройте origin так, чтобы к нему могли подключаться только IP Cloudflare (и ваши служебные адреса). И только затем включайте realip.

Если вы размещаете проект на VDS и держите Nginx как публичный вход, удобнее сразу заложить «жёсткую» сетевую политику (фаервол + allow/deny) под Cloudflare и отдельно — доступ для администрирования.

Вариант 1 (самый частый): Cloudflare real IP через заголовки

Для Cloudflare чаще всего используют CF-Connecting-IP или X-Forwarded-For. Практичный выбор:

  • CF-Connecting-IP — обычно один адрес клиента (без цепочки);
  • X-Forwarded-For — цепочка IP (клиент, прокси…), нужно понимать порядок и правила доверия.

Схема потока трафика: клиент, Cloudflare и origin с восстановлением реального IP

Пример настройки: real_ip_header + set_real_ip_from

Добавьте в http {} (обычно /etc/nginx/nginx.conf) или в отдельный файл, подключаемый через include:

http {
  real_ip_header CF-Connecting-IP;
  real_ip_recursive on;

  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;
  set_real_ip_from 103.31.4.0/22;
  set_real_ip_from 141.101.64.0/18;
  set_real_ip_from 108.162.192.0/18;
  set_real_ip_from 190.93.240.0/20;
  set_real_ip_from 188.114.96.0/20;
  set_real_ip_from 197.234.240.0/22;
  set_real_ip_from 198.41.128.0/17;
  set_real_ip_from 162.158.0.0/15;
  set_real_ip_from 104.16.0.0/13;
  set_real_ip_from 104.24.0.0/14;
  set_real_ip_from 172.64.0.0/13;
  set_real_ip_from 131.0.72.0/22;

  set_real_ip_from 2400:cb00::/32;
  set_real_ip_from 2606:4700::/32;
  set_real_ip_from 2803:f800::/32;
  set_real_ip_from 2405:b500::/32;
  set_real_ip_from 2405:8100::/32;
  set_real_ip_from 2a06:98c0::/29;
  set_real_ip_from 2c0f:f248::/32;
}

Что здесь важно:

  • real_ip_header — какой заголовок считаем источником реального IP;
  • set_real_ip_from — список доверенных сетей (только от них заголовку верим);
  • real_ip_recursive полезен при цепочке прокси, но включайте осознанно: чем сложнее цепочка, тем выше риск ошибиться в доверии.

Покупать и обновлять сертификаты удобнее централизованно: если вы используете коммерческие SSL-сертификаты для отдельных проектов или B2B‑контуров, заранее продумайте, где у вас терминация TLS (Cloudflare, Nginx, балансировщик) и как это повлияет на логи/диагностику.

Forwarded и X-Forwarded-For: когда пригодится

Иногда до Nginx стоит ещё один прокси/балансировщик, который формирует стандартный Forwarded (RFC 7239) или цепочку X-Forwarded-For. В реальности для Cloudflare чаще проще опираться на CF-Connecting-IP.

Если инфраструктура сложнее, удобная практика такая: на ближайшем к Nginx прокси вы приводите входящие данные к одному «каноническому» заголовку (например, выставляете X-Real-IP), а в Nginx уже указываете именно его в real_ip_header.

Как обновлять список сетей Cloudflare

Сети Cloudflare меняются. Если их «вписать и забыть», однажды получите плавающие проблемы: то realip перестанет работать, то вы начнёте отбрасывать легитимный трафик из новых диапазонов.

Практичный подход: держать отдельный файл (например, /etc/nginx/conf.d/cloudflare-realip.conf) и подключать его в http {}. Обновление — через конфигурационное управление (Ansible/Salt) или хотя бы регламент ручной проверки.

FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

Вариант 2: PROXY protocol — когда нужен и как включать

PROXY protocol передаёт исходный IP/порт на уровне TCP ещё до HTTP. Это удобно, когда:

  • у вас не HTTP, а TCP‑сервисы в stream {} (например, TLS passthrough);
  • вы хотите меньше зависеть от HTTP‑заголовков;
  • перед Nginx стоит L4‑балансировщик, который умеет PROXY protocol.

Нюанс: если включить приём PROXY protocol на слушающем сокете, а трафик придёт без него, Nginx не сможет распарсить начало соединения. Это проявится как ошибки рукопожатия или «мусор» вместо HTTP.

Nginx: listen ... proxy_protocol; и real_ip_header proxy_protocol

Пример для HTTPS‑виртуального хоста, где upstream приходит с PROXY protocol:

server {
  listen 443 ssl http2 proxy_protocol;

  real_ip_header proxy_protocol;
  real_ip_recursive off;

  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;
  set_real_ip_from 103.31.4.0/22;
  set_real_ip_from 141.101.64.0/18;
  set_real_ip_from 108.162.192.0/18;
  set_real_ip_from 190.93.240.0/20;
  set_real_ip_from 188.114.96.0/20;
  set_real_ip_from 197.234.240.0/22;
  set_real_ip_from 198.41.128.0/17;
  set_real_ip_from 162.158.0.0/15;
  set_real_ip_from 104.16.0.0/13;
  set_real_ip_from 104.24.0.0/14;
  set_real_ip_from 172.64.0.0/13;
  set_real_ip_from 131.0.72.0/22;

  set_real_ip_from 2400:cb00::/32;
  set_real_ip_from 2606:4700::/32;
  set_real_ip_from 2803:f800::/32;
  set_real_ip_from 2405:b500::/32;
  set_real_ip_from 2405:8100::/32;
  set_real_ip_from 2a06:98c0::/29;
  set_real_ip_from 2c0f:f248::/32;

  location / {
    proxy_pass http://127.0.0.1:8080;
  }
}

Здесь real_ip_header proxy_protocol говорит Nginx брать адрес клиента не из HTTP, а из метаданных TCP‑соединения.

Смешивать ли заголовки и PROXY protocol

Лучше выбрать один «источник истины». Если upstream‑приложение ожидает X-Forwarded-For, вы можете выставить его при проксировании, уже опираясь на восстановленный $remote_addr:

location / {
  proxy_set_header X-Forwarded-For $remote_addr;
  proxy_set_header X-Real-IP $remote_addr;
  proxy_set_header Forwarded "for=$remote_addr;proto=$scheme";
  proxy_pass http://127.0.0.1:8080;
}

Так вы унифицируете поведение приложений и правил безопасности независимо от того, как именно пришёл реальный адрес.

Как правильно закрыть origin: фаервол и allow/deny

Настройка realip без ограничения доступа к origin — одна из самых частых ошибок. Если origin доступен напрямую из интернета, злоумышленник может прийти в обход Cloudflare и подставить любой CF-Connecting-IP или X-Forwarded-For.

Два уровня защиты:

  1. Сетевой: разрешить входящие 80/443 только с подсетей Cloudflare (и ваших служебных IP).
  2. На Nginx: дополнительный allow/deny на уровне server или location.

Allow/deny в Nginx (как страховка)

server {
  listen 80;

  allow 173.245.48.0/20;
  allow 103.21.244.0/22;
  allow 103.22.200.0/22;
  allow 103.31.4.0/22;
  allow 141.101.64.0/18;
  allow 108.162.192.0/18;
  allow 190.93.240.0/20;
  allow 188.114.96.0/20;
  allow 197.234.240.0/22;
  allow 198.41.128.0/17;
  allow 162.158.0.0/15;
  allow 104.16.0.0/13;
  allow 104.24.0.0/14;
  allow 172.64.0.0/13;
  allow 131.0.72.0/22;

  allow 2400:cb00::/32;
  allow 2606:4700::/32;
  allow 2803:f800::/32;
  allow 2405:b500::/32;
  allow 2405:8100::/32;
  allow 2a06:98c0::/29;
  allow 2c0f:f248::/32;

  deny all;

  location / {
    return 200 "ok";
  }
}

Этот подход полезен как «предохранитель», но не заменяет фаервол: проверка allow/deny происходит уже после установки TCP‑соединения.

Кстати, если вы планируете включать строгие заголовки безопасности и HSTS, полезно заранее продумать миграции сертификатов и редиректы. По теме — миграция домена: 301, HSTS и SSL без сюрпризов.

Проверка правил доступа: origin принимает 80/443 только от подсетей Cloudflare

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

Логи: как убедиться, что realip действительно работает

Проверка «на глаз» по $remote_addr не всегда удобна. На время диагностики расширьте формат логов и выведите несколько полей одновременно:

log_format realip_debug '$remote_addr - $realip_remote_addr - $proxy_protocol_addr - $http_cf_connecting_ip - $http_x_forwarded_for - $request';
access_log /var/log/nginx/access_realip_debug.log realip_debug;

Как читать:

  • $remote_addr — адрес «как видит Nginx после realip»;
  • $realip_remote_addr — исходный адрес до подмены;
  • $proxy_protocol_addr — адрес из PROXY protocol (если он включён);
  • $http_cf_connecting_ip и $http_x_forwarded_for — что приехало в заголовках.

Rate limit по реальному IP: типовые грабли

Если ограничение задано по $binary_remote_addr, а realip не настроен, вы лимитируете Cloudflare, а не клиентов. После включения realip это исправится, но появляется другой нюанс: атаки могут «размазаться» по множеству IP, и лимит станет менее заметным. Обычно приходится комбинировать лимиты на edge‑уровне и в приложении.

Пример простого лимита на клиента:

limit_req_zone $binary_remote_addr zone=perip:10m rate=10r/s;

server {
  location /login {
    limit_req zone=perip burst=20 nodelay;
    proxy_pass http://127.0.0.1:8080;
  }
}

Это и есть «rate limit по реальному IP»: ключ зоны должен соответствовать клиенту, а не прокси.

Fail2ban за прокси: как банить правильно, если есть realip

Fail2ban читает логи и банит IP, который нашёл в строке. Если у вас в логах Cloudflare‑IP, вы фактически баните Cloudflare и создаёте себе проблемы. Чтобы было осмысленно:

  • в логах Nginx первым IP должен стоять уже восстановленный $remote_addr;
  • origin должен быть закрыт от прямого доступа (иначе заголовок подделают и «подставят» чужой IP под бан);
  • нужно понимать, где применять бан: на origin (часто бессмысленно для веб‑трафика через Cloudflare) или на edge/WAF‑уровне (обычно эффективнее).

Если вы баните на уровне origin‑фаервола, помните: реальный клиент физически не подключается к вашему серверу, подключается Cloudflare. Поэтому классические баны IP на origin часто не дают эффекта для сайта, если весь трафик идёт через Cloudflare. В таком сценарии fail2ban полезнее как сигнализация и для защиты сервисов, доступных напрямую (SSH, панели), а не как «анти‑бот» для HTTP.

GeoIP и «география по клиенту»: что поменяется после realip

Для GeoIP‑логики критично, чтобы Nginx уже знал реальный $remote_addr. После включения realip вы можете корректно использовать гео‑переменные для правил доступа, локализации или дополнительной аналитики.

Если вы используете гео‑ограничения (например, для админки), не делайте их единственной защитой. Комбинируйте с аутентификацией, ограничением по URI и при необходимости — allowlist по IP.

Чеклист: диагностика, если «реальный IP» не появляется

  1. Origin открыт напрямую: клиент приходит без Cloudflare, заголовки поддельные, а вы начинаете доверять им как истине. Закройте origin.
  2. Не добавлены все подсети в set_real_ip_from: часть узлов Cloudflare не считается доверенной.
  3. Выбрали не тот заголовок в real_ip_header: проверьте в логах, что реально приезжает ($http_cf_connecting_ip, $http_x_forwarded_for).
  4. Включили PROXY protocol на listen, но upstream его не отправляет: получите ошибки и «битый» трафик.
  5. Забыли IPv6: закрыли origin по IPv4, но оставили открытым по IPv6 (или наоборот).

Практическая рекомендация: заголовки vs PROXY protocol

Если у вас обычный веб‑сайт за Cloudflare и TLS терминируется на Nginx, начните с варианта через заголовки: real_ip_header + полный список set_real_ip_from + жёсткое ограничение доступа к origin. Это самый совместимый и предсказуемый путь.

PROXY protocol выбирайте, когда он действительно нужен по архитектуре: L4‑балансировка, stream {}, TLS passthrough или когда вы сознательно строите цепочку без доверия к HTTP‑заголовкам. Но включайте его только там, где вы точно гарантируете наличие PROXY protocol на входе.

Итог

Восстановление реального IP за Cloudflare — это не одна директива, а связка из трёх частей: (1) закрыть origin, (2) настроить доверенные сети через set_real_ip_from, (3) выбрать корректный источник (real_ip_header или proxy_protocol). После этого начинают «оживать» allow/deny, GeoIP, rate limit и нормальные логи, а инструменты вроде fail2ban перестают работать вслепую.

Если вы тюните кеширование и диапазонные запросы, учитывайте, что прокси‑слой и realip влияют на диагностику и разбор логов. В тему — настройка Range-запросов и кеша в Nginx/Apache.

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

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

NFS в Linux: v3 и v4.1, async, rsize/wsize и mount options для максимального throughput OpenAI Статья написана AI (GPT 5)

NFS в Linux: v3 и v4.1, async, rsize/wsize и mount options для максимального throughput

Разбираем NFS в Linux на практике: чем v3 отличается от v4.1, как устроены locking и сессии, когда (не) включать async, и как подо ...
Linux tc qdisc на VDS: fq_codel vs cake vs fq — борьба с bufferbloat и latency spikes OpenAI Статья написана AI (GPT 5)

Linux tc qdisc на VDS: fq_codel vs cake vs fq — борьба с bufferbloat и latency spikes

Разбираем очереди tc qdisc в Linux на VDS: fq_codel, cake и fq. Покажу, как измерять bufferbloat и latency spikes, а затем настрои ...
UFW и Docker в 2026: NAT, published ports и безопасный хост OpenAI Статья написана AI (GPT 5)

UFW и Docker в 2026: NAT, published ports и безопасный хост

Практическое руководство по UFW и Docker в 2026: как трафик на published ports уходит в NAT и FORWARD, что меняет iptables-nft/nft ...