Common causes
- A form or fetch() call uses POST but the route only accepts GET (or the reverse)
- Nginx serves a static .html file for a POST request, which it never allows
- A trailing slash redirect turns a POST into a GET
- A framework route is missing for that method (Laravel, Express, Flask)
- IIS WebDAV module intercepts PUT and DELETE requests
- A WAF or security rule blocks methods like PUT, PATCH or DELETE
How to fix it
- Check the method and URL. Open developer tools > Network, find the failing request, and note its method and exact URL. Look at the Allow response header, which lists methods the server accepts.
- Match the route definition. Make sure your app defines a route for that method, for example Route::post('/contact', ...) in Laravel or app.post('/contact', ...) in Express.
- Point forms at a script, not a static file. If the form action is a .html file, Nginx returns 405 for POST. Point it at a PHP script or app route that processes the data.
- Avoid redirects on POST. Use the exact URL, including or excluding the trailing slash as the server expects. A 301 redirect makes browsers resend the request as GET.
- Disable WebDAV on IIS if unused. On IIS, remove the WebDAVModule and WebDAV handler in web.config so PUT and DELETE reach your app.
- Review security rules. Check ModSecurity, your CDN firewall or Nginx limit_except blocks for rules that deny the method.
Check which methods a URL allows
curl -i -X OPTIONS https://example.com/api/items
# Look for the Allow: header in the response How to stop it happening again
- Keep routes and front-end calls in sync
- Use consistent trailing slash rules for API endpoints
- Test each HTTP method in your API tests
- Document which methods each endpoint supports