Middleware is powerful because it can affect every request. That is also why it can be difficult to debug: a controller may be perfectly correct while a middleware component changes the request or response around it.
Order is behaviour
A request pipeline is not just a list of features. Each component wraps the ones after it. Moving a component changes what it can see and what can see it.
A simplified CDIS pipeline is:
error handling
-> HTTPS and static files
-> logging and health checks
-> security headers
-> routing
-> session setup
-> authentication
-> authorisation
-> active-user tracking
-> MVC endpoint
Session-aware code cannot run before session state exists. Role-aware code must run after authentication. Response diagnostics that inspect cookies need to observe the response after the endpoint has executed.
Keep cross-cutting rules out of controllers
Request logging, security headers, active-session tracking, and database health checks affect many endpoints. Putting them into every controller action creates duplication and makes it easy to forget a new endpoint.
Middleware gives those rules a single enforcement point. The cost is that each component needs a clear comment explaining its purpose and its placement.
Security headers are policy, not decoration
Content Security Policy, frame restrictions, referrer policy, and content-type protection are part of the browser security boundary. A change to a script or external asset can interact with those headers, so the policy should be documented near the pipeline and verified with the affected page.
Diagnostics should be safe by default
Cookie and configuration diagnostics are useful during migration, but logs are not a safe place for secrets or raw user data. Diagnostics should report state, provider, and target information while redacting credentials and limiting payloads.
The lesson
Middleware works best when the pipeline reads like a design document. Name the purpose, document the ordering constraint, and test the failure mode caused by moving a component. That turns invisible framework behaviour into an explicit architecture.