Chyba vypadá takto. Integrace pošle POST na váš endpoint. Endpoint funguje, routa existuje, ručně se všechno ověří. Jenže v logu je GET na routu, která přijímá pouze POST, zodpovězený 404 nebo 405.

Příčina je téměř vždy stejná: mezi klientem a endpointem stojí přesměrování s kódem 301 nebo 302. A tyto kódy klienta k zachování metody nezavazují.

Ověření zabere deset sekund:

curl -sS -o /dev/null -L -d 'test=1' \
  -w 'metoda po přesměrování: %{method}\n' \
  'https://httpbingo.org/redirect-to?url=/anything&status_code=301'

Výstup: metoda po přesměrování: GET. Tělo požadavku se ztrácí spolu s ní.

Změňte ve stejném příkazu 301 na 308 a dostanete POST. Jediný rozdíl je stavový kód.

Pět kódů, dvě vlastnosti

Přesměrování se liší ve dvou nezávislých osách: zda jsou trvalá nebo dočasná, a zda přežije metoda požadavku.

Kód Název Trvalost Metoda zachována
301 Moved Permanently trvalé ne
302 Found dočasné ne
303 See Other ne, vždy GET
307 Temporary Redirect dočasné ano
308 Permanent Redirect trvalé ano

Všimněte si symetrie: 307 je 302 se zárukou metody a 308 je 301 se zárukou metody.

Odkud ten zmatek pochází

Formálně specifikace nikdy nevyžadovala měnit metodu u 301 a 302. Jenže rané prohlížeče a klienti to dělali přesně takto, a než si nesouladu někdo všiml, celý web už tak fungoval.

Chování tedy bylo zakotveno jako přípustné: RFC 9110, platná specifikace sémantiky HTTP, dovoluje klientovi u 301 a 302 nahradit POST za GET. Dovoluje, ale nevyžaduje — chování tak zůstává na klientovi a nelze se na ně spolehnout.

Aby existovala jednoznačná varianta, byly zavedeny kódy 307 a 308. U nich je změna metody zakázána: klient musí požadavek zopakovat se stejnou metodou i stejným tělem. Kód 308 je definován ve vlastním RFC 7538, referenční popis nabízí i dokumentace MDN.

Praktický závěr: 301 a 302 jsou bezpečné jen tam, kde je požadavek jistě GET. Všude, kam může dorazit POST, jsou potřeba 307 nebo 308.

Druhá past: trvalá přesměrování se kešují

Na tuhle se zapomíná ještě častěji než na problém s metodou.

301 a 308 prohlašují přesun za trvalý a prohlížeč je smí kešovat dlouho — někdy až do chvíle, kdy uživatel vymaže keš. Poté se serveru už prostě nikdo neptá.

Z toho plyne pravidlo: omylem nasazené 301 se prakticky nedá vzít zpět. Konfiguraci opravíte, ale návštěvníci, kteří starou odpověď už dostali, budou přesměrováváni dál. U 302 a 307 tenhle problém nenastává — jsou z definice dočasné.

Praktická rada: dokud schéma přesunů není ustálené, používejte dočasné kódy. Povýšit později 302 na 301 je snadné; opačná cesta nikoli.

Co vybrat

Stránka se natrvalo přestěhovala a přijímá jen GET → 301. Klasický případ: stará URL článku, změna struktury adres.

Totéž, ale endpoint přijímá POST → 308. Sem patří API, webhooky a formuláře.

Cíl se mění dočasně, jen GET → 302.

Dočasně a metoda musí zůstat zachována → 307.

Po POST poslat uživatele na stránku s výsledkem → 303. To je vzor POST/Redirect/GET: záměrně mění požadavek na GET, aby obnovení stránky formulář neodeslalo znovu. Tady je změna metody účelem, nikoli vadou.

Kde to v praxi selhává

Přesměrování http→https před API. Nejčastější a nejnepříjemnější případ. Klient je nastaven na http://, server odpoví 301 mířícím na https://, metoda se ztratí. Příznak: GET v logu na POST routě. Řeší se náhradou 301 za 308.

Kanonizace hostitele. Přesměrování z www na bez www (nebo naopak) se obvykle staví jako 301. Pro stránky v pořádku, ale pokud na témže hostiteli žije API, platí přesně stejný problém.

Odeslání formulářů. Formulář se odesílá na adresu, která přesměrovává. Uživatel vidí, že „se nic nestalo", protože data se k handleru nikdy nedostala.

Webhooky. Externí služba posílá POST na adresu, kterou jste jí kdysi dali. Pokud se ta adresa přesunula za 301, webhooky tiše přestanou fungovat, zatímco v logu odesílatele svítí úspěšná odpověď na přesměrování.

Všechny čtyři případy mají společný rys: endpoint je zdravý, ale požadavek se k němu nedostane v původní podobě. Diagnostika proto míří špatným směrem — hledá se chyba v handleru, a přitom je v konfiguraci webserveru.

Proč zkracovače používají 302

Krátký odkaz vrací téměř vždy 302 a jde o vědomou volbu, ne o nedbalost.

Důvodem je trvalost, nikoli metoda: cíl krátkého odkazu se může změnit. Použít 301 by znamenalo dovolit prohlížečům zakešovat skok natrvalo, a po změně cíle by část návštěvníků nadále přistávala na staré adrese. Metoda tu roli nehraje — krátké odkazy se procházejí požadavky GET.

Samotný mechanismus popisuje článek o tom, jak fungují zkracovače odkazů.

Co volba kódu nemění

Dva rozšířené omyly.

Odkazová síla. Volba mezi 301 a 302 nemá vliv na předávání síly — Google to potvrdil už v roce 2016. Podrobné zpracování najdete v článku předávají krátké odkazy odkazovou sílu?.

Výběr kanonické adresy. Přesměrování a rel="canonical" řeší různé věci: přesměrování stahuje adresu z oběhu, canonical nechává obě dostupné a označuje tu hlavní. Kdy použít co, popisuje průvodce canonicalem.

Krátký kontrolní seznam

  • Na routách přijímajících POST používat 307 nebo 308, nikoli 302 a 301.
  • Přesměrování http→https kontrolovat zvlášť: bývá nastaveno plošným pravidlem a na API se zapomene.
  • Dokud schéma není ustálené, dočasné kódy. 301 a 308 se kešují a sotva se dají vrátit.
  • Po zpracování formuláře použít 303 — to je správné chování, ne chyba.
  • Řetězce zkrátit na jediný skok: každý skok navíc přidává latenci i riziko.