Repository navigation
fix(web): preserve CORS origin schemes and Vary headers - #7901
Merged
Merged
Conversation
Signed-off-by: cloudbase-ai <22498243+cloudbase-ai@users.noreply.github.com>
Aias00
approved these changes
Oct 21, 2026
Aias00
left a comment
Contributor
There was a problem hiding this comment.
Approving. The scheme bug is fixed at the right test — oneOrigin.contains("://") asks "does this entry already carry a scheme", where the old startsWith(String.format("%s://", scheme)) asked "does it carry the gateway's scheme", which is exactly why https://frontend.example got prefixed with http:// and never matched. .map(String::trim) before the check is also correct, since otherwise " https://x ".contains("://") would still pass but the reflected Access-Control-Allow-Origin would have whitespace and fail the browser's byte comparison.
The Vary: Origin piece is designed correctly:
- Registering
beforeCommitbefore delegating to the chain is what makes it run after upstream headers have been merged, so an upstreamVaryis already present when you look at it. - Adding it for absent and denied origins (not just allowed ones) is the part that actually makes a shared cache safe —
testResponseWithoutOriginAlsoVariesByOriginandtestAllowListDoesNotReflectDisallowedOriginspin it. HttpHeaders.getVary()splits comma-separated values, so"Accept-Encoding, Origin"is seen as two tokens and does not get a duplicateOrigin.testVaryPreservesExistingTokensWithoutDuplicatingOrigincovers the lowercase and*cases too, and skipping augmentation whenVary: *is present is RFC-correct.
One behaviour change that goes beyond the issue and should be called out in the description:
!"null".equals(origin)now excludes the literalOrigin: nullfrom the CORS branch entirely. Refusing to reflectnullis the safer default, and I agree with it, but a deployment that previously relied onallowedAnyOriginechoingOrigin: null(sandboxed iframes, some webviews,file://origins) silently stops gettingAccess-Control-Allow-Originafter this. That is a compat note, not a defect — just do not let it ship as an undocumented side effect.
Non-blocking:
oneOrigin.contains("://")accepts anything with://in it, includingfoo://baror a typo likehttps:/frontend.example. A stricterstartsWith("http://") || startsWith("https://")(or parsing withURI) would reject junk at config time instead of letting it sit in the allow list looking valid.Vary: Originis now emitted on every response, including preflight and non-CORS traffic. Correct for cache safety, but it multiplies cache variants per origin for CDNs in front of the gateway. Expected trade-off — worth a line in the docs so operators are not surprised by a hit-rate drop.filterAndCompletewas added to drivesetCompletebecause thebeforeCommitcallback only runs on commit. That is a real behavioural coupling the oldassertOriginhelper hid; good that the helper now commits explicitly, since tests that never commit would otherwise silently stop exercising theVarypath.
This was referenced Oct 23, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #7370.
An allowed
https://frontend.exampleorigin is currently prefixed withhttp://when the gateway receives an HTTP request, so it fails the allow-list check. Trim configured origins and preserve their explicit scheme; scheme-less entries continue to inherit the gateway request scheme.Add
Vary: Originbefore response commitment, after upstream response headers have been applied. Preserve existing Vary tokens, recognize an existing Origin token case-insensitively, and retainVary: *. Responses for absent or denied origins also vary by Origin so a cached response without CORS permission cannot be reused for an allowed request.Wildcard and
allowedAnyOriginmodes continue reflecting concrete origins, including with credentials. As requested by the issue, the literal opaqueOrigin: nulldoes not receive CORS permission through those modes. Denied preflights continue down the filter chain, preserving #6720's behavior.Validation
The regression checks fail against the original implementation. After the fix, all 24
CrossFilterTestcases pass, including cross-scheme/trimmed/scheme-less origins, scheme/host/port rejection, preflight headers, credentials, wildcard modes, opaque origins, missing Origin, and Vary headers applied downstream. The selected reactor also passes Checkstyle and Apache RAT:These tests commit WebFlux mock responses; browser/proxy end-to-end tests and the full repository test suite were not run.
Make sure that:
./mvnw clean install -Dmaven.javadoc.skip=true. Focused verification is listed above.