Introduction

ASP.NET Core middleware forms a pipeline where each component processes the request in the order it was registered. The order is critical: authentication must come before authorization, exception handling must be first, and endpoint routing must be in the correct position. Getting the order wrong leads to silent failures where middleware does not execute as expected.

Symptoms

  • Authorization allows unauthenticated requests
  • Exception handler does not catch errors from other middleware
  • Static files not served even with UseStaticFiles
  • CORS preflight requests fail
  • Authentication cookies not set
  • UseRouting and UseEndpoints mismatch errors

Example incorrect ordering: ``csharp // WRONG order app.UseAuthorization(); // Runs BEFORE authentication - meaningless app.UseAuthentication(); // Should be FIRST app.UseExceptionHandler("/error"); // Should be first, not third app.UseStaticFiles(); // Should be before routing

Common Causes

  • Adding new middleware without considering its position in the pipeline
  • Misunderstanding the request/response flow (middleware order is reverse for response)
  • Mixing up UseRouting and UseEndpoints with UseAuthorization
  • Placing exception handling after the middleware that might throw
  • Not understanding that UseStaticFiles short-circuits the pipeline

Step-by-Step Fix

  1. 1.Use the correct default middleware order:
  2. 2.```csharp
  3. 3.var app = builder.Build();

// 1. Exception handling MUST be first if (app.Environment.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Error"); app.UseHsts(); }

// 2. HTTPS redirection (before other middleware) app.UseHttpsRedirection();

// 3. Static files (short-circuits for static content) app.UseStaticFiles();

// 4. Routing (determines which endpoint handles the request) app.UseRouting();

// 5. CORS (must be after UseRouting) app.UseCors("DefaultPolicy");

// 6. Authentication (must be before Authorization) app.UseAuthentication();

// 7. Authorization (must be after Authentication) app.UseAuthorization();

// 8. Custom middleware app.UseMiddleware<RequestLoggingMiddleware>();

// 9. Endpoint execution (must be last) app.MapControllers(); app.MapRazorPages(); ```

  1. 1.Understand the short-circuit pattern:
  2. 2.```csharp
  3. 3.// StaticFiles short-circuits: if it finds the file, it does NOT
  4. 4.// call the next middleware. This is why it must come before
  5. 5.// authentication if you want unauthenticated static file access.
  6. 6.app.UseStaticFiles(); // Serves wwwroot files, short-circuits
  7. 7.app.UseAuthentication(); // Only runs for non-static-file requests
  8. 8.app.UseAuthorization(); // Only runs for non-static-file requests
  9. 9.`
  10. 10.Create a custom middleware that respects the pipeline:
  11. 11.```csharp
  12. 12.public class RequestLoggingMiddleware
  13. 13.{
  14. 14.private readonly RequestDelegate _next;
  15. 15.private readonly ILogger<RequestLoggingMiddleware> _logger;

public RequestLoggingMiddleware(RequestDelegate next, ILogger<RequestLoggingMiddleware> logger) { _next = next; _logger = logger; }

public async Task InvokeAsync(HttpContext context) { var sw = Stopwatch.StartNew(); try { await _next(context); // Process request } finally { sw.Stop(); _logger.LogInformation( "{Method} {Path} responded {StatusCode} in {Elapsed}ms", context.Request.Method, context.Request.Path, context.Response.StatusCode, sw.ElapsedMilliseconds); } } } ```

  1. 1.Debug middleware execution order:
  2. 2.```csharp
  3. 3.// Add this temporary middleware at each position to trace execution
  4. 4.app.Use(async (context, next) =>
  5. 5.{
  6. 6.Console.WriteLine($"[1] Before: {context.Request.Path}");
  7. 7.await next();
  8. 8.Console.WriteLine($"[1] After: {context.Response.StatusCode}");
  9. 9.});

// Add similar at each position to see the full pipeline flow ```

Prevention

  • Document the middleware order in your Program.cs with numbered comments
  • Use WebApplicationFactory integration tests to verify middleware behavior
  • Test CORS, authentication, and authorization independently
  • Add a middleware ordering checklist to your code review template
  • Use the Microsoft.AspNetCore.Diagnostics package for standard error handling
  • Never place UseExceptionHandler after middleware that might throw