Skip to content

fix(stack): echo requested headers on CORS preflight#5815

Open
martijnwalraven wants to merge 1 commit into
supabase:developfrom
martijnwalraven:callboard/preflight-header-echo
Open

fix(stack): echo requested headers on CORS preflight#5815
martijnwalraven wants to merge 1 commit into
supabase:developfrom
martijnwalraven:callboard/preflight-header-echo

Conversation

@martijnwalraven

Copy link
Copy Markdown

Problem

The API proxy short-circuits every OPTIONS with 204 and a static access-control-allow-headers list, so the backing services' own CORS handling is never consulted. The static list predates headers today's browser clients send — supabase-js 2.x adds x-supabase-api-version (the same skew class as supabase/auth#1589, fixed service-side in supabase/auth#1612, but the proxy's short-circuit masks that fix), and postgrest-js writes send Prefer. Result: credentialless browser calls through the proxy fail CORS preflight (net::ERR_FAILED; hit in a real web e2e suite).

Fix

Preflights echo the request's Access-Control-Request-Headers. That is also what the classic CLI's proxy already does: its kong.yml ships the bare cors plugin, and Kong's handler reflects Access-Control-Request-Headers when no header list is configured — so this restores parity with the Go CLI's behavior rather than inventing policy. Echo over a wildcard because * is literal (not a wildcard) for credentialed requests. The echo is preflight-only; the static list stays for preflights naming no headers and for non-OPTIONS responses.

Tests

  • preflight echoes the requested headers
  • the echo is preflight-only: non-OPTIONS requests keep the static list

🤖 Generated with Claude Code

The proxy short-circuits every OPTIONS with 204 and a static
allow-headers list, so the backing services' own CORS handling is
never consulted — and the static list predates what browser clients
send today (supabase-js 2.x adds x-supabase-api-version, PostgREST
reads Prefer and the profile headers), so credentialless browser
calls through the proxy failed CORS preflight (net::ERR_FAILED,
observed at a real web e2e). This is the same skew class auth fixed
for itself in supabase/auth#1612 after supabase/auth#1589 — a fix
this proxy's short-circuit masks.

Preflights now echo the requested headers, which is exactly what the
classic CLI's proxy does: its kong.yml ships the bare cors plugin,
and Kong's handler reflects Access-Control-Request-Headers when no
header list is configured. Echo over a wildcard because `*` is
literal (not a wildcard) for credentialed requests. The echo is
preflight-only; the static list stays for preflights naming no
headers and for non-preflight responses. Regression tests cover the
echo and its preflight-only boundary.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@martijnwalraven martijnwalraven requested a review from a team as a code owner July 7, 2026 05:47
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.

1 participant