This post will walk you through how middleware ordering works, why it matters, and best practices to ensure your ASP.NET Core pipeline runs smoothly.
Making Middleware Order Right in ASP.NET Core
Getting Started
Middleware is at the heart of ASP.NET Core’s request-processing pipeline. Each piece of middleware has a specific role, from handling routing and authentication to serving static files and managing exceptions. But one of the most common pitfalls developers face is getting the middleware order wrong. The order in which middleware components are added can drastically affect the behavior and performance of your application.
In ASP.NET Core, middleware order refers to the sequence in which middleware components are added to the HTTP request pipeline — and therefore, the order in which they handle incoming requests and outgoing responses.
This order is critical, because each middleware can:- Handle a request (and optionally stop further processing), or
- Pass it to the next middleware in the pipeline.
Understanding the Middleware Pipeline
Middleware components are executed in the order they are registered in the Program.cs (or Startup.cs in older projects).
Each middleware can:- Process an incoming request.
- Optionally pass it to the next middleware.
- Optionally handle the response as it comes back up the chain.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Use(async (context, next) =>
{
Console.WriteLine("Middleware A: Before");
await next.Invoke();
Console.WriteLine("Middleware A: After");
});
app.Use(async (context, next) =>
{
Console.WriteLine("Middleware B: Before");
await next.Invoke();
Console.WriteLine("Middleware B: After");
});
app.Run(async context =>
{
Console.WriteLine("Middleware C: Terminal");
await context.Response.WriteAsync("Hello, World!");
});
app.Run();
Execution Order
Request → A (Before) → B (Before) → C → B (After) → A (After) → Response
Why Middleware Order Matters
Middleware order defines the behavioral flow of your app. Placing middleware in the wrong position can cause:- Authorization to fail because routing hasn't occurred yet.
- Static files to never serve because authentication blocks them.
- CORS issues because headers aren’t added early enough.
- Error pages not showing because exception handling runs too late.
If you put UseAuthorization() before UseRouting(), ASP.NET Core won’t know which endpoint is being authorized, and the request may never reach the right handler.
Common Middleware and Their Recommended Order
Here’s a common, recommended order for middleware in ASP.NET Core applications:var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
// 1. Exception handling and diagnostics
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
// 2. HTTPS redirection and static files
app.UseHttpsRedirection();
app.UseStaticFiles();
// 3. Routing
app.UseRouting();
// 4. CORS (before authentication/authorization)
app.UseCors("AllowAll");
// 5. Authentication and authorization
app.UseAuthentication();
app.UseAuthorization();
// 6. Custom middleware (e.g., logging, caching)
app.UseMiddleware<RequestTimingMiddleware>();
// 7. Endpoint mapping
app.MapControllers();
app.MapRazorPages();
app.Run();
Explanation:
- Error handling comes first so it can catch everything below.
- Static files should come before routing — they don’t need routing or authorization.
- Routing determines which endpoint is targeted.
- CORS, Authentication, and Authorization depend on routing metadata, so they come after UseRouting but before MapControllers.
- Custom middleware (logging, metrics, etc.) can go almost anywhere, but be deliberate about what requests they should see.
Diagnosing Middleware Order Problems
If something isn’t working as expected:- Check logs: Many middlewares (like routing or auth) log detailed messages about what they’re doing.
- Add temporary logging middleware:
app.Use(async (context, next) => { Console.WriteLine($"Handling: {context.Request.Path}"); await next.Invoke(); Console.WriteLine($"Finished: {context.Request.Path}"); }); - Consult documentation: The official ASP.NET Core docs list order requirements for each built-in middleware.
- Look for missing
await next()calls — forgetting this means the request stops prematurely.
Best Practices for Managing Middleware Order
- Group related middleware together: Keep exception handling, static files, routing, and security logic in consistent sections.
- Follow official templates: New ASP.NET Core project templates already use a safe default order.
- Avoid redundant middleware: Don’t add the same middleware twice (for example, two UseRouting() calls).
- Use comments for clarity: Future developers will thank you:
// Authentication must come after routing app.UseAuthentication();
Summary
Middleware order in ASP.NET Core isn’t arbitrary, it defines how your app handles every request and response. Getting the order wrong can lead to subtle and frustrating bugs, but understanding the flow helps you build secure, reliable, and performant applications.
Thanks