This is a viewer only at the moment see the article on how this works.
To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk
This is a preview from the server running through my markdig pipeline
Sunday, 09 November 2025
The ASP.NET Core request and response pipeline is the backbone of every web application built on this framework. Understanding how a request flows through your application and how responses are generated is crucial for building efficient, maintainable, and secure web applications. This series will guide you through every layer of the pipeline, from the moment a HTTP request arrives at your server to when the response is sent back to the client.
Whether you're building APIs, web applications, or microservices, the pipeline is always there, working behind the scenes. By understanding it deeply, you'll be able to optimize performance, implement cross-cutting concerns elegantly, and troubleshoot issues more effectively.
NOTE: This is part of my experiments with AI / a way to spend $1000 Calude Code Web credits. I've fed this a BUNCH of papers, my understanding, questions I had to generate this article. It's fun and fills a gap I haven't seen filled anywhere else.
At its core, the ASP.NET Core request pipeline is a series of components that process HTTP requests and generate HTTP responses. Think of it as a conveyor belt in a factory: the request enters at one end, passes through various stations (components) that examine, modify, or act upon it, and eventually a response emerges at the other end.
This architecture is based on the middleware pattern, where each component (middleware) has a specific responsibility and can:
Let's visualize the complete pipeline architecture in ASP.NET Core 8:
flowchart TB
OS[Operating System and Network TCP IP Socket Layer]
Kestrel[Kestrel Web Server HTTP11 HTTP2 HTTP3 QUIC TLS SSL Termination Connection Management]
Host[Host Layer Application Lifetime Dependency Injection Configuration Logging]
subgraph Middleware_Pipeline [Middleware Pipeline]
EH[Exception Handler Middleware catches exceptions]
HTTPS[HTTPS Redirection Middleware redirects HTTP to HTTPS]
Static[Static Files Middleware serves static content]
Routing[Routing Middleware matches request to endpoint]
AuthN[Authentication Middleware validates identity]
AuthZ[Authorization Middleware validates permissions]
Custom[Custom Middleware app specific logic]
EndpointMW[Endpoint Middleware executes matched endpoint]
end
EndpointExec[Endpoint Execution MVC Controllers and Actions Razor Pages Minimal APIs gRPC SignalR]
Response[Response Generation flows back through middleware]
BackKestrel[Back to Kestrel]
Client[Back to Client]
OS --> Kestrel --> Host --> EH --> HTTPS --> Static --> Routing --> AuthN --> AuthZ --> Custom --> EndpointMW --> EndpointExec --> Response --> BackKestrel --> Client
Let's follow a typical HTTP request as it travels through the pipeline:
When a client makes a request to your application, it arrives as raw TCP/IP packets at your server. The operating system's network stack assembles these packets into a complete HTTP request.
Kestrel, ASP.NET Core's cross-platform web server, receives the request. Kestrel:
HttpContext object that represents both the request and responseThe host provides the execution environment. It:
This is where your application logic begins. Each middleware component:
HttpContextIf the request makes it through all middleware, it reaches an endpoint:
The endpoint executes your business logic and generates a response.
The response flows back through the middleware pipeline in reverse:
sequenceDiagram
autonumber
participant Client
participant OS as OS/Network
participant Kestrel
participant Host
participant MW as Middleware Pipeline
participant Endpoint as Endpoint Execution
Client->>OS: HTTP request
OS->>Kestrel: Forward request
Kestrel->>Host: Create HttpContext
Host->>MW: Invoke pipeline
MW->>Endpoint: Route matched, execute
Endpoint-->>MW: Response
MW-->>Kestrel: Response after post-processing
Kestrel-->>OS: Send HTTP response
OS-->>Client: Response delivered
The HttpContext is the central object in the pipeline. It encapsulates:
public abstract class HttpContext
{
// The incoming request
public abstract HttpRequest Request { get; }
// The outgoing response
public abstract HttpResponse Response { get; }
// User identity and authentication
public abstract ClaimsPrincipal User { get; set; }
// Request-scoped services
public abstract IServiceProvider RequestServices { get; set; }
// Connection information
public abstract ConnectionInfo Connection { get; }
// WebSocket support
public abstract WebSocketManager WebSockets { get; }
// Request cancellation
public abstract CancellationToken RequestAborted { get; set; }
// Session state
public abstract ISession Session { get; }
// Generic feature collection
public abstract IFeatureCollection Features { get; }
// And more...
}
Everything you need to know about the current request and everything you need to build the response is accessible through HttpContext.
Middleware is the building block of the pipeline. At its simplest, middleware is a function that processes a request:
// Basic middleware signature
public delegate Task RequestDelegate(HttpContext context);
// Middleware can be implemented as a method
app.Use(async (context, next) =>
{
// Do something before the next middleware
Console.WriteLine($"Request: {context.Request.Path}");
// Call the next middleware
await next(context);
// Do something after the next middleware
Console.WriteLine($"Response: {context.Response.StatusCode}");
});
A request delegate is a function that can process an HTTP request. The entire middleware pipeline is built from a chain of request delegates:
public delegate Task RequestDelegate(HttpContext context);
Each middleware wraps the next delegate, creating a nested chain of calls.
Let's see a minimal ASP.NET Core 8 application that demonstrates the pipeline:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
// Middleware 1: Logging
app.Use(async (context, next) =>
{
Console.WriteLine($"[{DateTime.UtcNow:yyyy-MM-dd HH:mm:ss}] Request started: {context.Request.Method} {context.Request.Path}");
await next(context);
Console.WriteLine($"[{DateTime.UtcNow:yyyy-MM-dd HH:mm:ss}] Request finished: {context.Response.StatusCode}");
});
// Middleware 2: Custom header
app.Use(async (context, next) =>
{
context.Response.Headers["X-Custom-Header"] = "Hello from middleware!";
await next(context);
});
// Middleware 3: Short-circuit for specific path
app.Use(async (context, next) =>
{
if (context.Request.Path == "/health")
{
context.Response.StatusCode = 200;
await context.Response.WriteAsync("Healthy");
return; // Short-circuit - don't call next()
}
await next(context);
});
// Endpoint
app.MapGet("/", () => "Hello World!");
app.Run();
When you visit http://localhost:5000/, you'll see:
Console output:
[2024-01-15 10:30:45] Request started: GET /
[2024-01-15 10:30:45] Request finished: 200
Browser output:
Hello World!
Response headers:
X-Custom-Header: Hello from middleware!
When you visit http://localhost:5000/health:
Console output:
[2024-01-15 10:30:50] Request started: GET /health
[2024-01-15 10:30:50] Request finished: 200
Browser output:
Healthy
Response headers:
X-Custom-Header: Hello from middleware!
Notice how the third middleware short-circuited the pipeline for the /health path, but all middleware before it still executed.
Understanding the pipeline is crucial because:
Performance Optimization: Knowing the order of execution helps you place expensive operations appropriately and avoid unnecessary work.
Cross-Cutting Concerns: Middleware is perfect for implementing logging, authentication, error handling, and other concerns that affect all requests.
Debugging: When something goes wrong, understanding the pipeline helps you identify where the issue occurred.
Custom Extensions: You can create powerful custom middleware to extend the framework's capabilities.
Security: Understanding how authentication and authorization fit into the pipeline is essential for securing your application.
In this first part, we've established the foundation by understanding what the pipeline is, how it's structured, and how requests flow through it. We've seen the high-level architecture and examined the key concepts.
In the upcoming parts of this series, we'll dive deep into each layer:
Part 2: Server and Hosting Layer - We'll explore Kestrel configuration, host startup, and how to customize the hosting environment.
Part 3: Middleware Pipeline - We'll examine built-in middleware, learn to create custom middleware, and understand middleware ordering.
Part 4: Routing and Endpoints - We'll discover how requests are matched to endpoints and how the endpoint routing system works.
Part 5: MVC, Razor Pages, and Minimal APIs - We'll explore how different application models execute within the pipeline.
Part 6: Advanced Pipeline Hooks - We'll learn about advanced extension points like IStartupFilter, IHostedService, and custom endpoint data sources.
HttpContext is the central object containing all request and response informationThe pipeline is elegant in its simplicity yet powerful in its capabilities. As we progress through this series, you'll gain the knowledge to leverage every layer of this architecture effectively.
© 2025 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.