Kestrel

High-performance cross-platform web server for ASP.NET Core. Provides optimal performance and memory usage. Optimized for containers and edge devices.

ASP.NET Core.NETWeb ServerCross-platformHTTP Server

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();