Description
Is there an existing issue for this?
- I have searched the existing issues
Is your feature request related to a problem? Please describe the problem.
In ASP.NET Core Minimal APIs, however, there is currently no built-in support for setting Cache-Control
headers declaratively—unlike what exists in MVC via [ResponseCache] or [OutputCache].
This gap forces developers to manually set cache headers in each endpoint, which is repetitive, error-prone, and inconsistent.
Use Case A — Private caching (only client)
Endpoint: GET /api/user/ownProfile
In this case, the response contains user-specific data. The ideal behavior is to cache the response only on the client side, not on intermediate proxies. Server-side caching is optional but not required.
Desired header (example):
Cache-Control: private, max-age=60
Use Case B — Shared caching (client + reverse proxy)
Endpoint: GET /api/user/publicDepartments
This endpoint returns public, infrequently changing data. It should be cached both by clients and reverse proxies.
Desired header (example):
Cache-Control: public, max-age=300
At the moment, there's no unified way to describe and apply this caching policy declaratively in Minimal APIs. Developers are forced to do things like:
app.MapGet("/api/user/publicDepartments", () =>
{
context.Response.Headers["Cache-Control"] = "public,max-age=300";
return Results.Ok(...);
});
There are also custom solutions proposed by other community members. Some of them include custom filters, others implement their own OutputCachePolicy and etc. But they do not provide a robust implementation, which can lead to major security issues in applications.
This topic was partially discussed here and here.
Describe the solution you'd like
Introduce a builder-based extension for Minimal APIs to declaratively configure HTTP response caching behavior, focusing solely on HTTP-compliant cache directives such as Cache-Control, Vary, ETag, and Last-Modified.
Key Goals
- Fluent API design allows for flexibility in configuration, unlike the old implementation with the ResponseCache attribute. This allow combining multiple directives in a fluent and composable way.
- Support HTTP Caching RFC
- No server-side response caching or storage (unlike OutputCache)
Proposal:
app.MapGet("/api/user/publicDepartments", () => ...)
.WithCacheControl(policy => policy
.Public()
.MaxAge(TimeSpan.FromMinutes(5))
.VaryByHeader("Accept-Encoding")
);
app.MapGet("/api/user/ownProfile", () => ...)
.WithCacheControl(policy => policy
.Private()
.MaxAge(TimeSpan.FromSeconds(60))
.NoTransform()
);
app.MapGet("/api/data/download", () => ...)
.WithCacheControl(policy => policy
.Public()
.Immutable()
.MaxAge(TimeSpan.FromDays(30))
);
Rationale for Additional Features (example)
NoTransform
: Prevents proxies from modifying images, videos, or text.
Immutable
: Allows aggressive client-side caching when content is versioned (e.g., fingerprinted URLs).
SharedMaxAge
: Important when intermediaries (e.g., CDNs) should cache differently than browsers.
MustRevalidate
: Helps enforce strict revalidation when freshness expires.
In my opinion, the topic of dividing the cache into an external one based on headers and an internal one based on internal storage requires very careful consideration.
Additional context
No response