Skip to content

Add ResponseCache (CacheControl) to Minimal Api #62143

Open
@amyboose

Description

@amyboose

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-middlewareIncludes: URL rewrite, redirect, response cache/compression, session, and other general middlewares

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions