Skip to content

Conversation

@shivank-1011
Copy link

Include the response Access-Control-Allow-Origin header as an extra argument to the app's compiled etag function so ETags vary by CORS origin. This prevents CDN/304 + missing CORS headers from causing browser CORS errors. Backwards compatible: custom etag functions may ignore the extra arg.

Fix: ETag should vary by CORS origin

Summary

Fixes a bug where responses with the same body but different Access-Control-Allow-Origin headers produced identical ETags.

Details

res.send() now passes the response’s Access-Control-Allow-Origin value to the ETag generator so that ETags differ per origin.
This prevents caches or CDNs from serving incorrect 304 Not Modified responses that omit or mismatch CORS headers.

Changes

  • response.js – Forward Access-Control-Allow-Origin to the ETag generator.
  • utils.js – No functional change; already supports optional origin argument.
  • etag.cors.js – Added tests to verify ETag varies by CORS origin.

Notes

  • Backward compatible – custom ETag functions ignoring extra args still work.
  • Tests and CI pass successfully.

Include the response Access-Control-Allow-Origin header as an extra
argument to the app's compiled etag function so ETags vary by CORS origin.
This prevents CDN/304 + missing CORS headers from causing browser CORS errors.
Backwards compatible: custom etag functions may ignore the extra arg.
@krzysdz
Copy link
Contributor

krzysdz commented Nov 7, 2025

I don't think that ACAO should affect ETag value. ETag is about selected representation, which I understand to be the response data and metadata such as language, encoding and type.

If you serve different ACAO headers, then I assume it is based on the request Origin header (your tests do that). Other CORS headers such as Access-Contol-Allow-Methods and Access-Control-Allow-Headers, unless hardcoded, are likely to be based on request's Access-Control-Request-Method and Access-Control-Request-Headers headers.
In HTTP semantics (RFC 9110) there is a convinient tool for dealing with exactly such cases - the Vary header. As it is described:

The "Vary" header field in a response describes what parts of a request message, aside from the method and target URI, might have influenced the origin server's process for selecting the content of this response.

When this header is present, the cache MUST use request headers listed in response's Vary as additional keys. What this means is that if you serve responses with Vary: Origin, then responses to requests with Origin: example.com and Origin: example.org are cached separately.

If you'd like to read more, here are some references:

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.

2 participants