"Request method 'GET' is not supported" is Spring's way of saying HTTP 405 Method Not Allowed: the URL matched a route, but that route only accepts POST (or PUT, PATCH, DELETE), and the request arrived as GET. The plain servlet variant, "HTTP method GET is not supported by this URL", means the same thing coming out of HttpServlet when nobody overrode doGet. Neither is a routing miss (that would be a 404), and neither is a crash. In almost every case the fix is one of two things: make the caller send the right verb, or add a GET handler because the URL is supposed to serve something on GET.

This is the GET counterpart of our older guide on fixing "Request method 'POST' not supported". I wrote it because that post kept ranking for GET queries it did not really answer.

Quick summary

  • The error is an HTTP 405, not a 404 or 500. The route exists; the verb does not match. Spring throws HttpRequestMethodNotSupportedException, HttpServlet answers from its default doGet.
  • Spring's response carries an Allow header (for example Allow: POST). Read it first: it tells you which side to fix.
  • 8 causes cover nearly every report: browser address bar on a POST-only URL, a form defaulting to GET, a missing @GetMapping, a 302 redirect after POST, a login redirect replaying a POST URL, a servlet without doGet, framework method lists in Django, Flask or Express, and an API client using the wrong verb.
  • Express behaves differently: an unmatched GET returns 404 "Cannot GET /path", not 405, unless you add a fallback handler.
  • A 405 on a webhook or form-processing endpoint is correct behavior. Point uptime checks at a GET health endpoint rather than "fixing" the route.

What the error means

Every HTTP request has a method (GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS). A server maps URL plus method to a handler. When the URL matches but no handler is registered for that method, the correct answer is 405 Method Not Allowed with an Allow header naming the methods that would have worked. The HTTP status code reference lists the rest of the 4xx codes, and explains why 405 differs from 501.

The exact wording depends on the stack:

Where you see it Text Status
Spring MVC / Spring Boot log Resolved [org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'GET' is not supported] (Spring 5.x logs "Request method 'GET' not supported") 405
Spring Boot JSON body {"timestamp":"...","status":405,"error":"Method Not Allowed","path":"/api/orders"} 405
Java servlet (Tomcat, Jetty) HTTP method GET is not supported by this URL from HttpServlet.doGet 405 (400 on HTTP/1.0)
Flask / Werkzeug Method Not Allowed. The method is not allowed for the requested URL. 405
Django Log line Method Not Allowed (GET): /orders/, empty body 405
Express Cannot GET /orders 404 by default

What causes "Request method 'GET' is not supported"?

Eight situations account for almost every occurrence I have debugged or seen reported.

  • Someone opened a POST-only URL in the browser address bar, clicked a bookmark to it, or a crawler fetched it. The address bar can only send GET.
  • An HTML form points at a @PostMapping route but has method="get", or no method attribute at all. The default form method is GET.
  • The controller has @PostMapping("/orders") and nothing for GET, but a template links to /orders expecting a list page. Missing @GetMapping, or a @GetMapping on a slightly different path (/orders/ vs /orders, a typo in a path variable).
  • A POST handler returns redirect:/orders and /orders is POST-only. A 302 or 303 redirect makes the browser follow with GET.
  • Spring Security intercepted a POST because the session had expired, sent the user to the login page, then replayed the saved URL as a GET after login. The saved-request handler always redirects with GET.
  • A servlet overrides doPost and nothing else, so HttpServlet's default doGet answers with the "not supported by this URL" message.
  • A Flask route declared methods=['POST'], a Django view wrapped in @require_POST or a class-based view with only post(), an Express app.post with no app.get. The framework is doing what it was told.
  • An API client, script, Postman collection or monitor calls the endpoint with GET when the docs say POST, or the reverse. Some RPC-style APIs describe this as "method not found" because they treat the operation name and the verb together, so check both.

How to read the stack trace

The Spring log line looks like this:

WARN 8123 --- [nio-8080-exec-3] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'GET' is not supported]

There is no controller class in that line, which is why people get stuck. Spring resolved the exception before any handler method ran, so the useful information is elsewhere: the request path (log it, or read it from the JSON body's path field) and the Allow header on the response. Reproduce with curl to see both:

curl -i http://localhost:8080/api/orders
HTTP/1.1 405
Allow: POST
Content-Type: application/json
{"timestamp":"2026-08-15T09:12:44.118+00:00","status":405,"error":"Method Not Allowed","path":"/api/orders"}

Allow: POST says the route is registered for POST only. Now search the codebase for that path inside @PostMapping, @RequestMapping or @PutMapping and you have the handler. If the Allow header lists a method you did not expect, you may be hitting a different route than you thought, for example a catch-all pattern like /api/**.

For a servlet, Tomcat's error page reads "HTTP Status 405, Method Not Allowed" (Tomcat prints a dash between the two) and the message is the HttpServlet string. Open the servlet mapped to that URL in web.xml or @WebServlet and look at which doX methods it declares.

How to fix it step by step

1. Read the status code and the Allow header

Run curl -i (or open the Network tab in DevTools) and confirm you are looking at a 405, not a 404 or a 403. A 404 means no route matched at all; a 403 after a form submit is usually CSRF, and the fix is different. The Allow header decides the rest of the process.

2. Find the handler that matched

In Spring, grep for the path in mapping annotations. In a servlet app, open the servlet class. In Flask, Django or Express, open the route or view definition. You are looking for the method list attached to that path.

3. Decide who is wrong: the client or the route

If the endpoint processes a form, receives a webhook or mutates data, GET is supposed to fail. Fix the caller: the form method, the fetch call, the redirect target, the monitor. If the URL is meant to show a page or return a resource and nobody wrote that handler, the route is what needs to change.

4. Add or widen the handler

Spring Boot, the usual layout with a GET that renders and a POST that processes:

@Controller
@RequestMapping("/orders")
public class OrderController {

    @GetMapping
    public String list(Model model) {
        model.addAttribute("orders", service.findAll());
        return "orders/list";
    }

    @PostMapping
    public String create(@ModelAttribute OrderForm form) {
        service.create(form);
        return "redirect:/orders";
    }
}

If one method must serve both verbs (rare, but some legacy callback URLs need it):

@RequestMapping(value = "/callback", method = {RequestMethod.GET, RequestMethod.POST})
public ResponseEntity<Void> callback(HttpServletRequest request) {
    ...
}

Plain servlet, override doGet instead of relying on the base class:

@WebServlet("/orders")
public class OrderServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        req.getRequestDispatcher("/WEB-INF/orders.jsp").forward(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        // process the submission
        resp.sendRedirect(req.getContextPath() + "/orders");
    }
}

Express, where the default is a 404 and you may want an explicit 405 with Allow:

app.route('/orders')
  .get((req, res) => res.render('orders'))
  .post(createOrder)
  .all((req, res) => res.set('Allow', 'GET, POST').sendStatus(405));

Flask:

@app.route('/orders', methods=['GET', 'POST'])
def orders():
    if request.method == 'POST':
        return create_order()
    return render_template('orders.html')

Django, function view and class-based view:

from django.views.decorators.http import require_http_methods

@require_http_methods(["GET", "POST"])
def orders(request):
    ...

class OrderView(View):
    def get(self, request):
        ...
    def post(self, request):
        ...

5. Check redirects and login flows

After a successful POST, redirect to a URL that answers GET. redirect:/orders is fine once /orders has a @GetMapping; redirect:/orders/create is not if create is POST-only.

If you see the 405 right after logging in, the saved request is being replayed: either give that URL a GET handler that shows the form again, or configure the login success handler with a fixed target (defaultSuccessUrl("/dashboard", true) in Spring Security). CSRF failures return 403, so a 405 in this flow is the redirect, not the token.

6. Verify and monitor

Run the curl command again for both verbs and confirm GET returns 200 (or the intended 405 with Allow) and POST still works. Then add an uptime check against a GET-able health endpoint so a future regression on that route shows up before a user reports it.

When is 405 the correct behavior?

More often than the bug reports suggest. Webhook receivers, form processors, "mark as read" endpoints and anything that mutates state should reject GET, because GET requests get prefetched, cached, bookmarked and crawled. Adding a GET handler that does the POST's work would turn every crawler visit into a write.

For those routes, the right fix is on the client side and the response side: return 405 with an Allow header, do not return 200 with an error message in the body, and do not point an availability check at the POST route with a GET. If a monitor needs to cover the endpoint, give the service a small GET health route and check that instead. Our guide to the most common HTTP status codes covers the 4xx and 5xx codes you are likely to see next to a 405 in the same logs.

Framework cheat sheet

Framework Restrict a route to POST Allow GET too Response when GET is not allowed
Spring MVC @PostMapping("/x") add @GetMapping("/x") or method = {GET, POST} 405, Allow header, HttpRequestMethodNotSupportedException
Java servlet override doPost only override doGet 405 "HTTP method GET is not supported by this URL"
Express app.post('/x', h) app.get('/x', h2) 404 "Cannot GET /x" unless you add .all()
Flask methods=['POST'] methods=['GET', 'POST'] 405 Werkzeug page
Django @require_POST or CBV with post() only @require_http_methods(["GET","POST"]) or add get() 405, HttpResponseNotAllowed

If the message you are chasing says POST instead of GET, the causes mirror these and the POST version of this guide walks through them.