Skip to content

Fix browser caching behaviour#3772

Merged
martincostello merged 2 commits intomasterfrom
gh-3709
Feb 5, 2026
Merged

Fix browser caching behaviour#3772
martincostello merged 2 commits intomasterfrom
gh-3709

Conversation

@martincostello
Copy link
Collaborator

Pull Request

The issue or feature being addressed

#3709 and #3768.

Details on the issue fix or feature implementation

- Fix ETags not being used due to non-zero `max-age`.
- Fix incorrect ETag types for hashed files.
- Consistently respond with HTTP 304 for unmodified static files.
- Fix document URL not responding with correct response if custom JSON serializer options are used.

Resolves #3709 and #3768.
@martincostello martincostello added this to the v10.1.2 milestone Feb 5, 2026
@codecov
Copy link

codecov bot commented Feb 5, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.93%. Comparing base (83baaf0) to head (855d7fc).
⚠️ Report is 1 commits behind head on master.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #3772      +/-   ##
==========================================
+ Coverage   94.90%   94.93%   +0.02%     
==========================================
  Files         111      111              
  Lines        3870     3886      +16     
  Branches      780      783       +3     
==========================================
+ Hits         3673     3689      +16     
  Misses        197      197              
Flag Coverage Δ
Linux 94.93% <100.00%> (+0.02%) ⬆️
Windows 94.93% <100.00%> (+0.02%) ⬆️
macOS 94.93% <100.00%> (+0.02%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@martincostello martincostello marked this pull request as ready for review February 5, 2026 11:47
Copilot AI review requested due to automatic review settings February 5, 2026 11:47
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request fixes browser caching behavior for Swagger UI and ReDoc by properly implementing HTTP caching with ETags. The changes address issues where ETags were not being used due to non-zero max-age, incorrect weak ETag types for hashed files, and missing 304 Not Modified responses.

Changes:

  • Changed default cache lifetime from 7 days to 0 days to enable ETag-based caching
  • Fixed ETag generation to use strong ETags instead of weak ETags for content-hashed files
  • Implemented HTTP 304 Not Modified responses when If-None-Match header matches the ETag
  • Fixed document URL endpoint to properly serialize Urls property when custom JSON serializer options are used

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/Swashbuckle.AspNetCore.SwaggerUI/SwaggerUIOptions.cs Changed default CacheLifetime from 7 days to TimeSpan.Zero to enable ETag-based validation
src/Swashbuckle.AspNetCore.SwaggerUI/SwaggerUIMiddleware.cs Implemented 304 response handling, fixed ETag generation to use strong ETags, refactored to check If-None-Match header, fixed document URL serialization bug
src/Swashbuckle.AspNetCore.ReDoc/ReDocOptions.cs Changed default CacheLifetime from 7 days to TimeSpan.Zero to enable ETag-based validation
src/Swashbuckle.AspNetCore.ReDoc/ReDocMiddleware.cs Implemented 304 response handling, fixed ETag generation to use strong ETags, refactored to check If-None-Match header
test/Swashbuckle.AspNetCore.IntegrationTests/SwaggerUIIntegrationTests.cs Updated test assertions for strong ETags and zero max-age, added tests for 304 Not Modified responses
test/Swashbuckle.AspNetCore.IntegrationTests/ReDocIntegrationTests.cs Updated test assertions for strong ETags and zero max-age, added tests for 304 Not Modified responses
Comments suppressed due to low confidence (1)

src/Swashbuckle.AspNetCore.ReDoc/ReDocMiddleware.cs:136

  • The status code and ContentType are being set before checking If-None-Match header. When the response is 304 Not Modified, these headers should not be set. The ContentType and status code should only be set after verifying that the content has changed (i.e., when ifNoneMatch != etag). This is inconsistent with how SwaggerUIMiddleware handles the same scenario (lines 165-166 in SwaggerUIMiddleware.cs), where ContentType and StatusCode are only set in the else block after confirming the content has changed.
        response.StatusCode = StatusCodes.Status200OK;

        Stream stream;

        switch (fileName)
        {
            case "index.css":
                response.ContentType = "text/css";
                stream = ResourceHelper.GetEmbeddedResource(fileName);
                break;

            case "index.js":
                response.ContentType = "application/javascript;charset=utf-8";
                stream = ResourceHelper.GetEmbeddedResource(fileName);
                break;

            default:
                response.ContentType = "text/html;charset=utf-8";
                stream = _options.IndexStream();
                break;
        }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Pass the current HttpContext's CancellationToken to the write for the document URLs.
@martincostello martincostello enabled auto-merge (squash) February 5, 2026 11:54
@martincostello martincostello merged commit 3d260bb into master Feb 5, 2026
19 of 20 checks passed
@martincostello martincostello deleted the gh-3709 branch February 5, 2026 12:14
}

json ??= JsonSerializer.Serialize(_options.ConfigObject, _jsonSerializerOptions);
json ??= JsonSerializer.Serialize(_options.ConfigObject.Urls, _jsonSerializerOptions);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't this still suffer the issue? The json variable has a non-null default value, so if you pass json options it won't null coalesce

Or am I missing something?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

martincostello added a commit that referenced this pull request Feb 5, 2026
Fix URLs not being serialized correctly if a custom `JsonSerializerOptions` is provided.

See #3772 (comment).
martincostello added a commit that referenced this pull request Feb 5, 2026
Fix URLs not being serialized correctly if a custom `JsonSerializerOptions` is provided.

See #3772 (comment).
This was referenced Feb 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants