Mastering Output Caching & HybridCache in ASP.NET Core
Learn how to implement output caching and HybridCache in ASP.NET Core 8+.
20+ years shipping production .NET services in enterprise systems. Lessons pulled from things that broke in production.
- ✓Basic knowledge of ASP.NET Core (controllers, middleware, services)
- ✓Familiarity with dependency injection
- ✓Understanding of HTTP and REST APIs
- Output caching stores entire HTTP responses to reduce server load and improve response times.
- HybridCache combines in-memory and distributed caching for flexibility and performance.
- Use [OutputCache] attribute to cache controller actions or Razor Pages.
- HybridCache supports cache tags, eviction policies, and serialization.
- Proper cache invalidation is critical to avoid serving stale data.
Imagine you run a busy bakery. Instead of baking a fresh cake for every customer who orders the same type, you bake a few in advance and keep them ready. Output caching is like that: it stores the final webpage (the cake) so that when many users request the same page, the server just serves the cached copy instead of rebuilding it from scratch. HybridCache is like having both a display case (fast but small) and a freezer (slower but bigger) – you use the best storage based on how often the cake is requested.
In modern web applications, performance is paramount. Users expect pages to load in milliseconds, and servers must handle thousands of concurrent requests without breaking a sweat. One of the most effective ways to achieve this is through caching. ASP.NET Core offers powerful caching mechanisms, and with the release of .NET 8, a new player entered the scene: HybridCache. This article dives deep into output caching and HybridCache, showing you how to implement them in production-ready applications. You'll learn the differences between traditional output caching and the new HybridCache, when to use each, and how to avoid common pitfalls. We'll explore real-world scenarios, such as caching a dashboard that displays frequently updated data, and walk through debugging techniques for when things go wrong. By the end, you'll be equipped to boost your app's performance significantly while maintaining data freshness.
What is Output Caching?
Output caching is a technique that stores the entire HTTP response generated by an endpoint (controller action, Razor Page, or Minimal API) and serves it for subsequent identical requests. This avoids re-executing the server-side logic, reducing CPU load and improving response times. In ASP.NET Core, output caching is implemented via middleware and attributes. It supports vary-by rules (e.g., by query string, header, or user) to cache different versions of the same endpoint. The cache can be stored in-memory or in a distributed cache like Redis. Output caching is ideal for endpoints that produce the same response for many users, such as a list of products or a static homepage.
Introducing HybridCache
HybridCache is a new caching abstraction introduced in .NET 8 that combines an in-memory cache (L1) with a distributed cache (L2) like Redis. It provides a unified API for caching data, automatically managing the two tiers. The in-memory cache offers low latency, while the distributed cache provides durability and scalability across multiple servers. HybridCache supports cache tags, which allow you to invalidate groups of cache entries together. It also handles serialization and deserialization of cached objects. This is particularly useful for caching complex objects like database query results or computed data. HybridCache is not a replacement for output caching; rather, it's a lower-level caching mechanism that you can use in your services and middleware.
Configuring Output Caching Policies
Output caching policies define how responses are cached and vary. You can create named policies in the AddOutputCache call and apply them to endpoints using the [OutputCache] attribute or the CacheOutput extension method. Policies can specify expiration, vary-by rules (query, header, route, user), and whether to cache authenticated responses. For example, you might want to cache a product listing but vary by the 'category' query parameter. You can also lock cache entries to prevent multiple requests from triggering the same expensive operation simultaneously (cache stampede protection).
Cache Invalidation Strategies
Cache invalidation is the process of removing or updating cached data when the underlying source changes. Without proper invalidation, users see stale data. In ASP.NET Core output caching, you can invalidate entries by tag using the IOutputCacheStore interface. For HybridCache, you can use the RemoveByTagAsync method. Common strategies include: time-based expiration (TTL), event-driven invalidation (e.g., when a product is updated, invalidate its cache), and dependency-based invalidation (e.g., cache depends on a database row version). For output caching, you can also use the [InvalidateOutputCache] attribute to invalidate caches when a write operation occurs.
HybridCache with Redis in Production
In production, HybridCache typically uses Redis as the distributed cache backend. Redis provides high availability and persistence. To configure HybridCache with Redis, you need to add the Microsoft.Extensions.Caching.StackExchangeRedis package and register the Redis cache service. HybridCache automatically uses it as the L2 cache. You can also configure serialization (e.g., JSON or protobuf) for cached objects. It's important to set appropriate timeouts and connection limits to avoid bottlenecks. Additionally, consider using Redis Cluster for large-scale deployments.
Security Considerations for Caching
Caching can introduce security vulnerabilities if not handled carefully. Never cache responses that contain sensitive user data (e.g., personal information, authentication tokens). Use vary-by-user or disable caching for authenticated endpoints. Be cautious with caching responses that depend on user roles or permissions. Additionally, ensure that cache keys are not predictable to prevent cache poisoning attacks. For output caching, use the [OutputCache(NoStore = true)] attribute on actions that should never be cached. For HybridCache, avoid caching sensitive data altogether, or encrypt the cached values.
Performance Monitoring and Optimization
To ensure your caching strategy is effective, monitor cache hit ratios, memory usage, and latency. ASP.NET Core provides metrics for output caching via the Microsoft.AspNetCore.OutputCaching counters. For HybridCache, you can use the IDistributedCache metrics. Use Application Insights or Prometheus to collect and visualize these metrics. Optimize by adjusting cache sizes, expiration times, and eviction policies. For in-memory cache, set a size limit to prevent memory exhaustion. For distributed cache, ensure network latency is low. Also, consider using cache warming to pre-populate caches after a deployment.
The Stale Dashboard Fiasco
- Always double-check cache duration values in configuration.
- Implement cache invalidation for data that changes frequently.
- Use cache tags to group related cache entries for easy invalidation.
- Monitor cache hit ratios in production to detect stale data.
- Consider using HybridCache for more granular control.
dotnet add package Microsoft.AspNetCore.OutputCachingbuilder.Services.AddOutputCache(options => options.DefaultExpirationTimeSpan = TimeSpan.FromMinutes(1));| File | Command / Code | Purpose |
|---|---|---|
| OutputCacheExample.cs | var builder = WebApplication.CreateBuilder(args); | What is Output Caching? |
| HybridCacheExample.cs | using Microsoft.Extensions.Caching.Hybrid; | Introducing HybridCache |
| PolicyExample.cs | builder.Services.AddOutputCache(options => | Configuring Output Caching Policies |
| InvalidationExample.cs | public class ProductService | Cache Invalidation Strategies |
| RedisHybridCache.cs | using Microsoft.Extensions.Caching.Hybrid; | HybridCache with Redis in Production |
| SecureCaching.cs | [Authorize] | Security Considerations for Caching |
| MonitoringExample.cs | builder.Services.AddOutputCache(options => | Performance Monitoring and Optimization |
Key takeaways
Common mistakes to avoid
3 patternsCaching authenticated responses without varying by user.
Setting cache expiration too long for frequently updated data.
Not configuring a size limit for in-memory cache.
Interview Questions on This Topic
Explain how output caching works in ASP.NET Core and how you would configure it for a controller action that varies by query string.
Frequently Asked Questions
20+ years shipping production .NET services in enterprise systems. Lessons pulled from things that broke in production.
That's ASP.NET. Mark it forged?
3 min read · try the examples if you haven't