Fix 405 Method Not Allowed in Python Web Apps

Quick answer: HTTP 405 means the server recognized the request method but the target resource does not support it. Compare the client’s method with the route declaration, form method, URL and trailing-slash behavior, and the response’s Allow header. Do not solve a 405 by blindly changing permissions or converting every request to GET.

Python Pool infographic troubleshooting HTTP 405 route method client form OPTIONS and Allow header
A 405 means the server knows the request method but the target resource does not allow it; compare the client method with the route contract.

The message the method is not allowed for the requested URL usually means the URL exists, but the server route does not accept the HTTP method used by the client. A route may allow GET for showing a page, POST for submitting a form, PUT for replacement, or DELETE for removal.

HTTP status code 405 Method Not Allowed is different from 404 Not Found. A 404 means the route was not found. A 405 means the route matched, but the method did not. The server should often include an Allow header showing methods that are valid for that URL.

The fix can be on either side of the request. The client may be sending POST to a route that only allows GET. The server may define a route for GET but forget to include POST for form submission. Forms, JavaScript fetch calls, API clients, reverse proxies, and framework decorators can all be involved.

Do not start by changing every route to accept every method. A 405 response is often protecting a route from unsupported actions. First identify the intended action, then make the client and server agree on that one method.

The MDN 405 status documentation, Requests quickstart, Flask HTTP methods documentation, Django allowed method decorators, and FastAPI path operation documentation are useful references.

Inspect The Response Method And Allow Header

Start by reproducing the request in a small script. Print the status code and the Allow header so you can compare the method you sent with the methods the server accepts.

import requests

url = "https://example.com/api/items"
response = requests.request("POST", url, json={"name": "demo"})

print(response.status_code)
print(response.headers.get("Allow", "Allow header missing"))
print(response.text[:200])

If the response is 405 and the header says GET, HEAD, a POST request is not allowed at that URL. Change the client method or update the server route, depending on the endpoint’s intended behavior.

Send The Method The Endpoint Expects

Client code should choose the HTTP method based on the action. Reading data is usually GET. Creating data is usually POST. Replacing a resource is usually PUT. Removing one is usually DELETE.

import requests

def send_request(action, url, payload=None):
    methods = {
        "read": "GET",
        "create": "POST",
        "replace": "PUT",
        "remove": "DELETE",
    }
    method = methods[action]
    return requests.request(method, url, json=payload, timeout=20)

reply = send_request("create", "https://example.com/api/items", {"name": "demo"})
print(reply.status_code)

Do not retry the same failing method repeatedly. If the method is wrong, retries only create more 405 responses. Confirm the API documentation or route definition and then send the correct method.

Python Pool infographic showing Python client, HTTP method, route, server response 405, and allowed methods
A 405 response means the route exists but does not allow the HTTP method used by the client.

Allow POST In A Flask Route

In Flask, routes accept GET by default. Add methods=["GET", "POST"] when the same URL needs to show a form and handle its submission.

from flask import Flask, jsonify, request

app = Flask(__name__)

@app.route("/contact", methods=["GET", "POST"])
def contact():
    if request.method == "POST":
        return jsonify({"saved": True})
    return jsonify({"form": "contact"})

If a browser form submits to /contact with method="post" but the route only allows GET, Flask returns 405. The route and form method must agree.

Restrict Methods Clearly In Django

In Django, decorators can intentionally restrict a view to specific methods. Make sure the decorator list matches the behavior you want.

from django.http import JsonResponse
from django.views.decorators.http import require_http_methods

@require_http_methods(["GET", "POST"])
def profile(request):
    if request.method == "POST":
        return JsonResponse({"updated": True})
    return JsonResponse({"profile": "current"})

If a view is decorated with only ["GET"], Django should reject POST requests. Add the method only when the view actually handles it.

Python Pool infographic comparing GET POST PUT DELETE route declarations with client requests and responses
Make the client method agree with the route declaration and the operation the endpoint is designed to perform.

Match FastAPI Decorators To The Client

FastAPI uses decorators such as @app.get() and @app.post(). A POST request sent to a route registered only with @app.get() will not match the intended method.

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
    name: str

@app.post("/items")
def create_item(item: Item):
    return {"created": item.name}

Keep the client call and the decorator aligned. If the client creates an item with POST /items, the server should expose a POST operation for that path.

Build A Small 405 Diagnostic Helper

When debugging APIs, use one helper that reports status, final URL, and allowed methods. This keeps client-side troubleshooting consistent.

import requests

def explain_method(url, method):
    response = requests.request(method, url, timeout=20)
    return {
        "status": response.status_code,
        "url": response.url,
        "allow": response.headers.get("Allow"),
    }

print(explain_method("https://example.com/api/items", "POST"))

Use the output to decide the next step. If the URL is wrong after redirects, fix routing. If Allow omits your method, change the method or update the server route. If a proxy strips methods, inspect proxy rules separately.

Fix Checklist

First, reproduce the failing request and note the method, URL, status code, redirect target, and Allow header. Then compare that with the route declaration in Flask, Django, FastAPI, or whichever framework handles the request.

Next, check HTML forms and JavaScript calls. A form defaults to GET unless it sets method="post". A fetch call defaults to GET unless it sets a method. Many 405 errors come from this small mismatch.

Finally, keep method changes intentional. Do not add every method to a route just to silence the error. Allow only the methods the endpoint can safely handle, and update the client to call the endpoint as designed.

After the route and client are aligned, retest with the same URL and without browser cache assumptions. A redirect from a trailing slash, login page, or proxy rule can change the final URL and make the method check happen somewhere else.

Python Pool infographic showing Flask route, methods list, request, handler, and successful response
In Flask, explicitly allow the intended methods and confirm the browser form or client uses the same method.

Compare Route And Client Contracts

A route may accept GET while a form submits POST, or an API may expose PATCH while a client sends PUT. Write down the endpoint’s method contract and test the exact URL, including redirects, because a redirect can change which route receives the request.

import requests

url = "https://example.com/api/items/"
response = requests.post(url, json={"name": "sample"}, allow_redirects=False)
print(response.status_code)
print(response.headers.get("allow"))

Define Methods In A Flask Route

In Flask, list the methods the route is intended to handle and use a trailing-slash policy consistently. A browser form cannot submit arbitrary methods without JavaScript, so match the form’s method to the server route or use a deliberate method-override design.

from flask import Flask, request

app = Flask(__name__)

@app.route("/items/", methods=["GET", "POST"])
def items():
    if request.method == "POST":
        return {"created": True}, 201
    return {"items": []}
Python Pool infographic testing redirects, trailing slash, CORS preflight, proxy, CSRF, and validation
Check redirects, trailing slashes, OPTIONS preflight, proxies, CSRF rules, and framework route logs.

Read Allow And Distinguish Statuses

The Allow header tells a client which methods the resource supports. A 404 suggests the resource or route was not found, a 403 is a permission decision, and a 405 is a method mismatch. Log the method, path, status, and Allow value without logging credentials or private request bodies.

from http import HTTPStatus

def explain(response):
    if response.status_code == HTTPStatus.METHOD_NOT_ALLOWED:
        return response.headers.get("Allow", "check route methods")
    return response.reason

print(explain(response))

Handle OPTIONS And Browser Preflight

Cross-origin browser requests can send OPTIONS before the actual request. The server or framework must answer preflight with the allowed origin, methods, and headers. Do not confuse a failed preflight with the POST or PUT handler itself, and verify the response through the browser network panel.

from flask import Flask

app = Flask(__name__)

@app.route("/api/items/", methods=["OPTIONS"])
def options_items():
    return "", 204, {
        "Allow": "GET, POST, OPTIONS",
        "Access-Control-Allow-Methods": "GET, POST, OPTIONS",
    }

HTTP semantics define 405 as a known method that the target resource does not support. The MDN 405 reference also documents the expected Allow header. Keep the route contract and client request in sync.

For related request contracts, compare Requests JSON calls, write-method failures, and keyword handling when tracing what a client actually sends to a route.

Frequently Asked Questions

What does 405 Method Not Allowed mean?

The server recognizes the HTTP method but the requested resource does not support that method, such as POST sent to a GET-only route.

How do I fix a 405 in Flask?

Add the intended method to the route, change the client or form method to match, and confirm the URL and trailing slash are the same route.

What is the Allow header in a 405 response?

It lists methods the resource supports and helps a client or developer correct the request contract.

Is 405 the same as 404 or 403?

No. 404 concerns a missing resource, 403 concerns permission, and 405 means the resource exists but rejects that method.

Subscribe
Notify of
guest
0 Comments
Oldest
Newest Most Voted