# Проксі

## Що це таке

Проксі — це програма, що стоїть між клієнтом і сервером, до якого той хоче
дістатися. Вона приймає запит клієнта, робить власний запит до наступної
машини на шляху й повертає відповідь. Клієнт говорить із проксі, а проксі — із
сервером, тож два кінці ніколи не ділять одне з'єднання. Кажуть також
«проксі-сервер» або просто «посередник». Різновидів кілька, і найкорисніше
запитання до кожного — хто його туди поставив.

Специфікація HTTP, RFC 9110, називає три поширені види посередників
(intermediaries). Проксі у вузькому сенсі обирає сам клієнт, зазвичай через
локальне налаштування: параметр браузера чи змінну середовища. Шлюз (gateway),
який RFC також називає зворотним проксі (reverse proxy), стоїть на боці
сервера: для того, хто підключається, він поводиться як сервер-джерело, а
кожен запит передає всередину, одному чи кільком серверам за собою. Тунель
сліпо пересилає байти між двома з'єднаннями й перестає бути учасником розмови,
щойно його відкрито. Клієнт просить про тунель методом CONNECT, називаючи хост
і порт, а потім проводить крізь нього наскрізний TLS. RFC згадує й четвертий
вид, якого ніхто не обирав: проксі-перехоплювач (interception proxy), який
часто називають прозорим. Він перенаправляє вихідний трафік на порт 80 на межі
мережі, як це роблять публічні точки доступу та корпоративні брандмауери.

Кожен перехід — окреме з'єднання. На діаграмі в RFC між браузером і
сервером-джерелом стоять три посередники, тобто з'єднань чотири, і такі
налаштування, як keep-alive чи тайм-аут, стосуються кожного з них окремо. HTTP
також вимагає, щоб проксі себе називав: він мусить додавати поле заголовка Via
до кожного повідомлення, яке пересилає, і саме так у трасуванні видно петлю чи
несподіваного посередника.

HTTP — лише один рівень. Проксі SOCKS, версію 5 якого описує RFC 1928 від
березня 1996 року, пересилає TCP-з'єднання та UDP-датаграми, не читаючи їхнього
вмісту, тож ним може користуватися будь-який протокол. Відомий приклад — Tor:
за замовчуванням він приймає на порту 9050 SOCKS-з'єднання від локальних
програм.

## Чому це важливо

Хто керує проксі, той може читати трафік. Для звичайного HTTP це кожен
заголовок і кожне тіло повідомлення. Крізь тунель CONNECT він бачить ім'я
хоста й порт, а решта — це TLS від клієнта до сервера. Зворотний проксі, що
завершує TLS, бачить усе відкритим текстом, і це його робота, бо щоб
маршрутизувати запит, його треба прочитати. Тож поставити перед сайтом
зворотний проксі іншої компанії, а саме це й робить мережа доставки контенту
(CDN), означає віддати тій компанії адресу кожного відвідувача й кожен URL
раніше, ніж їх побачить сам сайт. Для інтернет-крамниці під атакою це може
бути слушний обмін. Проте це однаково рішення про приватність, і сторінка про
приватність має про нього сказати.

Другий наслідок — те, що дізнається сервер. RFC 7239 описує втрату прямо: за
проксі запити видаються такими, що надходять з адреси проксі. Журнал,
обмеження частоти запитів, список блокування чи список дозволених адрес, що
спираються на адресу клієнта, бачать тоді одну адресу на всіх. Звичне
виправлення — заголовок, який несе початкову адресу далі: стандартне поле
Forwarded з RFC 7239 або старіший X-Forwarded-For. Котрій копії цього
заголовка вірити — саме з цього питання починається більшість проблем.

Третій наслідок: проксі змінює значення помилки. Відповіді 502 Bad Gateway та
504 Gateway Timeout пише посередник про сервер, що стоїть за ним: проксі
отримав недійсну відповідь або не отримав її вчасно. Код 407 означає, що
облікові дані хоче сам проксі, перш ніж щось переслати. Знати, яка машина
написала помилку, — це половина діагнозу.

Четвертий: багато проксі кешують. RFC 9111 називає кеш, що зберігає відповіді
для більш ніж одного користувача, спільним; зазвичай він є частиною
посередника. Такий кеш робить сайт швидшим і дешевшим в обслуговуванні, і
водночас саме в ньому відповідь, призначена одній людині, може дістатися іншій.

## Як це виглядає на практиці

Три фрагменти роботи компанії обертаються навколо проксі: один поставили перед
сайтом і знову прибрали, інші належать стороннім і принесли б свої заголовки
до журналу, а ще один вимкнули навмисно.

У серпні 2026 року компанія перенесла свою DNS-зону з приблизно двадцяти
записів до репозиторію й узгодила її з API Cloudflare. Частиною цієї роботи
було пустити вебтрафік крізь мережу доставки контенту провайдера як зворотний
проксі, а потім
[робочий CDN-проксі перед сайтом розібрали заради приватності](/uk/portfolio/reconciled-a-dns-zone-declaratively-118/).
Заперечення стосувалося його місця: адреса кожного читача й кожен запитаний
шлях спершу потрапляли б до третьої сторони й оброблялися б за її правилами, а
для компанії, яка каже читачам, що за ними ніхто не стежить, це важило більше,
ніж захист від атак, який він давав. Рішення записане разом з обґрунтуванням,
тож його не відкриють знову випадково.

Місяцем пізніше компанія почала рахувати читачів за власним журналом
вебсервера, і довелося зважати на проксі, якими вона не керує.
[Журнал доступу викидає сім заголовків, що передають адресу клієнта](/uk/portfolio/measured-thirteen-addresses-from-the-access-log-alone-166/),
ще до запису рядка. Логер копіює заголовки без змін, тож у читача за
корпоративним проксі незамаскована адреса інакше лягла б у файл під назвою
заголовка, якої правило маскування не охоплювало.

Того ж місяця сайт опублікували як onion-дзеркало в мережі Tor, і
[власний проксі-порт демона Tor для вихідного трафіку в конфігурації відключено](/uk/portfolio/published-the-site-as-a-tor-onion-mirror-on-a-readable-168/).
Дзеркало виходить у мережу лише через ланцюжки, які демон будує назовні, тож
приймати підключення ззовні на хості нічому не треба, а локальним програмам
машина не пропонує шляху назовні.

## Де виникають проблеми

**Усі запити в журналі приходять з однієї адреси.** Застосунок стоїть за
зворотним проксі й записує адресу з'єднання, яке отримав, тобто адресу проксі.
Обмеження частоти тоді гальмує всіх одразу, а блокування, записане засобом
виявлення вторгнень, блокує проксі, тобто всю аудиторію. Нехай проксі передає
адресу клієнта в заголовку, а застосунок читає цей заголовок лише на
з'єднаннях із власної адреси проксі. У nginx для цього є директива
set_real_ip_from модуля realip; більшість вебфреймворків мають аналогічне
налаштування довірених проксі.

**Клієнт обирає собі адресу, надіславши заголовок.** Застосунок читає
X-Forwarded-For у кожному запиті, зокрема в тих, що дійшли до нього напряму,
і в тих, що прибули з підробленим значенням у заголовку від самого початку.
RFC 7239 прямо каже, що поле може змінити будь-який вузол на шляху, клієнт
теж. Довіряйте лише записам, які додав ваш власний проксі, рахуючи справа, і
прив'яжіть застосунок до loopback або приватної мережі, щоб проксі був єдиним
входом.

**Журнал, який мав бути анонімним, зберігає повні адреси.** Адресу клієнта
замасковано в полі, яке хтось перевірив, але та сама адреса їде в Forwarded,
X-Forwarded-For, X-Real-IP та подібних заголовках, і логер, що записує набір
заголовків у тому вигляді, в якому їх отримав, зберігає їх повністю. Складіть
перелік усіх заголовків, що можуть нести адресу, відкиньте або замаскуйте
кожен у момент запису рядка, а тоді надішліть тестовий запит і прочитайте
рядок на диску.

**Сторінки повертають 502 або 504, а власний журнал бекенда мовчить.** Помилку
написав проксі. Код 502 означає, що він отримав недійсну відповідь: бекенд не
працював, слухав на іншому порту або говорив звичайним HTTP там, де проксі
чекав TLS, чи навпаки. Код 504 означає, що бекенд відповідав довше за тайм-аут
читання проксі, який у nginx за замовчуванням становить 60 секунд. Спершу
прочитайте журнал помилок проксі, бо там названо адресу upstream, яку він
пробував, і збільшуйте тайм-аут лише для маршрутів, про які відомо, що вони
повільні.

**Користувач бачить сторінку іншого користувача.** Спільний кеш у проксі чи в
мережі доставки контенту зберіг персональну відповідь, наприклад сторінку
облікового запису, віддану разом із сесійним cookie, і віддав її наступному,
хто запитав той самий URL. Позначайте кожну відповідь, що стосується окремого
користувача, директивою Cache-Control: private, бо таку відповідь, за RFC
9111, спільний кеш зберігати не повинен, або no-store там, де копію не варто
зберігати ніде, і перевірте заголовки, які проксі повертає насправді.

## Що почитати далі

- [RFC 9110, section 3.7: Intermediaries](https://www.rfc-editor.org/rfc/rfc9110#section-3.7)
  — визначення проксі, шлюзу, тунелю та проксі-перехоплювача.
- [RFC 9110, section 9.3.6: CONNECT](https://www.rfc-editor.org/rfc/rfc9110#section-9.3.6)
  — як клієнт просить проксі відкрити тунель до хоста й порту.
- [RFC 7239: Forwarded HTTP Extension](https://www.rfc-editor.org/rfc/rfc7239)
  — стандартний заголовок для початкової адреси клієнта та його застереження.
- [RFC 9111: HTTP Caching](https://www.rfc-editor.org/rfc/rfc9111)
  — спільні й приватні кеші та директиви, що їх розмежовують.
- [RFC 1928: SOCKS Protocol Version 5](https://www.rfc-editor.org/rfc/rfc1928)
  — протокольно нейтральний проксі, що пересилає TCP-з'єднання й UDP.

<https://engineer.company/uk/glossary/proxy/>
