25. HTTP Response Caching
- HTTP Response Caching
About this chapter
Leverage HTTP response caching headers to enable browsers and CDNs to cache responses, reducing server load for public data and read-only endpoints.
- Response caching middleware: Installing and configuring ASP.NET Core caching
- Cache-Control headers: Using ResponseCache attribute to control caching behavior
- Duration and expiration: Setting appropriate cache durations for different endpoints
- Public vs private caching: Differentiating between shared caches and client-only caching
- Query parameter variation: Creating separate cache entries for different parameters
- Cache prevention: Disabling caching for state-mutating operations
Learning outcomes:
- Add response caching middleware to the pipeline
- Use ResponseCache attribute to control caching
- Set appropriate cache durations for different endpoints
- Understand Cache-Control header options
- Cache GET endpoints appropriately
- Prevent caching on POST/PUT/PATCH/DELETE operations
25.1 HTTP Response Caching
Response caching uses HTTP headers to tell browsers and proxies to cache responses. This reduces server load for cacheable endpoints.
dotnet add package Microsoft.AspNetCore.ResponseCaching
// Program.cs
builder.Services.AddResponseCaching();
var app = builder.Build();
app.UseResponseCaching(); // Must be before MapControllers
app.MapControllers();
25.2 Cache-Control Headers
Tell clients how long to cache responses:
[HttpGet("{id}")]
[ResponseCache(Duration = 300, Location = ResponseCacheLocation.Any)]
public async Task<ActionResult<PlatformReadDto>> GetPlatformById(int id)
{
// Sends: Cache-Control: public, max-age=300
// Browsers and intermediate caches will cache for 5 minutes
var platform = await _repository.GetPlatformByIdAsync(id);
return Ok(_mapper.Map<PlatformReadDto>(platform));
}
[HttpGet]
[ResponseCache(Duration = 600, VaryByQueryKeys = new[] { "pageIndex", "pageSize" })]
public async Task<ActionResult> GetPlatforms(int pageIndex = 1, int pageSize = 10)
{
// Different cache entries for different page parameters
// pageIndex=1&pageSize=10 cached separately from pageIndex=2&pageSize=10
return Ok();
}
[HttpPost]
[ResponseCache(NoStore = true)]
public async Task<ActionResult> CreatePlatform(PlatformMutateDto dto)
{
// Sends: Cache-Control: no-store
// Never cache POST requests (they mutate state)
return CreatedAtRoute(nameof(GetPlatformById), new { id = 1 }, dto);
}
[HttpGet("profile")]
[ResponseCache(Duration = 60, Location = ResponseCacheLocation.Client)]
public async Task<ActionResult> GetUserProfile()
{
// Sends: Cache-Control: private, max-age=60
// Only browsers cache this, not intermediate caches (CDNs, proxies)
// Used for user-specific data
return Ok();
}
Caching Rules:
- GET endpoints: Can be cached (idempotent)
- POST/PUT/PATCH/DELETE: Never cache (mutate data)
- Public data: Location = ResponseCacheLocation.Any (CDNs, proxies, browsers)
- User-specific data: Location = ResponseCacheLocation.Client (browsers only)
Note on GraphQL HTTP caching as a criticism