FastAPI 405 Method Not Allowed (tested)
FastAPI's 405 Method Not Allowed means the path exists but not for the HTTP method you used; the response's Allow header lists the methods it does accept. A 405 on OPTIONS is a missing CORS middleware.
1. 405 vs 307: method and trailing slash
from fastapi import FastAPI
from fastapi.testclient import TestClient
app = FastAPI()
@app.post("/items")
def create(): return {"created": True}
@app.get("/users/")
def users(): return []
c = TestClient(app)
for method, path in [("GET", "/items"), ("POST", "/items"), ("POST", "/items/"), ("GET", "/users")]:
r = c.request(method, path, follow_redirects=False)
print(f"{method} {path}: {r.status_code} allow={r.headers.get('allow')} location={r.headers.get('location')} {r.text[:40]}")
Output:
GET /items: 405 allow=POST location=None {"detail":"Method Not Allowed"}
POST /items: 200 allow=None location=None {"created":true}
POST /items/: 307 allow=None location=http://testserver/items
GET /users: 307 allow=None location=http://testserver/users/
FastAPI returns 405 Method Not Allowed when the path exists but not for that HTTP method, and the Allow header lists the methods that do exist (here POST). The usual causes: opening a POST-only URL in the browser (a browser address bar always sends GET), a client sending GET instead of POST, or a form whose method is wrong.
A path that differs only by a trailing slash gets a 307 redirect to the other form, not a 405. 307 keeps the method and body, but some clients and proxies do not follow it, or follow it to an http:// URL behind an HTTPS proxy, which can surface as a different error. Call the exact path you declared.
2. 405 on OPTIONS is a CORS problem
If the 405 in your log is for an OPTIONS request, the browser is sending a CORS preflight and you have no CORSMiddleware. See FastAPI CORS error on localhost, where we reproduced exactly that.
Checklist
- Read the
Allowheader: it tells you which methods the route accepts. - Testing a POST route? Use
/docsorcurl -X POST, not the address bar. - Match the decorator (
@app.post,@app.put) to the client's method, and the path including any trailing slash. - OPTIONS 405: add
CORSMiddleware.
Related: FastAPI 422 Unprocessable Entity.
Frequently asked questions
Why does FastAPI return 405 Method Not Allowed?
The route exists for another method, e.g. you sent GET (a browser address bar) to an @app.post route. The Allow header lists the accepted methods.
Why does FastAPI return 405 for OPTIONS?
That is a browser CORS preflight and no CORSMiddleware is installed. Add CORSMiddleware with your frontend origin.
Does a trailing slash cause 405 in FastAPI?
No. In FastAPI 0.161.1 a trailing-slash mismatch gets a 307 redirect to the declared path; a 405 means the method is wrong.
Written with AI assistance. Measured on 2026-10-23 with FastAPI 0.161.1 and Python 3.14.7 (TestClient, python:3-alpine in Docker). Each block was run verbatim and the output pasted unedited. PlainHeaders is our extension; Python and FastAPI are free.