Модуль 12

Proxy_pass та передача заголовків

Ось готовий урок, створений спеціально для тебе у стилі David Malan. Вмикаймо уяву, відкриваймо термінал і поїхали! 🚀


🎓 Урок: Proxy_pass та магія передачі заголовків

Привіт, друзі! Радий бачити вас на цій лекції. 👋

Сьогодні ми зазирнемо під капот того, як веб-сервери спілкуються між собою. Ми розберемося з директивою proxy_pass в Nginx і зрозуміємо, чому іноді ваш бекенд "втрачає пам'ять" про користувача і як це виправити за допомогою заголовків.

Готові? Тоді вперед!


1. 🔥 Вступ: проблема та мотивація

Уявіть ситуацію. Ви написали крутий додаток на Python (Django/FastAPI) або Node.js. Він працює на порту :8000. Але ви не виставляєте цей порт у світ. Ви ставите перед ним Nginx як охоронця (reverse proxy).

Користувач заходить на ваш сайт, Nginx приймає запит і пересилає його вашому додатку. Все працює? Ніби так.

Але тут ви дивитесь у логи свого Python-додатку і бачите дивну річ:

"Користувач із IP 127.0.0.1 намагався увійти..." "Користувач із IP 127.0.0.1 купив товар..."

Чекайте... Всі ваші користувачі раптом стали "localhost"? 🤨

А якщо вам треба заблокувати зловмисника по IP? Ви заблокуєте 127.0.0.1 і покладете свій власний сервер.

Чому це відбувається? Тому що для вашого бекенду (Python/Node) запит прийшов не від Олега з Києва, а від Nginx, який живе на тій самій машині.

Без розуміння proxy_pass та передачі заголовків ви: 1. Не зможете аналізувати географію користувачів. 2. Не зможете захиститися від DDOS. 3. Зламаєте логіку редіректів у своєму додатку.

Сьогодні ми навчимо Nginx не просто перекидати запити, а робити це розумно.


2. 🧠 Теоретична база (без сухої академічності)

Давайте розберемо це на аналогії.

Уявіть, що Бекенд — це Геніальний Шеф-кухар, який сидить у закритій кухні й готує страви. Він ніколи не бачить відвідувачів. Nginx — це Офіціант.

  1. Гість (Клієнт) каже Офіціанту: "Я хочу борщ. У мене алергія на часник".
  2. Офіціант йде на кухню і каже Шефу: "Один борщ".

⛔️ СТОП! Офіціант забув передати важливу деталь ("алергія на часник"). Шеф приготує звичайний борщ, і Гість постраждає.

У світі серверів: * proxy_pass — це дія "Офіціант йде на кухню". Це команда Nginx переслати запит далі. * Заголовки (Headers) — це ті самі "записки", які Офіціант має передати Шефу разом із замовленням: "Це замовлення від столика №5, у них алергія".

Що відбувається "під капотом"?

Коли Nginx робить proxy_pass, він створює новий HTTP-запит від себе до бекенду. За замовчуванням він не копіює інформацію про оригінального клієнта. Він просто каже: "Дай мені сторінку".

Щоб бекенд знав правду, ми використовуємо директиву proxy_set_header. Ми примусово клеїмо "стікери" на запит:

  • Host: Який домен запитував користувач? (важливо, якщо на бекенді кілька сайтів).
  • X-Real-IP: Яка справжня IP-адреса клієнта?
  • X-Forwarded-For: Весь ланцюжок IP-адрес, через які пройшов запит (якщо проксі серверів було кілька).

💡 Запам'ятайте головне: proxy_pass пересилає запит, але "стирає" особистість клієнта. proxy_set_header відновлює цю особистість, передаючи метадані.


3. 🧪 Приклади (від простого до реального)

Приклад 1: "Амнезія" (Базовий проксі)

Ось найпростіша конфігурація.

location / {
    proxy_pass http://localhost:8000;
}

Що ви очікуєте? Сайт працює. Що бачить бекенд? Host: localhost:8000 Remote Address: 127.0.0.1

Ваш додаток думає, що до нього звертається локальний скрипт, а не жива людина з Інтернету. Якщо додаток згенерує посилання на самого себе, він може створити посилання типу http://localhost:8000/image.jpg, яке у користувача не відкриється.


Приклад 2: "Паспортний контроль" (Правильний проксі)

Виправляємо ситуацію. Додаємо передачу особистості.

location / {
    proxy_pass http://localhost:8000;

    # Передаємо оригінальний домен (наприклад, my-shop.com), а не localhost
    proxy_set_header Host $host;

    # Передаємо справжній IP користувача
    proxy_set_header X-Real-IP $remote_addr;

    # Передаємо історію IP (для сумісності з фреймворками)
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

Чому це круто? 1. $host — змінна Nginx, що містить ім'я домену з запиту клієнта. 2. $remote_addr — IP клієнта. 3. Тепер ваш бекенд у логах бачить: Remote Address: 192.168.1.50 (реальний IP).


Приклад 3: "Проблема Слеша" (Класика жанру)

Подивіться уважно на ці два рядки. Як думаєте, є різниця?

  1. proxy_pass http://localhost:8000;
  2. proxy_pass http://localhost:8000/; (зі слешем у кінці)

Це змінює все! 🤯

  • Варіант 1 (без слеша): Nginx передає шлях "як є". Якщо запит /api/users, на бекенд полетить /api/users.
  • Варіант 2 (зі слешем): Nginx замінює частину шляху, яка співпала в location, на те, що після слеша (тобто на корінь). Якщо location /api/, а запит /api/users, то на бекенд полетить просто /users.

Це найчастіша помилка новачків, через яку "відвалюються" API.


4. 🛠 Практична частина

Час забруднити руки кодом! Виконайте ці завдання, щоб закріпити матеріал.

Задача 1: Налаштування детектива 🕵️‍♂️ Налаштуйте Nginx так, щоб він проксував запити з порту 80 на порт 3000 (умовний Node.js). * Вимога: Бекенд повинен знати, що запит прийшов з браузера Chrome (передайте заголовок User-Agent). Підказка: Nginx передає його автоматично, але переконайтеся в цьому.

Задача 2: Підміна особистості 🎭 Використовуючи proxy_set_header, створіть конфіг, де ви "брешете" бекенду. * Зробіть так, щоб незалежно від того, хто заходить на сайт, бекенд думав, що це заходить користувач із заголовком My-Custom-Header: Hello-CS50.

Задача 3: Вирішення конфлікту шляхів 🛣 У вас є бекенд, який очікує запити на адресу /v1/status. Але на сайті ви хочете, щоб це було доступно за адресою /server-health. * Напишіть location і proxy_pass (зі слешем чи без?), щоб /server-health перетворювалось у /v1/status на бекенді.

Задача 4: Міні-кейс 💼 Ви розробляєте чат. Вам потрібно, щоб Nginx підтримував WebSocket з'єднання (які потребують особливих заголовків Upgrade та Connection). Знайдіть інформацію або здогадайтеся, які proxy_set_header треба додати, щоб канал зв'язку не обривався.


5. 💡 Мислення як у розробника

Як думає Senior DevOps або досвідчений Backend-розробник, коли налаштовує проксі?

  1. "Don't Repeat Yourself" (DRY): Замість того, щоб копіювати proxy_set_header у кожний location, вони створюють окремий файл (наприклад, proxy_params) і просто підключають його: nginx location / { proxy_pass http://localhost:8000; include proxy_params; # Краса! 😍 }

  2. Безпека насамперед: Вони знають, що заголовок Host критично важливий для захисту від атак типу HTTP Host Header attacks. Вони ніколи не забувають його передавати.

  3. Зналагодження (Debugging): Якщо щось не працює, вони не вгадують. Вони дивляться:

    • access.log Nginx.
    • Логи програми.
    • Використовують curl -v, щоб бачити заголовки відповіді.

Типова помилка новачка: Думати, що proxy_pass — це магія. Ні, це просто HTTP-запит всередині HTTP-запиту. Якщо ви це зрозумієте, ви зможете дебажити будь-яку проблему.


6. 🧩 Підсумок

Отже, що ми сьогодні розібрали?

  1. Nginx — це посередник. Він як перекладач, який може або передати ваші слова точно, або спотворити їх.
  2. proxy_pass — це механізм пересилання запиту на інший сервер (або порт).
  3. Без proxy_set_header ваш бекенд сліпий. Він не знає реального IP користувача і реального хоста.
  4. Слеш має значення. / у кінці proxy_pass змінює URL, відсутність — залишає як є.

Що ви тепер вмієте? Ви можете "подружити" будь-який бекенд (Python, Go, Node.js) з Nginx так, щоб додаток отримував коректні дані про користувачів. Ви більше не боїтеся, що всі клієнти стануть 127.0.0.1.


🔮 У наступній серії... Ми навчимося керувати трафіком як боги. Що, якщо у вас не один бекенд, а три? Як розподілити навантаження між ними, щоб жоден не впав? Готуйтесь, тема наступного уроку: Load Balancing (Балансування навантаження).

А поки що — практикуйтесь і не забувайте перевіряти логи! Це був CS50... тобто, ваш урок з Nginx. 😉

Успіхів у коді!