Built-In Middleware — HTTPS, CORS, Static Files and Routing

ASP.NET Core ships with a set of built-in middleware components that handle the most common web application concerns. Each must be registered in a specific order because they depend on each other — UseAuthentication must come before UseAuthorization, and UseRouting must come before both. Knowing what each built-in middleware does and where it belongs in the pipeline is a practical production skill that prevents entire categories of security and correctness bugs.

Standard Middleware Pipeline for a Web API

var app = builder.Build();

// ── 1. Exception handling — first so it catches all downstream exceptions ──
if (app.Environment.IsDevelopment())
    app.UseDeveloperExceptionPage();
else
    app.UseExceptionHandler();   // maps exceptions to ProblemDetails

// ── 2. HSTS — adds Strict-Transport-Security header (production only) ─────
if (!app.Environment.IsDevelopment())
    app.UseHsts();

// ── 3. HTTPS redirection — redirect HTTP to HTTPS ─────────────────────────
app.UseHttpsRedirection();

// ── 4. Static files — serve from wwwroot before routing (no auth required) ─
app.UseStaticFiles();

// ── 5. Routing — must be before CORS, auth, and endpoints ─────────────────
app.UseRouting();

// ── 6. CORS — must be after routing, before auth ──────────────────────────
app.UseCors("AllowAngularDev");

// ── 7. Authentication — must be before authorization ──────────────────────
app.UseAuthentication();

// ── 8. Authorization — must be after authentication ───────────────────────
app.UseAuthorization();

// ── 9. Custom middleware (runs in request/endpoint context) ───────────────
app.UseMiddleware<CorrelationIdMiddleware>();

// ── 10. Endpoint mapping — terminal; must be last ─────────────────────────
app.MapControllers();
app.MapHealthChecks("/health");
Note: UseRouting() and UseEndpoints() (or their .NET 6+ equivalent MapControllers()) bookend the auth middleware. Route matching happens in UseRouting(), which makes the matched endpoint available in HttpContext.GetEndpoint(). Auth middleware reads the endpoint’s metadata (e.g., [Authorize] attributes) to decide whether to challenge or allow the request. Without UseRouting() first, auth middleware cannot see the endpoint metadata and cannot enforce endpoint-level policies.
Tip: Configure CORS in Program.cs with named policies to clearly separate development and production origins. Register multiple policies: builder.Services.AddCors(opts => { opts.AddPolicy("AllowAngularDev", p => p.WithOrigins("http://localhost:4200").AllowAnyMethod().AllowAnyHeader()); opts.AddPolicy("AllowProd", p => p.WithOrigins("https://blogapp.com").AllowAnyMethod().AllowAnyHeader()); }). Apply the correct policy in middleware with app.UseCors(app.Environment.IsDevelopment() ? "AllowAngularDev" : "AllowProd").
Warning: Never use AllowAnyOrigin().AllowCredentials() together in CORS policy — this is explicitly rejected by the CORS specification and will throw an exception. If you need credentials (cookies, Authorization headers), you must specify exact allowed origins with WithOrigins("https://blogapp.com"). The Angular client in Part 6 uses JWT bearer tokens (not cookies), so AllowCredentials() is generally not needed — AllowAnyHeader() to allow the Authorization header is sufficient.

CORS Configuration for Angular

// ── Register CORS policies in Program.cs build phase ─────────────────────
builder.Services.AddCors(options =>
{
    options.AddPolicy("Development", policy =>
        policy
            .WithOrigins("http://localhost:4200")     // Angular dev server
            .AllowAnyMethod()
            .AllowAnyHeader());

    options.AddPolicy("Production", policy =>
        policy
            .WithOrigins(
                "https://blogapp.com",
                "https://www.blogapp.com")
            .WithMethods("GET", "POST", "PUT", "DELETE", "PATCH")
            .WithHeaders("Authorization", "Content-Type", "X-Correlation-Id")
            .SetPreflightMaxAge(TimeSpan.FromHours(24)));   // cache preflight responses
});

// ── Apply in pipeline phase ───────────────────────────────────────────────
var corsPolicy = app.Environment.IsDevelopment() ? "Development" : "Production";
app.UseCors(corsPolicy);

Common Mistakes

Mistake 1 — UseAuthorization before UseAuthentication (authorization without identity)

❌ Wrong — authorization runs before the user is authenticated; all [Authorize] requests fail:

app.UseAuthorization();    // user identity not established yet
app.UseAuthentication();   // too late!

✅ Correct — UseAuthentication must always precede UseAuthorization.

Mistake 2 — UseCors after MapControllers (CORS headers never added)

❌ Wrong — controllers respond before CORS middleware runs; no CORS headers in response.

✅ Correct — UseCors must be before MapControllers (and after UseRouting).

🧠 Test Yourself

An Angular client at http://localhost:4200 calls the API at https://localhost:7001/api/posts. The browser sends a CORS preflight OPTIONS request. What must the API return for the browser to allow the actual request?