# 405 Method Not Allowed in ASP.NET Core Web APIs: Causes and Fixes

The 405 Method Not Allowed error is one of the most misleading HTTP responses you will encounter building ASP.NET Core Web APIs. The endpoint exists. The route matches. Your request reaches the server. And yet, the response comes back with a 405. The reason it catches even experienced engineers off-guard is that a 405 almost never means the method is missing from your codebase — it means ASP.NET Core's routing engine found a matching route template but the HTTP verb your client sent does not match what the endpoint accepts. That one distinction changes everything about how you approach the diagnosis. For developers who want to go further — including routing diagnostics, edge cases, and a fully wired production API that demonstrates these patterns in context — complete annotated source code is available on [Patreon](https://www.patreon.com/CodingDroplets).

Understanding which layer is responsible for the mismatch is the only way to fix a 405 reliably. The error surfaces from at least six distinct causes in ASP.NET Core — each pointing to a different part of the stack: attribute routing, IIS configuration, route constraints, CORS preflight handling, endpoint routing ambiguity, and reverse proxy verb filtering. Getting these right connects directly to the HTTP verb semantics and routing principles covered in [Chapter 2 of the ASP.NET Core Web API: Zero to Production course](https://aspnetcoreapi.codingdroplets.com/), which walks through correct verb mapping, status code handling, and API route design inside a complete production codebase.

[![ASP.NET Core Web API: Zero to Production](https://newsletter.codingdroplets.com/images/aspnet-core-api-course-banner-1.jpg align="center")](https://aspnetcoreapi.codingdroplets.com/)

According to [RFC 7231, Section 6.5.5](https://tools.ietf.org/html/rfc7231#section-6.5.5), a 405 response must include an `Allow` header listing the HTTP methods the target resource does support. If your 405 response arrives without that header, that is itself a diagnostic signal: the response is coming from somewhere below your ASP.NET Core application — IIS, nginx, a load balancer, or an API gateway — not from your routing engine.

## What Does 405 Mean in ASP.NET Core?

ASP.NET Core's endpoint routing engine returns a 405 when it can match a route template against the request path, but none of the matched endpoints accept the incoming HTTP method. This is in contrast to a 404, which fires when no route template matches the path at all. The framework deliberately distinguishes between "no route found" and "route found but wrong verb" — and that distinction is what makes 405 useful for client developers, but confusing to debug for API developers.

The `Allow` header in a 405 response from ASP.NET Core lists every HTTP method that the matched route does accept. Checking this header tells you exactly which verbs the server recognises for that path — and helps you confirm whether the problem is on your client or your server.

## Cause 1: Missing or Mismatched HTTP Verb Attribute

The most common cause of a 405 in ASP.NET Core is an action method that exists but lacks the correct HTTP verb attribute — or has the wrong one applied.

In ASP.NET Core with attribute routing, every action method that should handle a specific HTTP verb must be decorated explicitly. Without `[HttpGet]`, `[HttpPost]`, `[HttpPut]`, `[HttpDelete]`, `[HttpPatch]`, or `[HttpOptions]` (or their `[Route]` equivalents with a constraint), the endpoint does not register for that verb. A PUT request to a route that only registers `[HttpGet]` endpoints will produce a 405, with `Allow: GET` in the response.

A subtler variant: accidentally applying the wrong namespace's attribute. If you reference `System.Web.Mvc.HttpPost` instead of `Microsoft.AspNetCore.Mvc.HttpPost`, the action may not behave as an API endpoint at all. Always verify that your using directives are pulling from `Microsoft.AspNetCore.Mvc`.

## Cause 2: IIS WebDAV Module Blocking PUT and DELETE

If your ASP.NET Core API is hosted on IIS and PUT or DELETE requests consistently return 405 on those two verbs specifically — but GET and POST work — IIS's WebDAV module is almost certainly the cause.

The WebDAV module in IIS registers its own verb handlers for PUT and DELETE as part of the WebDAV protocol. When it is active, IIS intercepts PUT and DELETE requests before they ever reach the ASP.NET Core module. The result is a 405 response from IIS itself, not from your application — which is why the `Allow` header is often absent or unexpected.

The fix is to remove the WebDAV module and handler from your `web.config`:

```xml
<system.webServer>
  <modules>
    <remove name="WebDAVModule" />
  </modules>
  <handlers>
    <remove name="WebDAV" />
  </handlers>
</system.webServer>
```

If WebDAV was installed at the server level (via Windows Features), you may also need to disable it in IIS Manager at the site level under WebDAV Authoring Rules. If you are not using WebDAV for your application — and most REST APIs do not — removing it entirely is the cleanest fix.

## Cause 3: Route Constraint Mismatch Producing 405 Instead of 404

ASP.NET Core's endpoint routing engine evaluates route constraints during route matching. If a constraint fails — for example, `{id:int}` is applied and the client sends a non-integer identifier — the behaviour depends on whether another route exists that matches the path pattern without the constraint.

When no unconstrained fallback exists, ASP.NET Core returns a 404. But when multiple routes share the same template and one constraint fails, the routing engine may find a partial match on a different HTTP method, which produces a 405. This is the most confusing variant because the error message implies a verb problem when the real issue is a route parameter constraint.

Verify your route constraints are correct and consistent across all action methods on the same controller. Tools like `app.UseRouting()` combined with `app.UseEndpoints(e => e.MapControllers())` and a diagnostic middleware that logs `HttpContext.GetRouteData()` can reveal which routes the engine considered for a given request.

## What Causes 405 Method Not Allowed in ASP.NET Core for PUT and DELETE?

The most frequent cause for PUT and DELETE specifically — and the one that generates the most Stack Overflow questions — is the IIS WebDAV module. If you are on IIS, this is always the first thing to check. If you are on Kestrel or Docker (no IIS), then missing `[HttpPut]` or `[HttpDelete]` attributes on your action methods or an overlapping route registered only for GET and POST is the likely culprit.

## Cause 4: CORS Preflight OPTIONS Request Returning 405

Cross-Origin Resource Sharing adds a layer of verb complexity that affects 405 responses. For non-simple requests — any request with a custom header, or PUT/DELETE/PATCH — browsers send a preflight OPTIONS request before the actual request. This OPTIONS request must be handled by your ASP.NET Core pipeline.

If CORS middleware is not configured, or if `app.UseCors()` is missing from the middleware pipeline, or if it is placed in the wrong position (after `app.UseRouting()` and `app.UseEndpoints()`), the OPTIONS preflight reaches a point in the pipeline with no matching handler and returns 405. The browser then blocks the actual request, and your client-side code sees the 405 before the real verb ever goes out.

The correct middleware order is:

```csharp
app.UseRouting();
app.UseCors();          // Must come after UseRouting, before UseAuthorization
app.UseAuthorization();
app.MapControllers();
```

Adding `[HttpOptions]` as a verb to individual endpoints as a workaround is not the right fix — configure CORS middleware properly and it will handle OPTIONS automatically.

## Cause 5: Overlapping Routes with Conflicting Verb Registrations

When multiple controllers or action methods register routes that resolve to the same path, ASP.NET Core's endpoint routing engine considers all of them as candidates. If one matches the path but accepts only GET, and another matches the path but accepts only POST, a PUT request sees both partial matches and returns a 405 with `Allow: GET, POST`.

This situation typically arises when Minimal API endpoints and controller-based endpoints are mixed in the same application, or when route templates on separate controllers share the same base segment without explicit HTTP verb scoping. Auditing your route registrations with `app.MapGet(...)`, `app.MapPost(...)`, and `[Route]` combinations will surface these overlaps. The `dotnet-trace` tool and ASP.NET Core's built-in route debug endpoint can also make these conflicts visible at runtime.

## Cause 6: Reverse Proxy or API Gateway Verb Filtering

If your ASP.NET Core API is deployed behind nginx, an Azure API Management gateway, or an AWS Application Load Balancer, the proxy or gateway may have verb-level restrictions applied at the infrastructure layer. A gateway policy that explicitly allows only GET and POST passes through to your application correctly, but PUT, DELETE, and PATCH are rejected at the gateway itself before ever reaching Kestrel.

The distinguishing sign here is that the 405 response lacks the ASP.NET Core Problem Details structure and does not include your application's standard error response format. It will look like a raw HTTP response from the gateway with no JSON body. Check your infrastructure configuration — API Management inbound policies, nginx `limit_except` blocks, or ALB listeners — before assuming the problem is in your application code.

## How to Diagnose a 405 Without Guessing

A structured diagnostic sequence eliminates the guesswork:

1.  **Read the** `Allow` **header.** If it is present, your routing engine saw the endpoint — the verb is wrong or missing. If it is absent, the 405 is coming from infrastructure, not ASP.NET Core.
    
2.  **Check the hosting environment.** If IIS, look for WebDAV first. If Docker or Kestrel, skip to step 3.
    
3.  **Examine verb attributes.** Find the action method for the route and verify the `[Http*]` attribute matches the verb the client is sending.
    
4.  **Verify middleware order.** If the issue is on OPTIONS requests, check that `app.UseCors()` is positioned correctly.
    
5.  **Look for route overlaps.** If multiple controllers or Minimal API endpoints register the same path, identify which verbs each one accepts using the ASP.NET Core route debug endpoint.
    
6.  **Check infrastructure config.** If steps 1–5 find nothing, escalate to your proxy or gateway configuration.
    

The [ASP.NET Core 404 troubleshooting guide](https://codingdroplets.com/aspnet-core-404-valid-endpoints-causes-fixes) on Coding Droplets covers the routing diagnosis process in more detail — that same approach applies here, with verb matching as the additional layer.

If you are also seeing [415 Unsupported Media Type](https://codingdroplets.com/415-unsupported-media-type-aspnet-core-causes-fixes) errors from the same clients, the two are often related: misconfigured content negotiation and verb handling often appear together when an API is being integrated for the first time.

## FAQ

### Why does ASP.NET Core return 405 instead of 404 when the route exists?

ASP.NET Core's endpoint routing engine distinguishes between "no route matched" (404) and "a route matched but the HTTP method did not" (405). This distinction is intentional and defined in RFC 7231. When the path matches but the verb does not, a 405 is more informative because it tells the client that the resource *does* exist and lists which methods it accepts in the `Allow` response header.

### How do I fix 405 Method Not Allowed for PUT and DELETE in IIS?

Remove the WebDAV module from your `web.config` using `<remove name="WebDAVModule" />` in the `<modules>` section and `<remove name="WebDAV" />` in the `<handlers>` section. If your server has WebDAV installed at the system level, also disable it in IIS Manager under WebDAV Authoring Rules for your site. This is the most common fix for PUT and DELETE 405 errors on IIS-hosted ASP.NET Core APIs.

### Why is my browser sending an OPTIONS request and getting a 405?

Browsers send an OPTIONS preflight request for non-simple cross-origin requests before sending the actual verb. If your ASP.NET Core application does not have CORS middleware configured, or if `app.UseCors()` is in the wrong position in the middleware pipeline, the OPTIONS request finds no handler and returns a 405. Configure CORS middleware in the correct pipeline position — after `UseRouting()` and before `UseAuthorization()` — to handle OPTIONS preflights automatically.

### How can I see which HTTP methods a route accepts when I get a 405?

The 405 response from ASP.NET Core includes an `Allow` header that lists every HTTP method the matched route accepts. In a browser or API client like Postman, inspect the response headers. In code, `HttpContext.Response.Headers["Allow"]` contains this value. This is the fastest way to confirm whether the problem is on your client (sending the wrong verb) or on your server (missing a verb registration).

### Does 405 ever come from within my ASP.NET Core middleware pipeline?

Yes. Beyond routing, any middleware that returns a response without calling `next()` can return a 405 if it handles certain methods and not others. Custom middleware that handles authentication or rate limiting for specific verbs — but not OPTIONS or HEAD — is a source of unexpected 405 responses. Review your custom middleware implementations if infrastructure and routing causes have been eliminated.

### Can Minimal APIs and controller-based APIs cause 405 conflicts in the same app?

Yes. When both registration approaches are used in the same ASP.NET Core application and route templates overlap, the endpoint routing engine sees both sets of endpoints as candidates. If one set accepts GET and the other accepts POST for the same path, a PUT request will receive a 405 with `Allow: GET, POST`. The fix is to ensure route templates do not overlap, or to explicitly scope each group to its own path prefix.
