Main Ecosystems
HomePython CorePandas ReferenceNumPy ScientificFastAPI & PydanticDjango Enterprise
More Ecosystems
Environment & SetupRequests & HTTPAsyncIO ConcurrencyObject-Oriented OOPPyTorch Deep LearningScikit-Learn MLFlask FrameworkWeb ScrapingDatabase & ORMDevOps & Docker

405 Method Not Allowed: The method is not allowed for the requested URL in Flask

Verified FixPython 3.10+Flask 2.x / 3.x / WerkzeugSilo: flask

Quick Fix / Solution Rapide

Add methods=['GET', 'POST'] (or the required HTTP verb) to your @app.route() decorator, as Flask routes only accept GET by default.

Root Cause Analysis

This error occurs when Python tries to route an incoming HTTP request via Flask's URL routing map, but the requested HTTP method (such as POST, PUT, or DELETE) is not listed in the route decorator's allowed methods list.

Flask Routing Rules & Default Verbs

When you define a route using @app.route('/path') without specifying the methods argument, Flask defaults strictly to allowing GET (and automatically handles HEAD and OPTIONS). If a client, HTML <form method="POST">, or JavaScript fetch() sends a POST request to that URL, Flask's URL adapter fails the method check and returns HTTP status code 405 Method Not Allowed with an Allow header indicating the accepted verbs.

Key Scenarios Triggering 405

  1. HTML Form Submission: An HTML form posting data to a route defined with @app.route('/login') lacking methods=['GET', 'POST'].
  2. RESTful API Calls: Sending PUT, PATCH, or DELETE requests to an endpoint configured only for retrieval.
  3. Trailing Slash Redirects: Submitting a POST request to /api/data when the route is defined as /api/data/. Flask issues a 308/301 redirect to the slashed URL, but some HTTP clients downgrade the follow-up request to a GET or fail method validation.
  4. CORS Preflight Failures: Browsers sending preflight OPTIONS requests to endpoints where custom middleware stripped standard method handling.

Reproduction Code (MCVE)

Example: Bug Reproduction
from flask import Flask

app = Flask(__name__)

# By default, routes only accept GET requests
@app.route('/api/submit', methods=['GET'])
def submit():
    return {'status': 'ok'}

# In raw URL map routing, matching an unsupported method raises MethodNotAllowed
adapter = app.url_map.bind('localhost')
adapter.match('/api/submit', method='POST')

Solution 1: Explicitly Declare `methods=['GET', 'POST']` in `@app.route`

Add the list of supported HTTP methods to the decorator and branch inside the view function based on request.method.

Example: Recommended Solution
from flask import Flask, request

app = Flask(__name__)

@app.route('/api/submit', methods=['GET', 'POST'])
def handle_submit():
    if request.method == 'POST':
        return {'action': 'created', 'data': request.get_json(silent=True) or {}}, 201
    return {'action': 'read', 'message': 'Submit form via POST'}

# Validate both GET and POST requests succeed via test client
with app.test_client() as client:
    res_get = client.get('/api/submit')
    res_post = client.post('/api/submit', json={'name': 'PythonFix'})
    print(f'GET status: {res_get.status_code}, POST status: {res_post.status_code}')
    assert res_get.status_code == 200 and res_post.status_code == 201

Solution 2: Use Class-Based Views (`MethodView`) for Clean REST Routing

Implement Flask's MethodView to separate HTTP verbs into distinct class methods (get, post, delete) automatically.

Example: Alternative Solution
from flask import Flask, request
from flask.views import MethodView

app = Flask(__name__)

class ItemAPI(MethodView):
    def get(self, item_id):
        return {'item_id': item_id, 'name': 'Sample Item'}
        
    def post(self, item_id=None):
        return {'status': 'item created'}, 201

# Register class-based view
item_view = ItemAPI.as_view('item_api')
app.add_url_rule('/items/', defaults={'item_id': None}, view_func=item_view, methods=['GET', 'POST'])
app.add_url_rule('/items/<int:item_id>', view_func=item_view, methods=['GET'])

with app.test_client() as client:
    post_res = client.post('/items/')
    print(f'MethodView POST response: {post_res.status_code}')
    assert post_res.status_code == 201

Common Pitfalls & Error Contrasts

A subtle cause of 405 errors is forgetting to define methods on @app.post('/path') vs @app.route('/path'). In Flask 2.0+, helper decorators like @app.post(), @app.get(), @app.put(), and @app.delete() exist as shortcuts. Mixing them up (e.g. decorating with @app.get() and sending a POST) causes 405.

Contrasting 405 with similar HTTP errors:

  • 404 Not Found: The URL endpoint does not match any registered route pattern.
  • 405 Method Not Allowed: The URL exists and matched a route, but the requested HTTP verb is forbidden.
  • 400 Bad Request: The route accepted the HTTP method, but the payload format (e.g. malformed JSON) was rejected by the application handler.