HTTP Logging Middleware Tips That Actually Work in ASP.NET Core
Ever tried logging HTTP request bodies in ASP.NET Core and ended up with broken pipelines or empty responses? You’re not alone. HTTP logging middleware seems straightforward until you hit the gotchas that make everything fall apart.
I’ve collected 13 practical tips that solve the real problems developers encounter when implementing HTTP logging middleware. Here’s what actually works in production.
The Big 3 Issues (And How to Fix Them)
1. Request Body Disappears After Reading
// The fix: EnableBuffering() + reset position
context.Request.EnableBuffering();
var body = await new StreamReader(context.Request.Body).ReadToEndAsync();
context.Request.Body.Position = 0; // Critical!
2. Response Bodies Don’t Reach Clients
// The fix: Swap streams temporarily
var originalBody = context.Response.Body;
using var memStream = new MemoryStream();
context.Response.Body = memStream;
await next(context);
// Copy back to original stream
memStream.Seek(0, SeekOrigin.Begin);
await memStream.CopyToAsync(originalBody);
context.Response.Body = originalBody;
3. Performance Tanks with Large Payloads
// The fix: Size limits and conditional logging
if (context.Request.ContentLength > 1024)
{
_logger.LogInformation("Request too large, skipping body log");
await next(context);
return;
}
Quick Wins You Can Implement Today
Filter Noise from Health Checks:
var path = context.Request.Path.Value?.ToLower();
if (path?.StartsWith("/health") == true || path?.StartsWith("/metrics") == true)
{
await next(context);
return;
}
Redact Sensitive Data:
private string RedactSensitiveFields(string json)
{
return Regex.Replace(json, @"""(password|token|ssn)""s*:s*""[^""]*""",
@"""$1"":""***""", RegexOptions.IgnoreCase);
}
Skip Binary Content:
var contentType = context.Request.ContentType?.ToLower();
if (contentType?.Contains("multipart/form-data") == true ||
contentType?.Contains("image/") == true)
{
await next(context);
return;
}
Built-in vs Custom: When to Choose What
Feature | Built-in HttpLogging | Custom Middleware |
---|---|---|
Setup | 2 lines of code | ~50 lines |
Filtering | Basic | Full control |
Performance | Good | Optimizable |
Sensitive data | Basic redaction | Custom logic |
Use built-in for: Simple logging, quick debugging
Use custom for: Production apps, compliance requirements, performance optimization
Performance Tips That Matter
- Size limits: Cap at 1KB for dev, 10KB max for production
- Sampling: Log 10% of requests in production instead of 100%
-
Async operations: Always use
ReadToEndAsync()
andCopyToAsync()
- Path filtering: Skip static files, health checks, and metrics endpoints
-
Memory efficiency: Use
PipeReader
andSpan<T>
for zero-allocation processing
Clean Extension Method Pattern
// Registration
builder.Services.AddHttpLogging(options =>
{
options.MaxBodySize = 2048;
options.SamplingRate = 0.1; // 10% in production
options.SkipPaths = new[] { "/health", "/swagger" };
});
// Usage
app.UseHttpLogging();
Production Gotchas to Avoid
- Never log authentication endpoints – passwords and tokens leak
- Always reset stream positions – downstream middleware breaks otherwise
- Set memory limits – large uploads can crash your app
- Use structured logging – makes debugging and monitoring easier
- Handle exceptions gracefully – logging shouldn’t break request processing
Where to Send Your Logs
- Development: Console logging for immediate feedback
- Staging: File logging for integration debugging
- Production: External services (Seq, ELK) with file backup
The key is matching your logging strategy to your monitoring needs.
Want the complete implementation with all 13 tips, advanced filtering, and production-ready code? Check out my full guide: ASP.NET Core HTTP Logging Middleware: 13 Practical Micro Tips
What’s your biggest HTTP logging challenge? Drop a comment – I’d love to help troubleshoot specific scenarios.
P.S. – Always test your logging middleware in a staging environment that mirrors production load. What works with 10 requests might fail with 10,000.