Довірені проксі

Коли OxPHP працює за зворотним проксі (Kubernetes Ingress, Cloudflare, AWS ALB, nginx), кожен запит надходить з IP-адреси проксі. Без налаштування довірених проксі обмеження частоти запитів, журнал доступу та $_SERVER['REMOTE_ADDR'] бачать IP-адресу проксі замість справжнього клієнта.

Налаштування

bash
# Comma-separated CIDR list TRUSTED_PROXIES="10.0.0.0/8,172.16.0.0/12,192.168.0.0/16" # Shorthand: all RFC-1918 + loopback + link-local (IPv4 and IPv6) TRUSTED_PROXIES="private" # Keywords and CIDRs combine in one list TRUSTED_PROXIES="private,cloudflare,100.64.0.0/10"

Кожен елемент списку, розділеного комами, — це CIDR, окрема IP-адреса (один хост) або одне з ключових слів private і cloudflare у будь-якому регістрі. Елемент, що не є жодним із них, спричиняє помилку під час запуску.

Note

Якщо змінну не задано, OxPHP ігнорує всі заголовки переадресації. Це безпечне типове значення.

Як це працює

Коли запит надходить із довіреної IP-адреси, OxPHP перевіряє заголовки переадресації в порядку пріоритету:

  1. Forwarded (RFC 7239) — стандартизований заголовок
  2. X-Forwarded-For / X-Forwarded-Proto / X-Forwarded-Host / X-Forwarded-Port — фактичний стандарт як запасний варіант

Якщо присутній заголовок Forwarded, заголовки X-Forwarded-* ігноруються.

Визначення IP-адреси клієнта

OxPHP використовує алгоритм «крайня права недовірена адреса» — той самий підхід, що й nginx (real_ip_recursive on), Caddy, Traefik та Apache:

text
X-Forwarded-For: 203.0.113.50, 172.16.1.1, 10.0.0.5 TCP peer: 10.0.0.1 (trusted) Walk right-to-left: 10.0.0.5 → trusted → skip 172.16.1.1 → trusted → skip 203.0.113.50 → NOT trusted → client IP

Це запобігає підробці через значення, додані на початок. Зловмисник може дописати ліворуч фальшиві IP-адреси, але крайню праву недовірену адресу встановив останній довірений проксі в ланцюжку.

Елемент може містити порт: 203.0.113.50:4711 або [2001:db8::1]:4711 — у такій формі деякі балансувальники навантаження (зокрема Azure Application Gateway) записують адреси в X-Forwarded-For. Коли адреса клієнта береться з такого елемента, його порт стає REMOTE_PORT.

Обхід зупиняється на елементі без адреси або з адресою, яку не вдається розібрати як IP-адресу (порожній елемент, unknown, обфускований ідентифікатор RFC 7239), а також на рядку заголовка, який OxPHP не може прочитати як текст (наприклад, з байтом поза ASCII). Нічого ліворуч від цієї точки не використовується, бо ця частина ланцюжка може бути лише тим, що надіслав клієнт. Адреса клієнта — це крайня права недовірена адреса праворуч від цієї точки. Якщо всі елементи там довірені, нею стає крайній лівий із них; якщо там немає жодного елемента, адреса не переписується й лишається адресою вузла, що встановив з’єднання, разом із портом.

text
X-Forwarded-For: 198.51.100.9, unknown, 10.0.0.5 TCP peer: 10.0.0.1 (trusted) Walk right-to-left: 10.0.0.5 → trusted → skip unknown → does not parse → stop 198.51.100.9 → never reached Client IP: 10.0.0.5 (every entry right of the stop is trusted → the leftmost of them)

З ключовим словом cloudflare запит, чий ланцюжок переадресації показує, що він надійшов із периферійного вузла Cloudflare, натомість отримує адресу клієнта із заголовка CF-Connecting-IP. Див. За CDN і балансувальником навантаження платформи.

Що змінюється

Коли TRUSTED_PROXIES налаштовано, а IP-адреса, з якої встановлено з’єднання, є довіреною:

Компонент Без довірених проксі З довіреними проксі
$_SERVER['REMOTE_ADDR'] IP-адреса проксі Справжня IP-адреса клієнта; IP-адреса проксі, якщо заголовки переадресації не дають адреси
$_SERVER['REMOTE_PORT'] Вихідний порт проксі Порт клієнта з елемента ip:port у Forwarded або X-Forwarded-For, інакше 0; порт вузла, що встановив з’єднання, якщо адреса не переписується
$_SERVER['HTTPS'] Залежно від налаштувань TLS в OxPHP З Forwarded: proto= або X-Forwarded-Proto
$_SERVER['REQUEST_SCHEME'] http або https залежно від TLS З переданого протоколу
$_SERVER['SERVER_NAME'] Із заголовка Host З Forwarded: host= або X-Forwarded-Host
$_SERVER['SERVER_PORT'] Із заголовка Host З X-Forwarded-Port, інакше порт із X-Forwarded-Host / Forwarded: host=, інакше 443/80
Обмеження частоти запитів За IP-адресою проксі За IP-адресою клієнта
Журнал доступу IP-адреса проксі Справжня IP-адреса клієнта

Коли адресу клієнта переписано, REMOTE_PORT — це вихідний порт клієнта, якщо елемент, з якого взято адресу, його містить (вузол RFC 7239 Forwarded: for="ip:port" або елемент ip:port у X-Forwarded-For), і 0 в іншому разі. Відновити порт неможливо, тож замість того, щоб його вгадувати, його обнуляють. Адреса, взята з CF-Connecting-IP, теж не має порту, тож і тоді REMOTE_PORT дорівнює 0. Коли заголовки не дають адреси клієнта й адреса не переписується, як описано в розділі Визначення IP-адреси клієнта, REMOTE_PORT лишається портом вузла, що встановив з’єднання.

Мережі private

Скорочення private охоплює:

Мережа Опис
10.0.0.0/8 Приватна мережа класу A
172.16.0.0/12 Приватна мережа класу B
192.168.0.0/16 Приватна мережа класу C
127.0.0.0/8 Loopback
169.254.0.0/16 Link-local
::1/128 Loopback IPv6
fc00::/7 Унікальні локальні адреси IPv6
fe80::/10 Link-local IPv6

Спільний адресний простір (100.64.0.0/10, RFC 6598) не входить до private. Якщо ваш балансувальник навантаження підключається з нього, додайте його до списку поруч із ключовим словом: TRUSTED_PROXIES="private,100.64.0.0/10".

Мережі cloudflare

Ключове слово cloudflare додає периферійні діапазони, які Cloudflare публікує на cloudflare.com/ips: 15 діапазонів IPv4 і 7 діапазонів IPv6 станом на публікацію 2026-10-05. Список вкомпільовано в OxPHP, і він змінюється лише з релізом OxPHP. Коли список Cloudflare змінюється раніше:

  • Новий діапазон. Додайте його до списку поруч із ключовим словом: TRUSTED_PROXIES="private,cloudflare,<new range>". Доданий так діапазон вважається одним із ваших власних проксі, а не периферійним вузлом Cloudflare, тож запити через нього отримують адресу за правилом крайньої правої недовіреної адреси, а не з CF-Connecting-IP, доки реліз не додасть цей діапазон до ключового слова.
  • Відкликаний діапазон. Його неможливо відняти від ключового слова. Щоб прибрати його раніше, ніж це зробить реліз OxPHP, перелічіть діапазони, яким ви й далі довіряєте, як CIDR замість ключового слова. Так ви також відмовляєтеся від читання CF-Connecting-IP, яке прив’язане до ключового слова.

За CDN і балансувальником навантаження платформи

Поширене розгортання ставить CDN перед балансувальником навантаження хостинг-платформи:

text
client → Cloudflare → load balancer → OxPHP

З TRUSTED_PROXIES=private довіреним є лише балансувальник навантаження. Крайньою правою недовіреною адресою в X-Forwarded-For тоді стає периферійний вузол Cloudflare, який підключився до балансувальника, і кожен запит виглядає так, ніби надходить від Cloudflare, — у REMOTE_ADDR, журналі доступу, обмеженні частоти запитів і атрибуті спану client.address. Довіряйте обом переходам:

bash
TRUSTED_PROXIES="private,cloudflare"

Якщо балансувальник навантаження підключається з адреси поза діапазонами private, додайте до списку і його діапазон: TRUSTED_PROXIES="private,100.64.0.0/10,cloudflare".

З ключовим словом cloudflare OxPHP визначає адресу клієнта у три кроки:

  1. 1
    Пройти повз власні проксі. Починаючи з вузла, що встановив з’єднання, OxPHP іде ліворуч ланцюжком переадресації (Forwarded, якщо він є, інакше X-Forwarded-For) повз ваші власні проксі: діапазони private і перелічені вами CIDR. Адреса в діапазонах Cloudflare вважається адресою Cloudflare, навіть якщо ви її перелічили.
  2. 2
    Знайти перехід через Cloudflare. Якщо перший перехід, що не є одним із ваших власних проксі, — це адреса Cloudflare, запит пройшов через Cloudflare, і адресою клієнта є заголовок CF-Connecting-IP. Заголовок має бути присутній рівно один раз і містити коректну IP-адресу.
  3. 3
    Інакше — крайня права недовірена адреса. Якщо запит не проходив через Cloudflare або CF-Connecting-IP відсутній, повторюється чи некоректний, застосовується правило крайньої правої недовіреної адреси, а діапазони Cloudflare вважаються довіреними.

Перехід, на якому зупиняється обхід, — це вузол, що встановив з’єднання, або адреса, яку записав один із ваших власних проксі. Тому клієнт, що звертається до балансувальника навантаження напряму, не може змусити OxPHP прочитати CF-Connecting-IP, який він надіслав сам: балансувальник записує туди власну адресу клієнта, а до адреси Cloudflare, яку клієнт дописав лівіше, обхід ніколи не доходить. Як і правило крайньої правої недовіреної адреси, це спирається на те, що ваші проксі підтримують заголовок, який читає OxPHP, — дописують адресу, що до них підключилася, а не передають значення клієнта без змін.

Видаляйте заголовок Forwarded клієнта

Ваші проксі мають підтримувати й Forwarded. OxPHP читає його в пріоритеті перед X-Forwarded-For, а Cloudflare передає його далі в тому вигляді, в якому його надіслав відвідувач, бо це не один із заголовків, які встановлює Cloudflare. Тому балансувальник навантаження, що підтримує лише X-Forwarded-For, має видаляти Forwarded із запиту. Якщо він пропускає цей заголовок, будь-який клієнт — через Cloudflare чи ні — сам обирає адресу, яку бачить OxPHP, з ключовим словом cloudflare чи без нього.

CF-Connecting-IP також не читається, коли обхід зупиняється, не дійшовши до адреси Cloudflare, — на елементі без адреси або з адресою, яку не вдається розібрати, чи на рядку, який OxPHP не може прочитати як текст, як описано в розділі Визначення IP-адреси клієнта. На запис, який один із ваших проксі додає окремим рядком, не впливає нечитабельний рядок, що його клієнт надіслав раніше. Запис, який проксі дописує в той самий рядок, що й текст, надісланий клієнтом, втрачається разом із цим рядком: обхід зупиняється там, і адреса береться з того, що ваші проксі записали праворуч від цього рядка, або лишається адресою вузла, що встановив з’єднання, — але ніколи з того, що надіслав клієнт.

Cloudflare Workers

Cloudflare дописує в X-Forwarded-For адресу того, хто до нього підключився, тож для звичайного відвідувача обидва заголовки називають того самого клієнта. Вони можуть розходитися для запитів, які Cloudflare Workers надсилають до вашого origin-сервера: такі запити теж виходять з адрес Cloudflare, хоч би який обліковий запис володів Worker. Довідник HTTP-заголовків Cloudflare не уточнює, що такий запит може містити в X-Forwarded-For. Щодо CF-Connecting-IP уточнює: Worker може обрати значення лише для запиту до імені хоста у власній зоні; для імені хоста в іншій зоні Cloudflare заголовок містить фіксовану адресу 2a06:98c0:3600::103, а для імені хоста поза Cloudflare — адресу клієнта, яку Worker змінити не може.

Довіра до діапазонів Cloudflare все одно означає довіру до всього, що виходить із мережі Cloudflare, а не лише з вашої зони. Якщо адреса клієнта впливає на рішення щодо безпеки, налаштуйте origin-сервер так, щоб він приймав трафік лише з вашої зони, у будь-який спосіб, який пропонує ваша платформа.

Безпека

  • Безпечне типове значення — без TRUSTED_PROXIES жоден заголовок переадресації не обробляється
  • Перевірка CIDR — некоректні значення в TRUSTED_PROXIES спричиняють помилку під час запуску
  • Стійкість до підробки — алгоритм «крайня права недовірена адреса» ігнорує значення, додані зловмисником на початок
  • Заголовки переадресації в запитах із недовірених IP-адрес повністю ігноруються

Промпти для вашого агента

Ці промпти розраховані на агента з підключеним MCP-сервером документації OxPHP — потрібні сторінки він прочитає сам.

prompt
Налаштуй довірені проксі для мого розгортання OxPHP. Прочитай сторінку `security/trusted-proxies` через MCP-сервер документації OxPHP. З моїх файлів інфраструктури визнач, що стоїть перед застосунком, потім дай мені точну конфігурацію й поясни, що допускає кожне значення і що воно дозволило б клієнтові підробити, якщо я помилюся.

Див. також