Skip to content

fix: set Vary: Origin header when origin callback rejects#403

Open
mahmoodhamdi wants to merge 1 commit intoexpressjs:masterfrom
mahmoodhamdi:fix/set-vary-origin-on-rejected-cors
Open

fix: set Vary: Origin header when origin callback rejects#403
mahmoodhamdi wants to merge 1 commit intoexpressjs:masterfrom
mahmoodhamdi:fix/set-vary-origin-on-rejected-cors

Conversation

@mahmoodhamdi
Copy link
Copy Markdown

What

Sets the Vary: Origin response header when a dynamic origin callback returns false (rejecting the request origin).

Why

When using a dynamic origin function, the server's response depends on the Origin request header. Per the Fetch specification, the Vary: Origin header must be set whenever the response varies based on Origin — even when the origin is not allowed.

In particular, consider what happens if Vary is not used and a server is configured to send Access-Control-Allow-Origin for a certain resource only in response to a CORS request. When a user agent receives a response to a non-CORS request for that resource (for example, as the result of a navigation request), the response will lack Access-Control-Allow-Origin and the user agent will cache that response. Then, if the user agent subsequently encounters a CORS request for the resource, it will use that cached response from the previous non-CORS request, without Access-Control-Allow-Origin.

Before this fix: When the origin callback returned false, no headers were set. An HTTP cache could store this response and serve it to a later request from an allowed origin — that response would incorrectly lack Access-Control-Allow-Origin, breaking the CORS request.

After this fix: Vary: Origin is set even when the origin is rejected, so caches correctly treat responses as origin-dependent.

The fix is a single line in middlewareWrapper — calling vary(res, 'Origin') before next() in the rejection branch.

Testing

Updated two existing tests to verify Vary: Origin is present when origin callback rejects:

  • "should not allow origin when callback returns false" — now asserts Vary: Origin
  • "should not override options.origin callback" — rejection case now asserts Vary: Origin
49 passing (35ms)

----------|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files |     100 |      100 |     100 |     100 |
 index.js |     100 |      100 |     100 |     100 |
----------|---------|----------|---------|---------|-------------------

Fixes #330

When the origin callback returns false (rejecting the request origin),
the middleware now sets the Vary: Origin response header before passing
control to next(). This prevents HTTP caches from incorrectly reusing
a response generated for one origin when handling requests from a
different origin.

Per the Fetch specification (https://fetch.spec.whatwg.org/#cors-protocol-and-http-caches):
if a server's response depends on the Origin request header, Vary: Origin
must be set — even when the origin is not allowed — to ensure correct
cache behavior.

Previously, when a dynamic origin function rejected a request, no headers
were set at all. A cache could then store this response and serve it to
a subsequent CORS request from an allowed origin, which would incorrectly
lack the Access-Control-Allow-Origin header.

Fixes expressjs#330
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.

Vary: Origin should be set on non-CORS request

1 participant