Kestrel
High-performance cross-platform web server for ASP.NET Core. Provides optimal performance and memory usage. Optimized for containers and edge devices.
Application Server
Kestrel
Overview
Kestrel is a high-performance, cross-platform web server designed specifically for ASP.NET Core applications. As the default web server included with ASP.NET Core, Kestrel provides exceptional performance and memory efficiency while supporting modern web protocols including HTTP/1.1, HTTP/2, and HTTP/3. Built from the ground up for cloud-native and containerized environments, Kestrel offers optimal performance for microservices and scalable web applications. Its lightweight architecture and asynchronous I/O operations make it ideal for high-concurrency scenarios while maintaining compatibility across Windows, Linux, and macOS platforms.
Details
Kestrel 2025 edition represents the pinnacle of .NET web server technology, serving as the foundation for millions of ASP.NET Core applications worldwide. Engineered for modern application architectures, Kestrel leverages .NET's managed memory system and garbage collector to deliver consistent performance characteristics while automatically handling resource management. The server implements advanced features like connection pooling, request pipelining, and efficient buffer management to maximize throughput and minimize latency. Kestrel's modular design allows seamless integration with reverse proxies like IIS, Nginx, and Apache for production deployments, while its self-hosting capabilities enable standalone operation for microservices and edge computing scenarios. With built-in support for HTTPS, WebSockets, and gRPC, Kestrel provides comprehensive protocol support for diverse application requirements.
Key Features
- High Performance: Optimized for throughput and low latency with asynchronous I/O operations
- Cross-platform Support: Runs natively on Windows, Linux, and macOS
- Modern Protocols: Full support for HTTP/1.1, HTTP/2, HTTP/3, and WebSocket connections
- Self-hosting Capable: Can run standalone without external web server dependencies
- Integration Ready: Seamless integration with reverse proxies and cloud platforms
- Container Optimized: Lightweight footprint ideal for containerized deployments
Advantages and Disadvantages
Advantages
- Exceptional performance with minimal memory footprint and high throughput
- Native cross-platform support enabling consistent deployment across operating systems
- Built-in support for modern web protocols including HTTP/2 and HTTP/3
- Seamless integration with the .NET ecosystem and development tools
- Self-hosting capabilities reducing infrastructure complexity for microservices
- Production-ready security features with comprehensive HTTPS and TLS support
- Excellent integration with cloud platforms and container orchestration systems
Disadvantages
- Limited to .NET applications requiring specific runtime and framework versions
- Static file serving capabilities may require additional optimization for high-traffic scenarios
- Configuration complexity can increase for advanced scenarios requiring custom behaviors
- Reverse proxy often recommended for production deployments adding architectural complexity
- Less mature ecosystem compared to traditional web servers like Apache or Nginx
- Performance characteristics tied to .NET runtime behavior and garbage collection patterns
Reference Links
Configuration Examples
Basic Setup and Configuration
// Program.cs - Minimal ASP.NET Core application with Kestrel
var builder = WebApplication.CreateBuilder(args);
// Add services to the container
builder.Services.AddControllers();
var app = builder.Build();
// Configure the HTTP request pipeline
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.MapControllers();
app.MapGet("/", () => "Hello World from Kestrel!");
app.Run();
// Alternative configuration with explicit Kestrel setup
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.UseKestrel(serverOptions =>
{
// Custom Kestrel configuration
serverOptions.Listen(IPAddress.Any, 5000);
serverOptions.Listen(IPAddress.Any, 5001, listenOptions =>
{
listenOptions.UseHttps();
});
});
var app = builder.Build();
app.MapGet("/", () => "Hello from custom Kestrel configuration!");
app.Run();
Comprehensive Kestrel Configuration
// Program.cs - Advanced Kestrel configuration
using Microsoft.AspNetCore.Server.Kestrel.Core;
using System.Net;
using System.Security.Cryptography.X509Certificates;
var builder = WebApplication.CreateBuilder(args);
// Configure Kestrel server options
builder.WebHost.ConfigureKestrel(serverOptions =>
{
// Global server limits
serverOptions.Limits.MaxConcurrentConnections = 1000;
serverOptions.Limits.MaxConcurrentUpgradedConnections = 100;
serverOptions.Limits.MaxRequestBodySize = 30 * 1024 * 1024; // 30MB
// Timeouts
serverOptions.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(2);
serverOptions.Limits.RequestHeadersTimeout = TimeSpan.FromMinutes(1);
// Data rates
serverOptions.Limits.MinRequestBodyDataRate = new MinDataRate(
bytesPerSecond: 100,
gracePeriod: TimeSpan.FromSeconds(10));
serverOptions.Limits.MinResponseDataRate = new MinDataRate(
bytesPerSecond: 100,
gracePeriod: TimeSpan.FromSeconds(10));
// HTTP endpoint
serverOptions.Listen(IPAddress.Any, 5000, listenOptions =>
{
listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
});
// HTTPS endpoint
serverOptions.Listen(IPAddress.Any, 5001, listenOptions =>
{
listenOptions.Protocols = HttpProtocols.Http1AndHttp2AndHttp3;
listenOptions.UseHttps(httpsOptions =>
{
httpsOptions.SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13;
});
});
// Unix socket (Linux/macOS)
if (!OperatingSystem.IsWindows())
{
serverOptions.ListenUnixSocket("/tmp/kestrel.sock", listenOptions =>
{
listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
});
}
// Named pipe (Windows)
if (OperatingSystem.IsWindows())
{
serverOptions.ListenNamedPipe("kestrel-pipe", listenOptions =>
{
listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
});
}
});
// Add services
builder.Services.AddControllers();
builder.Services.AddHealthChecks();
var app = builder.Build();
// Configure middleware pipeline
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.MapHealthChecks("/health");
app.Run();
Configuration via appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Microsoft.AspNetCore.Server.Kestrel": "Information"
}
},
"AllowedHosts": "*",
"Kestrel": {
"Endpoints": {
"Http": {
"Url": "http://0.0.0.0:5000"
},
"Https": {
"Url": "https://0.0.0.0:5001",
"Certificate": {
"Path": "/app/certificates/app.pfx",
"Password": "certificate-password"
}
},
"gRPC": {
"Url": "https://0.0.0.0:5002",
"Protocols": "Http2"
}
},
"Limits": {
"MaxConcurrentConnections": 1000,
"MaxConcurrentUpgradedConnections": 100,
"MaxRequestBodySize": 31457280,
"KeepAliveTimeout": "00:02:00",
"RequestHeadersTimeout": "00:01:00"
},
"DisableStringReuse": false,
"AddServerHeader": false
},
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=MyApp;Trusted_Connection=True;MultipleActiveResultSets=true"
}
}
Configuration Binding in Program.cs
// Program.cs - Using configuration from appsettings.json
var builder = WebApplication.CreateBuilder(args);
// Bind Kestrel configuration from appsettings.json
builder.Services.Configure<KestrelServerOptions>(
builder.Configuration.GetSection("Kestrel"));
// Alternative: Direct configuration binding
builder.WebHost.ConfigureKestrel((context, serverOptions) =>
{
var kestrelSection = context.Configuration.GetSection("Kestrel");
serverOptions.Configure(kestrelSection);
});
var app = builder.Build();
app.MapGet("/", () => "Configured via appsettings.json");
app.Run();
SSL/TLS and HTTPS Configuration
// Program.cs - HTTPS and certificate configuration
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(serverOptions =>
{
// HTTP endpoint for health checks
serverOptions.Listen(IPAddress.Any, 5000);
// HTTPS with certificate file
serverOptions.Listen(IPAddress.Any, 5001, listenOptions =>
{
listenOptions.UseHttps("certificates/app.pfx", "certificate-password");
});
// HTTPS with certificate from store
serverOptions.Listen(IPAddress.Any, 5002, listenOptions =>
{
listenOptions.UseHttps(httpsOptions =>
{
httpsOptions.ServerCertificate = LoadCertificateFromStore();
httpsOptions.SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13;
httpsOptions.ClientCertificateMode = ClientCertificateMode.AllowCertificate;
httpsOptions.AllowAnyClientCertificate();
});
});
// HTTPS with certificate callback
serverOptions.Listen(IPAddress.Any, 5003, listenOptions =>
{
listenOptions.UseHttps((stream, clientHelloInfo, state, cancellationToken) =>
{
// Dynamic certificate selection based on SNI
var hostname = clientHelloInfo.ServerName;
return LoadCertificateForHostname(hostname);
});
});
});
// Certificate loading methods
static X509Certificate2 LoadCertificateFromStore()
{
using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
var certificates = store.Certificates
.Find(X509FindType.FindBySubjectName, "localhost", false);
return certificates.Count > 0 ? certificates[0] :
throw new InvalidOperationException("Certificate not found");
}
static X509Certificate2 LoadCertificateForHostname(string hostname)
{
// Implement dynamic certificate loading logic
return hostname switch
{
"api.example.com" => new X509Certificate2("api.pfx", "password"),
"web.example.com" => new X509Certificate2("web.pfx", "password"),
_ => new X509Certificate2("default.pfx", "password")
};
}
var app = builder.Build();
// Middleware for HTTPS redirection
app.UseHttpsRedirection();
// Security headers middleware
app.Use(async (context, next) =>
{
context.Response.Headers.Add("Strict-Transport-Security",
"max-age=31536000; includeSubDomains");
context.Response.Headers.Add("X-Content-Type-Options", "nosniff");
context.Response.Headers.Add("X-Frame-Options", "DENY");
await next();
});
app.MapGet("/", () => "Secure HTTPS endpoint");
app.Run();
Production Configuration with Host Filtering
// Program.cs - Production configuration with security
var builder = WebApplication.CreateBuilder(args);
// Host filtering configuration
builder.Services.Configure<HostFilteringOptions>(options =>
{
options.AllowedHosts = new List<string>
{
"example.com",
"www.example.com",
"api.example.com",
"localhost"
};
options.IncludeFailureMessage = builder.Environment.IsDevelopment();
});
builder.WebHost.ConfigureKestrel(serverOptions =>
{
// Production settings
serverOptions.AddServerHeader = false;
serverOptions.DisableStringReuse = false;
// Connection limits for production
serverOptions.Limits.MaxConcurrentConnections = 2000;
serverOptions.Limits.MaxRequestBodySize = 10 * 1024 * 1024; // 10MB
serverOptions.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(1);
// Production endpoints
if (builder.Environment.IsProduction())
{
// HTTPS only in production
serverOptions.Listen(IPAddress.Any, 443, listenOptions =>
{
listenOptions.Protocols = HttpProtocols.Http1AndHttp2AndHttp3;
listenOptions.UseHttps();
});
}
else
{
// Development endpoints
serverOptions.Listen(IPAddress.Loopback, 5000);
serverOptions.Listen(IPAddress.Loopback, 5001, listenOptions =>
{
listenOptions.UseHttps();
});
}
});
var app = builder.Build();
// Production middleware pipeline
if (app.Environment.IsProduction())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
else
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseHostFiltering();
app.UseRouting();
app.MapGet("/", () => "Production-ready Kestrel");
app.MapHealthChecks("/health");
app.Run();
Container and Docker Configuration
# Dockerfile for Kestrel application
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY ["MyApp.csproj", "."]
RUN dotnet restore "./MyApp.csproj"
COPY . .
WORKDIR "/src/."
RUN dotnet build "MyApp.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "MyApp.csproj" -c Release -o /app/publish /p:UseAppHost=false
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
# Set environment variables for Kestrel
ENV ASPNETCORE_URLS=http://+:80;https://+:443
ENV ASPNETCORE_ENVIRONMENT=Production
# Create a non-root user
RUN adduser --disabled-password --gecos '' appuser && chown -R appuser /app
USER appuser
ENTRYPOINT ["dotnet", "MyApp.dll"]
// Program.cs - Container-optimized configuration
var builder = WebApplication.CreateBuilder(args);
// Configure Kestrel for container environments
builder.WebHost.ConfigureKestrel(serverOptions =>
{
// Listen on all interfaces for container scenarios
var port = int.Parse(Environment.GetEnvironmentVariable("PORT") ?? "8080");
serverOptions.Listen(IPAddress.Any, port);
// Container-friendly limits
serverOptions.Limits.MaxConcurrentConnections = 1000;
serverOptions.Limits.MaxRequestBodySize = 5 * 1024 * 1024; // 5MB
// Optimize for container startup
serverOptions.Limits.KeepAliveTimeout = TimeSpan.FromSeconds(30);
serverOptions.Limits.RequestHeadersTimeout = TimeSpan.FromSeconds(30);
});
// Add health checks for container orchestration
builder.Services.AddHealthChecks()
.AddCheck("kestrel", () => HealthCheckResult.Healthy("Kestrel is running"));
var app = builder.Build();
// Lightweight middleware for containers
app.UseRouting();
app.MapHealthChecks("/health");
app.MapHealthChecks("/ready");
app.MapGet("/", () => new
{
Service = "MyApp",
Version = "1.0.0",
Environment = app.Environment.EnvironmentName,
Server = "Kestrel",
Timestamp = DateTime.UtcNow
});
app.Run();
Advanced Configuration and Monitoring
// Program.cs - Advanced monitoring and diagnostics
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using System.Diagnostics.Metrics;
var builder = WebApplication.CreateBuilder(args);
// Custom metrics for Kestrel monitoring
var meter = new Meter("MyApp.Kestrel");
var connectionCounter = meter.CreateCounter<int>("kestrel_connections_total");
var requestDuration = meter.CreateHistogram<double>("kestrel_request_duration_seconds");
builder.WebHost.ConfigureKestrel(serverOptions =>
{
// Connection callback for monitoring
serverOptions.ConfigureConnectionLogging();
// Advanced endpoint configuration
serverOptions.Listen(IPAddress.Any, 5000, listenOptions =>
{
listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
// Connection callbacks
listenOptions.Use(async (context, next) =>
{
connectionCounter.Add(1);
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
try
{
await next();
}
finally
{
requestDuration.Record(stopwatch.Elapsed.TotalSeconds);
}
});
});
// gRPC endpoint
serverOptions.Listen(IPAddress.Any, 5001, listenOptions =>
{
listenOptions.Protocols = HttpProtocols.Http2;
listenOptions.UseHttps();
});
});
// Add comprehensive health checks
builder.Services.AddHealthChecks()
.AddCheck<KestrelHealthCheck>("kestrel")
.AddCheck("memory", () =>
{
var memoryUsed = GC.GetTotalMemory(false);
var maxMemory = 500 * 1024 * 1024; // 500MB threshold
return memoryUsed < maxMemory
? HealthCheckResult.Healthy($"Memory usage: {memoryUsed / 1024 / 1024} MB")
: HealthCheckResult.Unhealthy($"High memory usage: {memoryUsed / 1024 / 1024} MB");
});
var app = builder.Build();
// Custom middleware for request logging
app.Use(async (context, next) =>
{
var startTime = DateTime.UtcNow;
await next();
var duration = DateTime.UtcNow - startTime;
app.Logger.LogInformation("Request {Method} {Path} completed in {Duration}ms with status {StatusCode}",
context.Request.Method,
context.Request.Path,
duration.TotalMilliseconds,
context.Response.StatusCode);
});
app.UseRouting();
app.MapHealthChecks("/health", new HealthCheckOptions
{
ResponseWriter = async (context, report) =>
{
var result = new
{
Status = report.Status.ToString(),
Checks = report.Entries.Select(e => new
{
Name = e.Key,
Status = e.Value.Status.ToString(),
Description = e.Value.Description,
Duration = e.Value.Duration.TotalMilliseconds
}),
TotalDuration = report.TotalDuration.TotalMilliseconds
};
await context.Response.WriteAsync(System.Text.Json.JsonSerializer.Serialize(result));
}
});
app.MapGet("/metrics", async context =>
{
// Custom metrics endpoint
var metrics = new
{
Server = "Kestrel",
Uptime = DateTime.UtcNow - Process.GetCurrentProcess().StartTime,
Connections = connectionCounter,
MemoryUsage = GC.GetTotalMemory(false),
RequestCount = requestDuration
};
await context.Response.WriteAsync(System.Text.Json.JsonSerializer.Serialize(metrics));
});
app.Run();
// Custom health check implementation
public class KestrelHealthCheck : IHealthCheck
{
public Task<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context,
CancellationToken cancellationToken = default)
{
try
{
// Implement Kestrel-specific health checks
return Task.FromResult(HealthCheckResult.Healthy("Kestrel server is running"));
}
catch (Exception ex)
{
return Task.FromResult(HealthCheckResult.Unhealthy("Kestrel server error", ex));
}
}
}
Integration with Reverse Proxy (Nginx)
# nginx.conf - Reverse proxy configuration for Kestrel
upstream kestrel_backend {
server 127.0.0.1:5000;
# For load balancing multiple instances
# server 127.0.0.1:5001;
# server 127.0.0.1:5002;
}
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com www.example.com;
# SSL configuration
ssl_certificate /etc/ssl/certs/example.com.crt;
ssl_certificate_key /etc/ssl/private/example.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE+AESGCM:ECDHE+CHACHA20:DHE+AESGCM:DHE+CHACHA20:!aNULL:!MD5:!DSS;
# Headers for Kestrel
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
# Static files served by Nginx
location /static/ {
alias /var/www/static/;
expires 1y;
add_header Cache-Control "public, immutable";
}
# API and application routes to Kestrel
location / {
proxy_pass http://kestrel_backend;
proxy_buffering off;
proxy_cache_bypass $http_upgrade;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 300s;
proxy_connect_timeout 75s;
}
# Health check endpoint
location /health {
proxy_pass http://kestrel_backend/health;
access_log off;
}
}
// Program.cs - Configuration for reverse proxy integration
var builder = WebApplication.CreateBuilder(args);
// Configure forwarded headers for reverse proxy
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
// Configure known proxies (adjust for your setup)
options.KnownProxies.Add(IPAddress.Parse("10.0.0.0"));
options.KnownNetworks.Add(new IPNetwork(IPAddress.Parse("10.0.0.0"), 8));
});
builder.WebHost.ConfigureKestrel(serverOptions =>
{
// Only listen locally when behind reverse proxy
serverOptions.Listen(IPAddress.Loopback, 5000);
});
var app = builder.Build();
// Enable forwarded headers
app.UseForwardedHeaders();
// Trust proxy headers
app.UseRouting();
app.MapGet("/", (HttpContext context) => new
{
RemoteIP = context.Connection.RemoteIpAddress?.ToString(),
Scheme = context.Request.Scheme,
Host = context.Request.Host.ToString(),
Headers = context.Request.Headers.ToDictionary(h => h.Key, h => h.Value.ToString())
});
app.Run();