fix(api-client): Treat * as wildcard in preflight Access-Control-Allow-Headers#12503
Merged
anthonyshew merged 2 commits intovercel:mainfrom Mar 31, 2026
Merged
Conversation
…w-Headers The CORS spec defines * as a wildcard meaning "all headers are allowed". The preflight check in do_preflight only matched the literal string "authorization" via regex, so a server responding with Access-Control-Allow-Headers: * caused turbo to strip the Authorization header from subsequent requests — resulting in 401s. Short-circuit with an exact "*" check before the regex.
Contributor
|
@bitttttten is attempting to deploy a commit to the Vercel Team on Vercel. A member of the Team first needs to authorize it. |
anthonyshew
approved these changes
Mar 31, 2026
Contributor
anthonyshew
left a comment
There was a problem hiding this comment.
Hey, @bitttttten, thanks. Simple enough change that will be helpful for folks.
anthonyshew
reviewed
Mar 31, 2026
Contributor
|
The windows partition failure is a flake. We are okay to merge. |
github-actions Bot
added a commit
that referenced
this pull request
Mar 31, 2026
## Release v2.9.2-canary.2 Versioned docs: https://v2-9-2-canary-2.turborepo.dev ### Changes - release(turborepo): 2.9.2-canary.1 (#12508) (`2bebb91`) - fix: Unblock `watch` loop so interruptible persistent tasks restart on file changes (#12509) (`95ec9ed`) - fix(api-client): Treat * as wildcard in preflight Access-Control-Allow-Headers (#12503) (`960b2eb`) Co-authored-by: Turbobot <turbobot@vercel.com>
Contributor
Author
|
Thanks gang, big up turborepo 🥳 |
github-actions Bot
added a commit
that referenced
this pull request
Mar 31, 2026
## Release v2.9.2-canary.3 Versioned docs: https://v2-9-2-canary-3.turborepo.dev ### Changes - fix(api-client): Treat * as wildcard in preflight Access-Control-Allow-Headers (#12503) (`960b2eb`) - release(turborepo): 2.9.2-canary.2 (#12510) (`d4d3f9d`) - docs: Document `turbo.*` generator variables (#12511) (`aa014c7`) - fix: Backfill missing pnpm workspace importer entries during prune (#12514) (`45f2f43`) Co-authored-by: Turbobot <turbobot@vercel.com>
github-actions Bot
added a commit
that referenced
this pull request
Mar 31, 2026
## Release v2.9.2 Versioned docs: https://v2-9-2.turborepo.dev ### Changes - release(turborepo): 2.9.1 (#12498) (`4240dd4`) - fix: Add retry logic to example update workflow push step (#12499) (`00a4aae`) - docs: Add documentation for cacheMaxAge and cacheMaxSize options (#12500) (`5b65487`) - feat(examples): Add Next.js + Elysia full-stack starter template (#12414) (`1559d0f`) - fix: Resolve correct nested bun lockfile versions during prune (#12506) (`ee5bcbf`) - Revert "fix: Avoid `setsid()` in PTY spawn to prevent macOS Gatekeeper CPU spikes" (#12507) (`6c3b8a6`) - release(turborepo): 2.9.2-canary.1 (#12508) (`2bebb91`) - fix: Unblock `watch` loop so interruptible persistent tasks restart on file changes (#12509) (`95ec9ed`) - fix(api-client): Treat * as wildcard in preflight Access-Control-Allow-Headers (#12503) (`960b2eb`) - release(turborepo): 2.9.2-canary.2 (#12510) (`d4d3f9d`) - docs: Document `turbo.*` generator variables (#12511) (`aa014c7`) - fix: Backfill missing pnpm workspace importer entries during prune (#12514) (`45f2f43`) - release(turborepo): 2.9.2-canary.3 (#12515) (`59a42a5`) - fix: Include transitive dependencies in engine graph pruning for affected paths using Task Graph (#12516) (`8c88521`) - release(turborepo): 2.9.2-canary.4 (#12518) (`aed2066`) - chore: Update AI-generated response text for clarity (#12517) (`4a4e661`) - fix: Preserve shallow install strategy during npm lockfile pruning (#12520) (`72577db`) --------- Co-authored-by: Turbobot <turbobot@vercel.com>
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.
fix(api-client): treat
*as wildcard in preflightAccess-Control-Allow-HeaderscheckBackground
We run a self-hosted Turborepo remote cache backed by an AWS Lambda Function URL with
TURBO_PREFLIGHT=true. Our CORS configuration usesAccess-Control-Allow-Headers: *. I'm thinking this is likely what most self-hosted remote cache deployments use too, which is why I am proposing handling this in turborepo core too. I appreciate that a fix is to be explicit with CORS headers, likeallow_headers = ["Authorization", "Content-Type", "User-Agent", "x-artifact-duration", "x-artifact-tag", "x-artifact-sha", "x-artifact-dirty-hash" ]etc. although some may not have control and I thought that*is quite a common value.The problem
With
TURBO_PREFLIGHT=true, every artifact request returns 401 despite the token being valid. Without preflight, the same token works fine. We traced it todo_preflightinturborepo-api-client. After the OPTIONS preflight, turbo checksAccess-Control-Allow-Headersto decide whether to include theAuthorizationheader:The regex
(?i)(?:^|,) *authorization *(?:,|$)looks for the literal stringauthorizationin a comma-separated list. The CORS wildcard*— which per the Fetch spec means "all headers are allowed" — doesn't match. Soallow_authisfalse, turbo strips the Bearer token, and the server returns 401. So anyone configuring CORS with*would run into this too.The fix
Short-circuit with
allowed_headers == "*"before the regex, matching the CORS spec where*means "allow all headers". A test case is added alongside the existingallow-authandno-allow-authtests.Let me know if you have any questions. Rust isn't my strongest language so this PR was helped along with Opus 🫶