Błąd 405 Method Not Allowed oznacza, że serwer zna adres, pod który wysłano żądanie, ale nie obsługuje dla niego użytej metody HTTP, np. POST, PUT albo DELETE. Adres jest więc poprawny, a problem leży w sposobie, w jaki klient próbuje z niego skorzystać. Najczęściej widzę ten błąd przy formularzach kontaktowych, które przestały działać po zmianie przekierowań, i przy integracjach z API. W tym wpisie pokazuję, jak szybko ustalić, skąd bierze się błąd 405, i jak go naprawiam.
Dlaczego serwer blokuje metodę zapytania
Poprawna odpowiedź 405 zawiera nagłówek Allow z listą metod, które serwer przyjmuje pod danym adresem. To pierwsza
wskazówka, którą sprawdzam. Najczęstsze przyczyny:
| Przyczyna | Typowa sytuacja |
|---|---|
| przekierowanie zamienia POST na GET | formularz wysyła dane na adres bez ukośnika na końcu albo przez HTTP, serwer przekierowuje kodem 301 lub 302, przeglądarka powtarza żądanie jako GET, a skrypt przyjmuje tylko POST |
| POST wysłany do pliku statycznego | atrybut action formularza wskazuje plik .html zamiast skryptu; Nginx odpowiada wtedy 405 |
| nieobsłużone żądanie OPTIONS | przeglądarka przed żądaniem z innej domeny wysyła zapytanie wstępne CORS, a serwer lub aplikacja go nie obsługuje |
| trasa w API zdefiniowana dla innej metody | endpoint w REST API przyjmuje tylko GET, a klient wysyła PUT lub DELETE |
| blokada metod na serwerze lub w zaporze | reguła w konfiguracji, WAF lub ustawienia hostingu przepuszczają tylko GET i POST |
Jak diagnozuję i naprawiam błąd 405
1. Sprawdzam, czy po drodze nie ma przekierowania
To przyczyna, którą znajduję najczęściej. W zakładce Network w narzędziach deweloperskich włączam opcję zachowania historii żądań i wysyłam formularz. Jeśli przed odpowiedzią 405 widzę przekierowanie 301 lub 302, a ostatnie żądanie ma metodę GET, przyczyna jest jasna. To samo sprawdzam w terminalu:
curl -i -X POST https://example.pl/kontakt -d "email=test@example.pl"
Naprawa polega na wysyłaniu formularza od razu na docelowy adres, z właściwym protokołem i końcowym ukośnikiem. Jeśli przekierowanie musi zostać, stosuję kody 307 lub 308, które zachowują metodę i treść żądania. Przy porządkowaniu przekierowań sprawdzam też, czy nie tworzą łańcuchów ani pętli przekierowań.
2. Sprawdzam, dokąd formularz faktycznie wysyła dane
W kodzie formularza sprawdzam atrybuty action i method, a w aplikacjach JavaScript konfigurację żądania w
fetch lub Axios. Częsty błąd to literówka w nazwie metody albo brak opcji method, przez co żądanie wysyłane jest
jako GET. Takie pomyłki wyłapuję tak samo jak inne
błędy w JavaScripcie, czyli przez podgląd żądania w zakładce Network.
3. Sprawdzam obsługę CORS i metody OPTIONS
Gdy błąd pojawia się tylko przy żądaniach z innej domeny, a w zakładce Network widzę nieudane żądanie OPTIONS, problemem jest zapytanie
wstępne CORS. Serwer lub aplikacja muszą na nie odpowiedzieć kodem 204 lub 200 z nagłówkami Access-Control-Allow-Methods
i Access-Control-Allow-Headers. Nie rozwiązuję tego przez dopuszczenie wszystkich metod i domen, tylko przez wskazanie tych,
których aplikacja faktycznie potrzebuje.
4. Sprawdzam definicję trasy i reguły serwera
W API sprawdzam, dla jakich metod zarejestrowana jest trasa, np. parametr methods przy rejestracji endpointu w REST API
WordPressa. Jeśli kod jest poprawny, a błąd zostaje, szukam reguł ograniczających metody w konfiguracji Nginx, w pliku
.htaccess albo w zaporze hostingu. Niektóre hostingi domyślnie blokują PUT
i DELETE i trzeba to zmienić w panelu lub przez wsparcie techniczne. Gdy przyczyna nadal jest niejasna, przechodzę do metod, którymi
debuguję kod JavaScript i PHP.
Błąd 405 a inne kody
Błąd 405 łatwo pomylić z 404 i 403. Błąd 404 oznacza, że adres nie istnieje, a 405, że istnieje, tylko nie dla tej metody. Błąd 403 oznacza z kolei odmowę dostępu niezależnie od metody. Dla SEO błąd 405 zwykle nie ma znaczenia, bo Googlebot pobiera strony metodą GET, a 405 dotyczy formularzy i API. Wyjątkiem jest sytuacja, w której zwykła podstrona zwraca 405 dla GET, co zawsze oznacza błąd w konfiguracji.
Pozostałe błędy HTTP
Każdy kod błędu wskazuje na inną warstwę, więc diagnozę zaczynam od innego miejsca: