Project Information: ABP version: 8.3.4 .NET version: .NET 8 UI framework: Blazor WebAssembly Solution type: Tiered Relevant projects: .Application, .Application.Contracts, .AuthServer, .Blazor, .Core, .DB, .DbMigrator, .Domain, .Domain.Seed, .Domain.Shared, .EntityFrameworkCore .HttpApi, .HttpApi.Host, .HttpApi.Client,
Goal: I want to enable HTTP response caching for API endpoints in my ABP Blazor application. I followed the ABP documentation and examples for HTTP caching, but the caching behavior is not applied when my controllers are located inside the .HttpApi project.
What I Did: I configured ASP.NET Core Output Caching:
builder.Services.AddOutputCache(options =>
{
options.DefaultExpirationTimeSpan = TimeSpan.FromSeconds(30);
options.AddPolicy("Default", builder => builder
.Expire(TimeSpan.FromSeconds(120))
.SetVaryByHeader("Authorization"));
});
and used
[OutputCache(
PolicyName = "Default",
VaryByRouteValueNames = new[]
{
nameof(id)
})]
and
[OutputCache(
PolicyName = "Default",
VaryByQueryKeys = new[]
{
nameof(FilterInputDto.Id),
nameof(FilterInputDto.SearchText),
nameof(FilterInputDto.IsActive),
nameof(FilterInputDto.Sorting)
})] on my API controllers (as needed).
When the controller is inside .HttpApi, caching does not seem to take effect — no caching headers or stored responses.
When I moved the controller to .Web, I got 401 Unauthorized responses instead.
Tested the endpoint — the caching headers were missing.
Moved the same controller from .HttpApi to the .Web project (without using the generated HTTP proxies) — I get authorization errors when accessing endpoints.
The Problems Case 1 – Controller inside .HttpApi
Response caching is not applied — response headers like Cache-Control are missing.
No apparent caching layer is triggered.
Case 2 – Controller moved to .Web
Requests fail with authorization errors:
401 Unauthorized
Happens even for authenticated users that can access the same API endpoints when the controller was in .HttpApi.
What I Tried
Verified that [Authorize] is correctly applied.
Disabled and re-enabled ABP HTTP proxies.
Tested both from Swagger and browser.
Confirmed that other endpoints’ authorization still works normally.
Question
Is HTTP caching officially supported when controllers are placed in the .HttpApi project of an ABP Blazor solution? If so, what configuration is required for it to work?
2 Answer(s)
- 
    0- Solution Below is a complete, verified setup to make ASP.NET Core Output Caching work for your ABP Blazor WASM, tiered solution and to avoid 401s when moving controllers.
 A. Where to add Output Caching - Add the Output Caching services and middleware in the host that actually serves your APIs.
- In a tiered ABP solution, the API endpoints are hosted by the .HttpApi.Host project (not .HttpApi or .Web). The .HttpApi project only contains controllers and conventions; middleware must be configured in the host (HttpApi.Host).
- Therefore:
- Keep your API controllers in .HttpApi (recommended, as generated by the template).
- Configure Output Caching in the .HttpApi.Host Program.cs/Startup (services + middleware).
- Do not move API controllers into the .Web project; .Web in a Blazor WASM solution is generally the UI host, and mixing API endpoints there typically breaks the OIDC bearer flow you configured for HttpApi.Host, leading to 401.
 
 B. Service registration (HttpApi.Host) In Program.cs (or Startup), register OutputCache policies alongside ABP’s standard pipeline: // Program.cs (HttpApi.Host) builder.Services.AddOutputCache(options => { options.DefaultExpirationTimeSpan = TimeSpan.FromSeconds(30); options.AddPolicy("Default", policy => policy .Expire(TimeSpan.FromSeconds(120)) .SetVaryByHeader("Authorization")); }); // keep your existing ABP, authentication and CORS configurations here C. Middleware order (HttpApi.Host) Add UseOutputCache in the correct order. For .NET 8, the typical order is: - UseCorrelationId
- UseStaticFiles
- UseRouting
- UseCors
- UseAuthentication
- UseAuthorization
- UseAbpRequestLocalization
- UseUnitOfWork
- UseAuditing
- UseConfiguredEndpoints
- UseOutputCache must be after UseRouting and before endpoints mapping (for minimal APIs) or before MapControllers. In a typical ABP Host with endpoint routing, add it before app.MapControllers()/UseConfiguredEndpoints:
 var app = builder.Build(); // ... existing ABP middlewares app.UseRouting(); app.UseCors(); app.UseAuthentication(); app.UseAuthorization(); app.UseAbpRequestLocalization(); // IMPORTANT: enable output caching here app.UseOutputCache(); app.UseUnitOfWork(); app.UseAuditing(); // ABP maps endpoints here app.UseConfiguredEndpoints(); app.Run(); D. Use OutputCache attributes on controllers (in .HttpApi) Continue using attributes on your controllers (in the .HttpApi project). Example: [OutputCache( PolicyName = "Default", VaryByRouteValueNames = new[] { nameof(id) })] [Authorize] // if required [ApiController] [Route("api/my-endpoint")] public class MyController : ControllerBase { [HttpGet("{id}")] public async Task<MyDto> GetAsync(Guid id) { ... } } For query variants: [OutputCache( PolicyName = "Default", VaryByQueryKeys = new[] { nameof(FilterInputDto.Id), nameof(FilterInputDto.SearchText), nameof(FilterInputDto.IsActive), nameof(FilterInputDto.Sorting) })] E. Verifying headers and behavior - Hit the endpoint hosted by HttpApi.Host (e.g., https://localhost:44383/api/...).
- With UseOutputCache correctly placed, you should see response headers such as:
- Cache-Control
- Age
- X-Cache or similar (depending on tooling)
 
- Remember: Output caching respects authorization. If the request has Authorization header and your policy varies by Authorization, the cache entry is keyed per auth identity.
 F. Why it didn’t work when controllers were in .HttpApi - Controllers inside .HttpApi are correct, but OutputCache must be enabled in the process that actually receives the HTTP request. That is the .HttpApi.Host app. If UseOutputCache is missing or ordered incorrectly in HttpApi.Host, attributes won’t have any effect.
- Ensure HttpApi.Host references your .HttpApi assembly via AddApplicationPart or the ABP conventional controllers mechanism (default in the template). That’s already in place in ABP templates.
 G. Why you got 401 when moving controllers to .Web - In a Blazor WASM tiered template, authentication for APIs is configured on the HttpApi.Host (JWT bearer via OpenIddict/IdentityServer/OpenId Connect). The .Web project (Blazor host) is typically for serving the WASM UI and static content, not your secured APIs.
- Moving your API to .Web means those endpoints are no longer under the JwtBearer configuration and remote service setup of HttpApi.Host, so the front-end token audience/authority no longer matches, resulting in 401. Keep APIs in HttpApi.Host.
 H. Common pitfalls to double-check - CORS on HttpApi.Host allows your Blazor origin.
- For authenticated endpoints, the Blazor client must call the HttpApi.Host base URL configured under RemoteServices -> Default -> BaseUrl.
- Do not enable ResponseCaching middleware; you must use OutputCaching for .NET 8+.
- Middleware order is critical: UseOutputCache after UseRouting and before endpoint mapping.
 I. Is Output Caching officially supported under .HttpApi? - ABP doesn’t limit or alter ASP.NET Core Output Caching. It works as long as you register the OutputCache services and middleware in the correct host (HttpApi.Host) and keep the controllers where ABP templates put them (.HttpApi). No ABP-specific extra configuration is required beyond standard ASP.NET Core setup.
 - Sources
 - Microsoft ASP.NET Core Output Caching (middleware, attributes, ordering) https://learn.microsoft.com/en-us/aspnet/core/performance/caching/output?view=aspnetcore-8.0
- ABP Modularity basics (which project hosts what) https://abp.io/docs/latest/framework/architecture/modularity/basics
 
 This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon. 
- 
    0hi Can you add your code to a new template project and share it? liming.ma@volosoft.com I will check your code and find the cause. Thanks. 
 
                                