Wszystkie artykuły
webhook debuggingHTTP errorsauthenticationtroubleshooting

Dlaczego twój webhook zwraca 401 lub 403 (i jak to naprawić)

Webhook zwracający 401 lub 403 wpada do niewielkiej liczby szufladek. Najczęściej to nawet nie problem podpisu — to middleware lub domyślne ustawienie frameworka odrzucające żądanie zanim uruchomi się twój kod. Oto checklista diagnostyczna, której używamy, w kolejności, w jakiej jej używamy.

Najpierw oddziel 401 od 403

Znaczą co innego i naprawa jest inna.

401 Unauthorized: serwer dostał żądanie, spojrzał na poświadczenia (lub podpis) i ich nie zaakceptował. Twój handler prawdopodobnie się uruchomił, obliczył oczekiwany podpis i odrzucił.

403 Forbidden: serwer dostał żądanie i odmówił przetworzenia z innego powodu. Często żądanie nigdy nie dotarło do twojego handlera — middleware lub domyślne ustawienie frameworka wysłało 403.

Otwórz przechwytywanie żądań tunelu i spójrz na odpowiedź. Jeśli widzisz body odpowiedzi swojego handlera („invalid signature"), to 401 z twojego kodu. Jeśli odpowiedź jest generyczna, a logi handlera nic nie pokazują, framework odrzucił je zanim handler się uruchomił.

Pięć najbardziej prawdopodobnych przyczyn

1. Middleware CSRF (Django, Rails, Laravel)

Domyślna ochrona CSRF odrzuca POST bez tokena sesji. Dostawcy webhooków go nie wysyłają. Objawy: 403, generyczne body odpowiedzi, brak logów handlera.

Naprawa: wyklucz trasę webhooka z ochrony CSRF. W Django @csrf_exempt. W Rails skip_before_action :verify_authenticity_token, only: [:webhook]. W Laravel dodaj ścieżkę do VerifyCsrfToken::$except. Przewodnik po webhookach Django przechodzi wersję Django od początku do końca.

2. Middleware auth zastosowany zbyt szeroko

Dodałeś middleware auth globalnie (JWT, sprawdzanie sesji, wymóg API key) i trasa webhooka go odziedziczyła. Dostawca nie wysyła twojego nagłówka auth, więc middleware wysyła 401 zanim handler się uruchomi.

Naprawa: wyklucz ścieżkę webhooka z middleware auth. Uwierzytelnianie webhooka to podpis, nie schemat chroniący resztę twojego API.

3. Nieudana weryfikacja podpisu

Handler się uruchomił, obliczył oczekiwany podpis i nie pasował. Pięć podprzyczyn, mniej więcej w malejącej kolejności częstości:

  • Body sparsowane przez middleware zanim uruchomiła się weryfikacja (surowego body już nie ma).
  • Złe kodowanie (hex vs base64). Zobacz przewodnik po weryfikacji podpisów.
  • Zły sekret (test vs live, dashboard vs CLI, plik env vs runtime).
  • Timestamp za stary (podpis jest ważny, ale przeterminowany — pewnie testujesz starym powtórzonym payloadem).
  • Końcowe białe znaki w sekrecie wczytanym z pliku env.

4. Zarejestrowany zły URL tunelu

Zrestartowałeś tunel i URL się zmienił, ale dashboard dostawcy ma jeszcze stary. Objaw wygląda jak 401, bo żądanie nigdy do ciebie nie dotarło — ale w rzeczywistości inne żądanie trafia do innego serwera (często poprzedniej sesji tunelu, która teraz odmawia lub zwraca 401).

Naprawa: potwierdź, że URL w dashboardzie dostawcy pasuje do twojej obecnej sesji tunelu. Jeśli potrzebujesz stabilnego URL, spójrz na nazwane tunele lub zarezerwowaną subdomenę.

5. Ograniczenia CORS, content-type lub metody

Rzadsze przy webhookach, ale możliwe. Jeśli twoja trasa akceptuje tylko application/json, a dostawca wysyła application/x-www-form-urlencoded (np. Twilio), niektóre frameworki dają 415 — ale źle skonfigurowany może dać 403. Albo trasa jest zarejestrowana dla GET, a dostawca robi POST.

90-sekundowy przepływ diagnostyczny

Oto kolejność, którą przechodzimy:

  1. Przeczytaj body odpowiedzi. Jeśli ma w sobie słowa twojego handlera, żądanie dotarło do handlera. Przejdź do debugowania podpisu. Jeśli to generyczna strona błędu frameworka, żądanie nie dotarło. Przejdź do debugowania middleware.
  2. Sprawdź logi handlera. Czy odpalają się jakieś instrukcje logujące z twojego handlera? Potwierdza, czy żądanie do ciebie dotarło.
  3. Sprawdź zarejestrowany URL. Otwórz dashboard dostawcy. Potwierdź, że URL pasuje do twojego obecnego tunelu. Potwierdź, że wskazuje na właściwą ścieżkę.
  4. Porównaj nagłówki przychodzące. Przechwytywanie tunelu pokazuje dokładne nagłówki, które wysłał dostawca. Porównaj z tym, co czyta twój kod. Nagłówki są niewrażliwe na wielkość liter, ale sposób dostępu różni się między frameworkami — request.headers.get('Stripe-Signature') w jednych, request.META['HTTP_STRIPE_SIGNATURE'] w innych.
  5. Zweryfikuj, że body jest surowe w czasie podpisu. Wypisz długość body tuż przed obliczeniem podpisu. Jeśli zero lub zaskakująco małe, middleware je zjadł.
  6. Sprawdź podwójnie sekret. Porównaj zmienną env w runtime z dashboardem. console.log(process.env.WEBHOOK_SECRET.length) — czy długość pasuje do tego, co pokazuje dashboard?

Z naszego doświadczenia kroki 1 i 5 łapią 80% przypadków w pierwsze dwie minuty.

Przechwyć nieudane żądanie

Najbardziej przydatne narzędzie tutaj to tunel z przechwytywaniem i powtarzaniem żądań. Nie musisz czekać, aż dostawca ponowi — powtarzasz przechwycone żądanie wobec lokalnego handlera podczas debugowania. Każda próba jest natychmiastowa.

Jeśli używasz tunelu, który nie przechwytuje żądań, debugujesz z jedną ręką związaną z tyłu. Przejście na taki, który to robi (albo uruchomienie tcpdump, albo postawienie nginx przed serwerem dev), zwraca się za pierwszym razem, gdy zaoszczędzisz godzinę.

Jak 401 wygląda u każdego dostawcy

  • Stripe: 401 z twojego kodu oznacza nieudaną weryfikację podpisu. Dashboard Stripe pokazuje dostawę jako nieudaną i zawiera twoje body odpowiedzi.
  • GitHub: jeśli handler zwraca 401, GitHub oznacza dostawę jako nieudaną i ponawia. Strona ostatnich dostaw pokazuje odpowiedź.
  • Shopify: 401 z handlera webhooka jest OK dla bezpieczeństwa, ale Shopify ponowi. Po 19 kolejnych niepowodzeniach w ciągu 48 godzin Shopify wyłącza subskrypcję.

Szerszy kontekst debugowania webhooków zobacz w jak debugować webhooki lokalnie. Dołącz do listy oczekujących PortPreview po tunel z wbudowanym przechwytywaniem.

Najczęściej zadawane pytania

Co oznacza 401 na webhooku?
Twój handler dostał żądanie, ocenił poświadczenia lub podpis i je odrzucił. Najczęściej to nieudana weryfikacja podpisu spowodowana parsowaniem body przed weryfikacją, złym kodowaniem (hex vs base64) lub złym sekretem.
Dlaczego dostawcy webhooków widzą 403 z mojej aplikacji?
403 zwykle oznacza, że żądanie nigdy nie dotarło do twojego handlera. Najczęstsza przyczyna to middleware CSRF (w Django, Rails, Laravel) lub middleware auth zastosowany zbyt szeroko. Wyklucz trasę webhooka z CSRF i z ogólnego auth API, aby żądanie mogło dotrzeć do twojego kodu weryfikacji.
Jak debugować błędy auth webhooka bez ponownego wyzwalania zdarzeń?
Użyj tunelu z przechwytywaniem i powtarzaniem żądań. Przechwyć jedną dostawę od dostawcy, potem powtarzaj ją wobec lokalnego handlera tyle razy, ile trzeba podczas debugowania. Każde powtórzenie jest natychmiastowe i nie zależy od dostawcy.