Skip to content

feat: tiered cache headers#958

Open
araujogui wants to merge 3 commits into
nodejs:mainfrom
araujogui:feat/tiered-cache-headers
Open

feat: tiered cache headers#958
araujogui wants to merge 3 commits into
nodejs:mainfrom
araujogui:feat/tiered-cache-headers

Conversation

@araujogui

Copy link
Copy Markdown
Member

Fixes #160

Copilot AI review requested due to automatic review settings June 21, 2026 22:21
@araujogui araujogui requested a review from a team as a code owner June 21, 2026 22:21
@cursor

cursor Bot commented Jun 21, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Caching behavior changes for all CDN and browser clients; incorrect path classification could serve stale latest content or under-cache release binaries.

Overview
Replaces the single success cache policy with immutable (1-year, immutable) and mutable (1-day, must-revalidate) policies, chosen from the client’s original request path (including before latest alias substitution via getOriginalUrl).

Immutable applies to versioned release assets; mutable applies to directory listings, index.json/llms.txt-style metadata, /api/*, and any path segment from latestVersions (e.g. latest, latest-v20.x). Only 200 responses get long-lived caching; 206/304/412 and errors keep the existing failure policy. R2 and origin middleware set Cache-Control centrally (cacheControlFor); R2 no longer embeds cache headers in object metadata. E2E and unit tests cover the new behavior and R2 error responses 400/416.

Reviewed by Cursor Bugbot for commit 82a6785. Bugbot is set up for automated code reviews on this repo. Configure here.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Implements tiered Cache-Control policies to better reflect which served resources are immutable (versioned release assets) vs mutable (indexes, directory listings, moving latest* aliases), addressing issue #160.

Changes:

  • Introduces centralized cache decision logic (isImmutablePath, cacheControlFor) and corresponding unit tests.
  • Applies cache policy based on the original (unsubstituted) request URL in both R2 and origin fetch paths.
  • Replaces the single success cache header with explicit immutable / mutable policies and updates e2e expectations.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/utils/request.ts Adds getOriginalUrl() helper to consistently use the unsubstituted URL when present.
src/utils/cache.ts Adds path/status-based cache policy selection for immutable vs mutable resources.
src/utils/cache.test.ts Unit tests for cache policy/path classification logic.
src/providers/r2Provider.ts Removes provider-owned cache-control policy and defers to middleware.
src/middleware/r2Middleware.ts Sets cache-control per response using original URL + status; updates directory listing caching.
src/middleware/originMiddleware.ts Applies tiered cache-control to origin responses using original URL pathname.
src/constants/cache.ts Replaces success with immutable / mutable cache header strings.
e2e-tests/file.test.ts Updates assertions for new mutable cache policy on index.json.
e2e-tests/fallback.test.ts Updates assertions for new mutable cache policy on fallback-served mutable resources.

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

Comment on lines 43 to 47
if (!hasTrailingSlash(request.urlObj.pathname)) {
// We always want directory listing requests to have a trailing slash
const url = request.unsubstitutedUrl ?? request.urlObj;
const url = getOriginalUrl(request);
return Response.redirect(`${url}/`, 301);
}
Comment on lines +163 to +165
// Set by R2Middleware, which has access to the original request URL needed
// to decide between immutable/mutable/failure cache policies.
'cache-control': '',
Comment thread src/utils/cache.ts
Comment on lines +63 to +66
* Only fully-successful (200) responses are given a long-lived cache policy.
* Conditional/range responses (206/304/412) and errors keep the `failure`
* policy so they're always re-validated.
*/

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 82a6785. Configure here.


let responseBody;
if (request.method === 'GET') {
responseBody = renderDirectoryListing(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Directory index mutable cache

Low Severity

When a directory request is satisfied by serving index.html instead of an HTML listing, setCacheControl still uses the trailing-slash pathname. isImmutablePath then treats the response as a mutable directory URL, so versioned static index files can miss the long-lived immutable cache policy they would get at the same path with index.html in the URL.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 82a6785. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Better caching directives

2 participants