# ASP.NET Core Web API Returns 404 on Valid Endpoints: Causes and Fixes

Getting a 404 response from an ASP.NET Core Web API endpoint you know exists is one of the most confusing experiences in .NET development. The route is registered, the controller is written, the application starts without error — and yet every request comes back with `404 Not Found`. The frustrating part is that there is no exception, no stack trace, and no obvious pointer to what went wrong. If you want to go beyond the causes below and see how routing, middleware ordering, and error handling fit together in a complete production API, the [full implementation with annotated source code is on Patreon](https://www.patreon.com/CodingDroplets) — everything wired together the way enterprise teams actually build it.

Understanding [REST API design and routing correctly from the start](https://aspnetcoreapi.codingdroplets.com/) saves hours of debugging like this. Chapter 2 of the Zero to Production course walks through HTTP verb semantics, route naming, and API versioning inside a real production codebase — so the context of when a 404 happens and why is always clear.

[![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/)

This article covers every common cause of ASP.NET Core Web API returning 404 on a valid endpoint — middleware ordering, missing registrations, route attribute mismatches, and more — with a clear fix for each.

* * *

## What Does a 404 Actually Mean in This Context?

When ASP.NET Core returns 404 on a known endpoint, it means the routing system did not find a match for the incoming request. The request reached the server, the pipeline executed, but no endpoint handler was selected. This is different from a resource-not-found 404 that your own code returns explicitly.

The router evaluates every registered endpoint against the incoming HTTP method, path, route constraints, and optionally API version. If none match, the framework returns 404. The implication is that at least one of those matching criteria is failing — not necessarily the path.

* * *

## Cause 1: `app.MapControllers()` Is Missing

The single most common cause. Your controller and routes are defined correctly, but you forgot to call `app.MapControllers()` in `Program.cs`. Without it, none of your controller routes are registered with the endpoint routing system.

Always verify this call exists. It must appear after any authentication/authorization middleware, but it must appear.

**Fix:** Add `app.MapControllers()` before `app.Run()`.

* * *

## Cause 2: Middleware Pipeline Order Is Wrong

ASP.NET Core's middleware pipeline is ordered, and several middleware components must appear in a specific sequence. Getting the order wrong can silently prevent routing from working.

The correct order for a Web API is:

1.  `app.UseHttpsRedirection()`
    
2.  `app.UseRouting()` *(optional in .NET 6+ with endpoint routing, but sometimes needed explicitly)*
    
3.  `app.UseAuthentication()`
    
4.  `app.UseAuthorization()`
    
5.  `app.MapControllers()`
    

If `app.UseAuthorization()` appears before `app.UseAuthentication()`, the authorization middleware has no identity to evaluate. Rather than throwing an error, it can silently reject the request with a 404 or 401 depending on the policy configuration.

Similarly, placing `app.MapControllers()` before `app.UseAuthorization()` in certain configurations means authorization middleware never runs for your controller endpoints, causing routing surprises.

**Fix:** Follow the middleware ordering above strictly. The [ASP.NET Core Middleware Pipeline Checklist](https://codingdroplets.com/aspnet-core-middleware-pipeline-checklist-dotnet-teams) covers the correct order in full detail.

* * *

## Cause 3: Missing or Mismatched Route Attribute

If a controller uses attribute routing but the `[Route]` attribute on the controller or the `[HttpGet]`/`[HttpPost]` attribute on the action is missing, mistyped, or inconsistent, the router cannot match the URL.

Common issues:

*   Controller has `[Route("api/[controller]")]` but you are calling `/api/Product` when the controller is `ProductsController` (plural vs singular matters)
    
*   Action is missing the HTTP verb attribute (`[HttpGet]`, `[HttpPost]`, etc.) entirely
    
*   Route template has a typo: `[HttpGet("{od}")]` when it should be `[HttpGet("{id}")]`
    
*   Route constraint blocks valid values: `[HttpGet("{id:guid}")]` when you are passing an integer
    

**Fix:** Use `app.UseEndpoints` diagnostics or the route debugger (`Microsoft.AspNetCore.Routing.EndpointMiddleware` at `Debug` log level) to list all registered routes and compare against what you are calling.

* * *

## Cause 4: The `[ApiController]` Attribute Is Missing

The `[ApiController]` attribute on a controller class does more than just enable automatic model validation. It also enables certain routing behaviors. Without it in some project configurations, content negotiation and routing can behave differently, and endpoints can silently fail to match.

**Fix:** Ensure every Web API controller carries both `[ApiController]` and `[Route("api/[controller]")]` (or a specific route template). Inheriting from `ControllerBase` is correct for APIs; inheriting from `Controller` is for MVC views.

* * *

## Cause 5: API Versioning Returns 404 for Unversioned Requests

If you have added API versioning (via the `Asp.Versioning.Mvc` package) and configured it to require explicit versioning, requests that do not include a version declaration return 404 rather than the endpoint.

By default, `AssumeDefaultVersionWhenUnspecified` is `false`. This means a request to `/api/products` when you have defined only `/api/products?api-version=1.0` returns 404.

**Fix:** Either set `AssumeDefaultVersionWhenUnspecified = true` in your versioning options, or explicitly include the version in every request. The first option is safer during initial roll-out; the second is stricter and preferred for mature APIs.

* * *

## Cause 6: CORS Preflight Returns 404

Cross-Origin Resource Sharing (CORS) preflight requests use the `OPTIONS` HTTP method. If your API does not have a CORS policy configured, the preflight `OPTIONS` request hits the routing system with no matching endpoint and returns 404. The actual `GET` or `POST` request may succeed directly but the browser blocks it after the failed preflight.

**Fix:** Add `app.UseCors()` with an appropriate named policy before `app.UseAuthorization()`. If you are building a fully accessible public API, a permissive development policy is acceptable. Production APIs should whitelist specific origins.

* * *

## Cause 7: Reverse Proxy Strips or Modifies the Path

When your API sits behind a reverse proxy such as Nginx, Azure Front Door, or an AWS Application Load Balancer, the proxy may rewrite the incoming path. If your application expects requests at `/api/products` but the proxy strips the `/api` prefix and forwards only `/products`, routing fails and returns 404.

**Fix:** Use `ForwardedHeaders` middleware and configure `PathBase` correctly if the proxy removes a path prefix. Alternatively, configure the proxy to preserve the full path. Check `app.UsePathBase("/api")` if your app is mounted at a sub-path.

* * *

## How Do You Quickly Diagnose a 404 Caused by Routing?

The fastest diagnostic is to enable endpoint routing logging at `Debug` level in your `appsettings.Development.json`:

```json
"Logging": {
  "LogLevel": {
    "Microsoft.AspNetCore.Routing": "Debug"
  }
}
```

When using a tool like the `Bogus` package or simply `curl`, look for `"No candidates found"` or `"Matched endpoint"` messages in the log output. If the correct endpoint is listed but not matched, the issue is in constraints or middleware. If the endpoint is absent entirely, the registration is missing.

The development environment's endpoint explorer middleware (accessible at `/_endpoints` in recent .NET versions with `MapGet("/_endpoints", ...)`) can also list every registered route and its metadata.

* * *

## How to Prevent This from Recurring

A few structural practices prevent 404 routing surprises from reaching production:

**Integration tests are the primary guard.** A single integration test using `WebApplicationFactory` that calls every controller route once — even with incorrect payloads — will catch missing registrations, wrong verb configurations, and middleware order issues in CI. See the [7 Common ASP.NET Core Dependency Injection Mistakes](https://codingdroplets.com/aspnet-core-dependency-injection-mistakes-and-fixes) article for how DI misconfiguration can indirectly cause routing failures.

**Use conventional routing sparingly.** Attribute routing is explicit: every route is written on the controller. Conventional routing relies on convention matching and can silently exclude actions if the convention does not match. For APIs, attribute routing is almost always the right choice.

**Keep** `Program.cs` **middleware order reviewed.** Treat middleware ordering as an architectural contract. Document the intended order in a comment or teams wiki. Middleware insertion during feature additions is the most common way order bugs creep in.

**Apply API versioning early and consistently.** Retrofitting versioning onto an existing API repeatedly causes 404 regressions. If versioning is in your roadmap, introduce the package early with permissive default settings so it is never the surprise cause of a 404.

* * *

## FAQ

### Why does my ASP.NET Core API return 404 in production but work fine in development?

The most common cause is environment-specific middleware configuration. In development, `app.UseDeveloperExceptionPage()` can catch and reroute certain requests, masking a routing misconfiguration. In production, the actual routing failure surfaces. Also check whether `ASPNETCORE_ENVIRONMENT` is set correctly in production — missing this environment variable defaults to `Production`, which may skip `MapControllers()` registration if you have it under an `if (app.Environment.IsDevelopment())` block.

### Does the HTTP method (verb) matter for 404 vs 405 errors?

Yes, and the distinction matters. If a route exists but the wrong HTTP verb is used, ASP.NET Core returns `405 Method Not Allowed` (not 404) since .NET 7+. If you see 404, the entire route pattern is not matching — not just the verb. Seeing 405 narrows the problem to the verb attribute (`[HttpGet]` vs `[HttpPost]` etc.).

### Can a missing DI registration cause a 404?

Yes, indirectly. If a controller constructor has a dependency that is not registered with the DI container, `IControllerActivator` throws an exception during controller instantiation. If this exception is unhandled or swallowed by a global error handler configured to return 404 instead of 500, you see 404 with no visible error. Check your global exception handler configuration and ensure it returns appropriate status codes per exception type.

### Does adding `[Authorize]` to a controller cause 404s?

Normally no — unauthorized access returns 401 (or redirects to login for MVC apps). However, if `UseAuthentication()` or `UseAuthorization()` is missing from the pipeline, authorization short-circuits in unexpected ways. In some configurations, missing authentication middleware causes the request to never reach the endpoint, appearing as 404. Always follow the correct middleware order.

### My Minimal API endpoint returns 404 but no exception. What should I check?

Minimal API endpoints registered with `app.MapGet(...)`, `app.MapPost(...)`, etc. require that `app.Build()` is called on the `WebApplication` builder before `app.MapGet()` is called, and `app.Run()` is invoked after all mappings. Check that the route `"/api/resource"` does not have a trailing slash conflict (route `/api/resource/` is different from `/api/resource`). Also verify the root path — Minimal API routes are case-insensitive by default but are sensitive to the leading slash.

### Does SSL or HTTPS redirect cause a 404?

Indirectly. `app.UseHttpsRedirection()` redirects HTTP requests to HTTPS. If the HTTPS certificate is not configured correctly, the redirect may fail and the client receives 404. If you are testing locally over plain HTTP and the redirection fails to resolve, the final response after the failed redirect appears as 404. Use HTTPS in development by default to avoid this class of problem.

### Can a misconfigured route constraint silently produce 404?

Yes. Route constraints like `{id:int}` or `{id:guid}` silently reject requests where the value does not match, returning 404 without a validation error. If you define `[HttpGet("{id:int}")]` and call the endpoint with a string value, the constraint fails and the router returns 404. This is by design but frequently catches developers off-guard. Always verify the constraint type matches what your client sends.
