Як тестувати вебхуки локально
Чому Stripe, GitHub та інші сервіси не бачать ваш localhost і як зловити справжній вебхук на публічній адресі, а потім переслати його собі на машину.
Оновлено:
Ви пишете обробник вебхуків на http://localhost:3000, а Stripe, GitHub чи Telegram вимагають публічну HTTPS-адресу. Розберімо, чому так, які є способи це обійти, і як працювати у два кроки: спершу зловити й роздивитися справжній запит, а тоді переслати його на локальний сервер.
Чому сервіс не може достукатися до localhost
localhost означає «ця сама машина». Коли сервер провайдера надсилає запит на http://localhost:3000/webhook, він стукає сам до себе, а не до вас. До того ж ваш ноутбук зазвичай сидить за NAT і файрволом, тож публічної адреси в нього немає. А більшість провайдерів ще й приймають лише HTTPS з валідним сертифікатом.
Отже, потрібні три речі:
- Публічне доменне ім’я.
- HTTPS із довіреним сертифікатом.
- Спосіб доставити кожен запит із цієї адреси на ваш локальний порт.
Які є варіанти
| Підхід | Як працює | Коли зручно |
|---|---|---|
| Тунель (ngrok, cloudflared тощо) | Клієнт на вашій машині тримає з’єднання з публічним сервером, який проксіює трафік на ваш порт | Коли локальний застосунок має бути доступний довго |
CLI провайдера (наприклад, stripe listen) |
Провайдер сам стрімить події на локальний слухач | Якщо працюєте з одним сервісом і в нього такий CLI є |
| Webhook Inspector | Публічна адреса, що зберігає кожен запит, плюс пересилання на localhost через вкладку браузера або CLI-агент | Коли хочеться спершу побачити сирий запит, а потім повторити чи переслати його |
Тунель шле трафік одразу в застосунок: якщо обробник упав або повернув не той статус, запит пропав, хіба що провайдер повторить його сам. Адреса-перехоплювач зберігає кожен запит, тож його можна роздивитися, виправити код і надіслати ще раз.
Крок 1: зловіть справжній запит
- Відкрийте онлайн-інспектор вебхуків. Реєстрація не потрібна. Ви отримаєте унікальну адресу на кшталт
https://webhook.voronyuk.com/abcd23efgh/stripe, деabcd23efgh— випадковий хеш із 10 символів, аstripe— назва, яку ви обираєте самі (або випадкова). - Вставте цю адресу в налаштування вебхуків провайдера. Будь-який підшлях і query-параметри після неї теж ловляться, тобто
https://webhook.voronyuk.com/abcd23efgh/stripe/orders?x=1теж працює. - Згенеруйте подію в сервісі або надішліть запит самі:
curl -X POST https://webhook.voronyuk.com/abcd23efgh/stripe/orders?x=1 \
-H "Content-Type: application/json" \
-d '{"event":"order.created","id":42}'
Запит з’являється одразу, без оновлення сторінки: метод, шлях, query, заголовки, тіло (JSON красиво відформатований, form data розкодовані, бінарні дані показано в base64), розмір і IP клієнта. За замовчуванням адреса відповідає 200 з тілом {"ok":true,"request_id":N}.
Саме тут видно, що провайдер насправді надсилає: точні заголовки, формат підпису, JSON у тілі чи form-urlencoded. Можна шукати за шляхом, заголовками й тілом, фільтрувати за методом, копіювати будь-який запит як curl або експортувати все в JSON чи HAR.
Крок 2: перешліть запит на localhost
Коли вже зрозуміло, що приходить, віддайте запит своєму коду. У панелі Пересилання є три режими.
Relay через браузер
Поки вкладка інструмента відкрита, вона пересилає кожен запит на ціль на кшталт http://localhost:3000. Запит іде з браузера, тому локальний сервер має дозволяти CORS. Встановлювати нічого не треба.
CLI-агент
CLI-агент працює без браузера, сам перепідключається і доганяє запити, які пропустив, поки був офлайн. Готову команду з токеном скопіюйте з панелі Пересилання:
WI_TOKEN='<токен>' inspector listen https://webhook.voronyuk.com/abcd23efgh/stripe --to localhost:3000
Пересилання з сервера
Якщо ціль публічна, скажімо staging-сервер, запити може пересилати сам сервер. Приватні адреси й localhost заблоковано з міркувань безпеки. Обмеження: до 60 пересилань на хвилину для однієї адреси, тайм-аут 5 секунд, редиректи не виконуються.
Що бачить відправник
Пересилання одностороннє. Застосунок отримує кожен запит, але сервіс-відправник завжди бачить відповідь самого інспектора — стандартну 200 або налаштовану вами, — а не відповідь застосунку. Для сервісів, яким досить 2xx (Stripe, GitHub, Shopify), цього вистачає. Якщо ж сервіс чекає відповідь застосунку в HTTP-відповіді (як-от challenge у url_verification від Slack), дайте йому публічну адресу самого застосунку.
Як зіставляються шляхи
Підшлях і query перехопленого запиту дописуються до цілі. Якщо ціль http://localhost:3000:
https://webhook.voronyuk.com/abcd23efgh/stripe/orders?x=1
→ http://localhost:3000/orders?x=1
Тож якщо обробник висить на /webhook, реєструйте в провайдера https://webhook.voronyuk.com/abcd23efgh/stripe/webhook.
Крок 3: виправте й повторіть
Коли обробник падає, нову подію генерувати не обов’язково. У інструменті для перехоплення вебхуків будь-який запит можна повторити з браузера (працює й для localhost) або з сервера (лише для публічних адрес). Виправили код, повторили запит, і так по колу.
Кілька практичних порад:
- Для перевірки підпису потрібне сире тіло. Більшість провайдерів підписують саме ті байти, які надсилають. Перевіряйте підпис на сирому тілі, а не на JSON, який ви вже розпарсили й серіалізували назад.
- Відповідайте швидко. Провайдери довго не чекають, деякі лише кілька секунд. Спершу поверніть
2xx, важку роботу робіть потім. - Підставляйте власні відповіді. Для кожної адреси можна задати статус (200–599), заголовки, тіло до 64 КБ і затримку до 30 секунд. Так зручно перевіряти, як провайдер реагує на помилки й тайм-аути.
Обмеження
- До 10 адрес на сесію і до 1000 збережених запитів на адресу (найстаріші видаляються).
- Тіло зберігається до 1 МБ. Одна адреса приймає до 500 запитів на хвилину.
- Запити зберігаються 72 години.
- Звичайний
http://перенаправляється на HTTPS (GET — 301, інші методи — 308). - Сесія — це cookie, а не акаунт. Кнопка Session link відкриває ті самі адреси в іншому браузері. Перехоплені запити бачить лише власник сесії.
Покрокові інструкції для конкретних сервісів: вебхуки Stripe, вебхуки GitHub і вебхук Telegram-бота.
Часті питання
Як отримувати вебхуки на localhost без ngrok?
Підійде будь-яка схема, що дає публічну HTTPS-адресу і шлях до вашої машини. У Webhook Inspector запити ловляться на публічній адресі й пересилаються на localhost через relay у браузері або CLI-агент, без налаштування тунелю.
Чому мій локальний ендпоінт нічого не отримує?
Провайдер просто не бачить localhost на вашому комп’ютері. Зареєструйте публічну адресу, переконайтеся, що запит туди доходить, і вже потім пересилайте його локально.
Чи потрібен CORS для relay через браузер?
Так. Relay надсилає запити з вкладки браузера на локальний сервер, тож той має дозволяти cross-origin запити. Якщо не хочеться чіпати CORS, скористайтеся CLI-агентом.
Що буде із запитами, які прийшли, поки ноутбук був вимкнений?
Вони все одно перехоплюються і зберігаються 72 години. CLI-агент після перепідключення доганяє пропущені запити, а будь-який запит можна повторити вручну.
Чи приватна адреса для перехоплення?
Перехоплені запити бачить лише ваша сесія. Але надсилати запити на адресу може будь-хто, хто її знає, тож не світіть її і завжди перевіряйте підпис провайдера в обробнику.
Спробуйте зараз
Отримайте безкоштовний URL для вебхуків і дивіться, як приходять запити, — без реєстрації.
Отримати URL для вебхуків