Вебхук: что это и как принять вебхук на своём компьютере
Что такое вебхук, чем входящий отличается от исходящего и как принять вебхук на localhost: туннель, постоянный адрес и проверка подписи.
Вебхук — это HTTP-запрос, который сервис сам отправляет на ваш адрес, когда что-то случилось: пришла оплата, создали сделку, пользователь написал боту. Чтобы принять вебхук на своём компьютере, нужен публичный адрес, который ведёт на localhost. Его даёт туннель: fxtunnel http 8080 печатает адрес вида https://lemon.fxtun.ru, вы вписываете его в настройки сервиса, и запросы начинают приходить в ваш локальный обработчик.
Дальше по порядку: что такое вебхук и в какую сторону он идёт, почему до localhost он не доходит, как принять его через туннель, как не перенастраивать сервис после каждого перезапуска и как проверить, что запрос пришёл от того, от кого надо.
Что такое вебхук простыми словами
Обычный API работает по принципу «спросил — получил»: ваш код сам ходит в сервис и забирает данные. Вебхук переворачивает направление. Вы один раз сообщаете сервису адрес, а он сам присылает туда POST-запрос при каждом событии. Опрашивать API раз в минуту больше не нужно, событие приходит через секунды.
Путаница начинается со словами «входящий» и «исходящий». Они зависят от того, с чьей стороны смотреть, и разные сервисы называют их по-разному. В документации Битрикс24, например:
- входящий вебхук — адрес, по которому ваш код вызывает методы REST API Битрикс24;
- исходящий вебхук — Битрикс24 сам отправляет событие на адрес вашего обработчика.
Для туннеля важен один вопрос: кто кому отправляет запрос.
Вы отправляете запрос сервису — туннель не нужен. Сюда относится вебхук канала Discord: это адрес, на который ваш скрипт шлёт сообщения, и они появляются в канале. В документации Discord их так и описывают: простой способ публиковать сообщения в каналы, без бота и авторизации. Ваш компьютер здесь клиент, запрос уходит наружу, и публичный адрес ему ни к чему. То же с входящим вебхуком Битрикс24.
Сервис отправляет запрос вам — нужен публичный адрес. Исходящие вебхуки Битрикс24, уведомления об оплате, setWebhook у Telegram-бота, Interactions Endpoint URL у приложения Discord. Discord прямо пишет, что это публичный адрес, на который он присылает взаимодействия. Вот этим запросам и нужен туннель.
Почему вебхук не доходит до localhost
localhost — это адрес «сам себе». Когда сервис в интернете пытается отправить запрос на http://localhost:8080/hook, он стучится в собственный сервер, а не в ваш ноутбук. Указать ему IP вашего компьютера тоже не выйдет: дома он спрятан за роутером, а у многих провайдеров ещё и за общим NAT с серым адресом. Запрос из интернета до вашей машины просто не находит дороги.
Вторая преграда — HTTPS. Многие сервисы шлют вебхуки только на https://. Например, Telegram требует HTTPS и принимает вебхуки только на портах 443, 80, 88 и 8443 (Bot API, setWebhook). Самоподписанный сертификат на ноутбуке — отдельная морока.
Туннель снимает обе проблемы. Клиент fxtunnel сам подключается к серверу fxTunnel исходящим соединением, поэтому роутер и NAT ему не мешают. Сервер принимает запросы на https://<поддомен>.fxtun.ru с настоящим сертификатом и передаёт их вашему локальному обработчику.
POST /hook lemon.fxtun.ru fxtunnel http 8080localhost:8080 Принимаем вебхук за пять минут
Обработчик
Для проверки хватит обработчика на стандартной библиотеке Python. Он печатает каждый POST-запрос и отвечает 200:
# hook.py
from http.server import BaseHTTPRequestHandler, HTTPServer
class Hook(BaseHTTPRequestHandler):
def do_POST(self):
body = self.rfile.read(int(self.headers.get("Content-Length", 0)))
print(self.command, self.path, body.decode(errors="replace"), flush=True)
self.send_response(200)
self.end_headers()
HTTPServer(("127.0.0.1", 8080), Hook).serve_forever()python3 hook.pyЕсли у вас уже есть приложение на Express, Django или Laravel, запустите его: туннелю всё равно, что слушает порт.
Туннель
Установите клиент и войдите (один раз):
curl -fsSL https://fxtun.ru/install.sh | sh
fxtunnel loginНа Windows вместо первой строки выполните в PowerShell irm https://fxtun.ru/install.ps1 | iex.
Откройте туннель к порту обработчика:
fxtunnel http 8080Connecting to fxtunnel server...
Tunnel established!
HTTP: http://lemon.fxtun.ru
HTTPS: https://lemon.fxtun.ru
Forwarding to localhost:8080
Ready to receive connections
Проверьте с любого компьютера или прямо с этого:
curl -X POST https://lemon.fxtun.ru/hook -d '{"event":"test"}'В окне hook.py появится строка POST /hook {"event":"test"}. Путь запроса сохраняется: сервис стучится в /hook, и обработчик видит именно /hook.
Адрес в настройках сервиса
Осталось вписать https://lemon.fxtun.ru/hook туда, куда сервис просит адрес обработчика. У GitHub это Payload URL в настройках вебхука репозитория, у Telegram — параметр url метода setWebhook, у Битрикс24 — адрес обработчика в исходящем вебхуке.
У поддоменов fxtun.ru есть предупредительная страница для тех, кто впервые открывает туннель в браузере. Вебхуки она не задевает: страница показывается только на GET-запросы, в ответ на которые ваш сервер отдал HTML. POST-запрос сервиса уходит прямо в обработчик (документация HTTP-туннелей).
Постоянный адрес: не перенастраивать сервис каждый раз
Здесь большинство и спотыкается. Без флага --domain каждый запуск fxtunnel http 8080 даёт новый случайный поддомен. Сегодня lemon, завтра river. Сервис же продолжает слать события на вчерашний адрес, и они уходят в пустоту, пока вы не впишете новый.
Первый шаг — выбрать имя самому:
fxtunnel http 8080 --domain myappАдрес станет https://myapp.fxtun.ru и не изменится между запусками, пока имя свободно. Этот флаг работает на любом тарифе. Слабое место одно: пока ваш туннель выключен, имя может занять другой пользователь.
Чтобы имя принадлежало только вам, его резервируют:
fxtunnel domains add myappReserved: myapp → https://myapp.fxtun.ru
После этого чужой туннель на myapp сервер не пустит. Резервировать поддомены можно с тарифа Base: на нём их пять. Для рабочего проекта можно подключить и свой домен вида hooks.example.com. Как устроены все три варианта и что выбрать, разобрано в статье о постоянном адресе туннеля.
Проверка подписи: чужие запросы тоже дойдут
Адрес туннеля публичный. Если кто-то его узнает, он может прислать туда что угодно, в том числе поддельное «оплата прошла». Поэтому серьёзные сервисы подписывают вебхуки, а обработчик проверяет подпись до того, как что-то делать.
Общий принцип почти везде одинаковый:
- В настройках вебхука вы задаёте секрет, или сервис выдаёт его сам.
- Сервис считает подпись от тела запроса и секрета и кладёт её в заголовок.
- Обработчик считает то же самое от сырого тела, до разбора JSON, и сравнивает. Не совпало — отвечает
401и ничего не делает.
Детали у каждого сервиса свои:
- GitHub кладёт в заголовок
X-Hub-Signature-256значениеsha256=и HMAC-SHA256 тела. Сравнивать советует функцией постоянного времени, а не обычным==(документация GitHub). - Telegram подписи не считает. Он присылает в заголовке
X-Telegram-Bot-Api-Secret-Tokenсекрет, который вы передали вsetWebhookпараметромsecret_token(Bot API). - Discord подписывает взаимодействия ключом Ed25519 (заголовки
X-Signature-Ed25519иX-Signature-Timestamp) и ждёт401на неверную подпись (документация Discord).
Проверка в стиле GitHub на той же стандартной библиотеке:
import hashlib
import hmac
import os
SECRET = os.environ["WEBHOOK_SECRET"].encode()
def signature_ok(body: bytes, header: str) -> bool:
expected = "sha256=" + hmac.new(SECRET, body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, header)В do_POST из примера выше вызовите signature_ok(body, self.headers.get("X-Hub-Signature-256", "")) и при False отвечайте 401.
Ещё два правила, которые экономят нервы. Отвечайте 2xx быстро, а тяжёлую работу делайте после ответа: сервисы ждут ответа ограниченное время. И будьте готовы к повторной доставке одного и того же события: что именно сервис делает при ошибке и таймауте, написано в его документации.
Отладка: посмотреть запрос и отправить его снова
Самое утомительное в работе с вебхуками — вызвать событие ещё раз. Чтобы проверить исправление в обработчике, приходится заново создавать сделку, проводить тестовую оплату или писать боту.
В клиенте fxtunnel для этого есть инспектор. Он открывается на http://127.0.0.1:4040, показывает каждый пришедший запрос с заголовками и телом, а кнопкой повтора отправляет сохранённый запрос в ваш локальный обработчик ещё раз. Сервис при этом не участвует: поправили код, нажали повтор, посмотрели ответ. Инспектор работает только на этом компьютере и доступен с тарифа Base. Подробнее — в разборе инспектора трафика.
На бесплатном тарифе инспектор выключен. Там выручает печать запросов в самом обработчике, как в hook.py.
Что дальше
- Как тестировать вебхуки GitHub, Stripe и других сервисов — в статье «Тестирование вебхуков через туннель».
- Боты Telegram и Discord на локальной машине — отдельный разбор.
- Вебхуки конкретных сервисов: Битрикс24, уведомления ЮKassa, Callback API ВКонтакте, amoCRM, Telegram Mini App.
- Чтобы не менять адрес в сервисе, — постоянный адрес туннеля.
Постоянный адрес
Закрепить поддомен за собой можно с тарифа Base, там же — свой домен вида app.example.com с сертификатом. На бесплатном тарифе адрес меняется при каждом запуске.
Читайте дальше
Telegram и Discord боты через туннель — гайд
Telegram и Discord доставляют события только на публичный HTTPS-адрес. Показываем, как дать боту такой адрес через fxTunnel и отлаживать вебхуки в Inspector.
13 мин · 19 марта 2026WordPress на localhost через туннель — гайд
WordPress хранит URL в базе и ломается за туннелем без правки wp-config.php. Показываем настройку WP_HOME/WP_SITEURL, демо клиенту и вебхуки WooCommerce.
12 мин · 15 марта 2026Тестирование вебхуков через туннель — Stripe, GitHub
Stripe и GitHub не могут достучаться до localhost. Показываем, как принимать вебхуки через fxTunnel, закреплять URL и отлаживать запросы в Inspector.
7 мин · 3 февраля 2026