From fc5db9bb030e9f0ca3dedab15ef2a5f0d8d9f4f2 Mon Sep 17 00:00:00 2001 From: Katerina Skroumpelou Date: Tue, 11 Aug 2026 16:43:26 +0300 Subject: [PATCH] docs: update client-side tracing and Edge Function CORS guides (#48924) Updates the client-side tracing and Edge Function CORS docs for changes shipping in `@supabase/supabase-js` v2.112.3 (supabase/supabase-js#2603, supabase/supabase-js#2604). The tracing guide gains a vendor compatibility table (plain OpenTelemetry works as is, Sentry needs `propagateTraceparent: true`, Datadog RUM needs `allowedTracingUrls`), the new `respectSamplingDecision` semantics (non-sampled requests now carry `traceparent` only, so logs stay correlatable), a troubleshooting entry for the SDK's new propagator warning, and a note that browser calls to Edge Functions need the trace headers in the function's CORS allow-list. The CORS guide now states explicitly that trace headers are sent only when trace propagation is opted in (never by default), adds a table of when each SDK header is actually sent, and the hardcoded `corsHeaders` examples are updated to the full header list. Should merge after the v2.112.3 release is published, since it documents that version's behavior. ## Summary by CodeRabbit * **Documentation** * Expanded CORS guidance with trace-propagation requirements and SDK version considerations. * Added browser and Edge Function setup guidance for client-side tracing. * Documented updated sampling behavior, advanced configuration, vendor setup examples, and troubleshooting. * **Bug Fixes** * Updated CORS configurations to allow retry and tracing headers required for supported requests. * Improved compatibility for browser requests that transmit distributed tracing context. --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- apps/docs/content/guides/functions/cors.mdx | 24 ++++++++++-- .../client-side-tracing.mdx | 38 ++++++++++++++----- apps/www/supabase/functions/_shared/cors.ts | 3 +- .../supabase/functions/_shared/cors.ts | 3 +- 4 files changed, 53 insertions(+), 15 deletions(-) diff --git a/apps/docs/content/guides/functions/cors.mdx b/apps/docs/content/guides/functions/cors.mdx index 1ca46368fd5..351beb10421 100644 --- a/apps/docs/content/guides/functions/cors.mdx +++ b/apps/docs/content/guides/functions/cors.mdx @@ -55,16 +55,34 @@ export default { } ``` -This approach ensures that when new headers are added to the Supabase SDK, your Edge Functions automatically include them, preventing CORS errors. +Importing from the SDK keeps your allow-list aligned with the headers the client libraries send: when you upgrade the SDK version in your function and redeploy, newly added headers are picked up with it. As of `@supabase/supabase-js` v2.112.3 the list includes the trace context headers (`traceparent`, `tracestate`, `baggage`) used by [client-side tracing](/docs/guides/monitoring-and-debugging/client-side-tracing) — functions deployed with an older version need a redeploy before browsers can call them with trace propagation enabled. + + + +The allow-list only states what a browser _may_ send — it doesn't change what the SDK sends. Trace context headers are sent exclusively by clients that explicitly opt in to trace propagation (`tracePropagation: true`, disabled by default). Clients that haven't opted in send exactly the same headers as before. + + + +The full list, and when each header is sent: + +| Header | Sent | +| -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | +| `authorization` | Every request (session token or API key) | +| `apikey` | Every request | +| `x-client-info` | Every request (SDK name and version) | +| `content-type` | Requests with a body | +| `x-retry-count` | Only on automatic retry attempts (`postgrest-js` retries failed idempotent requests by default) | +| `traceparent`, `tracestate`, `baggage` | **Only when [trace propagation](/docs/guides/monitoring-and-debugging/client-side-tracing) is explicitly enabled** — never by default | ### For versions before 2.95.0 -If you're using `@supabase/supabase-js` before v2.95.0, you'll need to hardcode the CORS headers. Add a `cors.ts` file within a [`_shared` folder](/docs/guides/functions/development-environment#recommended-project-structure): +If you're using `@supabase/supabase-js` before v2.95.0, you'll need to hardcode the CORS headers. Add a `cors.ts` file within a [`_shared` folder](/docs/guides/functions/development-environment#recommended-project-structure). The list must cover every header your calling clients send — include the trace context headers if any client enables `tracePropagation`: ```ts _shared/cors.ts export const corsHeaders = { 'Access-Control-Allow-Origin': '*', - 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type', + 'Access-Control-Allow-Headers': + 'authorization, x-client-info, apikey, content-type, x-retry-count, traceparent, tracestate, baggage', } ``` diff --git a/apps/docs/content/guides/monitoring-and-debugging/client-side-tracing.mdx b/apps/docs/content/guides/monitoring-and-debugging/client-side-tracing.mdx index 44b8a378ac4..23ce8095775 100644 --- a/apps/docs/content/guides/monitoring-and-debugging/client-side-tracing.mdx +++ b/apps/docs/content/guides/monitoring-and-debugging/client-side-tracing.mdx @@ -6,7 +6,7 @@ description: 'Propagate W3C trace context from the Supabase JS, Swift, and Dart The Supabase JS, Swift, and Dart SDKs can attach [W3C Trace Context](https://www.w3.org/TR/trace-context/) headers (`traceparent`, `tracestate`, `baggage`) to outgoing requests. The resulting `trace_id` flows through Supabase services and appears in API Gateway and Edge Function logs, so you can correlate client-side spans with the server-side logs they produced — end-to-end, across the network boundary. -Because the headers follow the W3C standard, any compliant tracing SDK (such as OpenTelemetry, Sentry, Datadog, or Honeycomb) can pick up the trace on the server side, including in self-hosted collectors. +Because the headers follow the W3C standard, any compliant tracing SDK (such as OpenTelemetry, Sentry, Datadog, or Honeycomb) can pick up the trace on the server side, including in self-hosted collectors. On the client side, some vendor SDKs need a small configuration change before they emit the standard headers — see [Using a vendor tracing SDK](#using-a-vendor-tracing-sdk) in the JavaScript tab. { For security, trace headers are only attached to requests targeting Supabase domains (`*.supabase.co`, `*.supabase.in`, and `localhost` for local development). Third-party hosts called through a custom `fetch` are never tagged. + + +Calling Edge Functions from the browser with trace propagation enabled requires the function's CORS allow-list to include the trace headers. In the function, import `corsHeaders` from `npm:@supabase/supabase-js@^2.112.3/cors` or add `traceparent`, `tracestate`, and `baggage` to your own allow-list, then redeploy the function. See [CORS support for Edge Functions](/docs/guides/functions/cors). + + + ## Advanced configuration Pass an object instead of `true` for fine-grained control: @@ -81,22 +87,33 @@ import '@supabase/supabase-js/tracing' const supabase = createClient(SUPABASE_URL, SUPABASE_KEY, { tracePropagation: { enabled: true, - // Default: true. When false, headers are attached even if the - // upstream trace is not sampled — useful when you want every - // Supabase request tagged with a trace_id for log correlation. + // Default: true. Non-sampled requests carry only `traceparent` (with the + // sampled flag preserved, so nothing is recorded downstream) — log + // correlation keeps working while `tracestate` and `baggage` are withheld. + // Set to false to always send the full trace context regardless of sampling. respectSamplingDecision: false, }, }) ``` -| Option | Type | Default | Description | -| ------------------------- | --------- | ------- | ------------------------------------------------------------------- | -| `enabled` | `boolean` | `false` | Enable trace propagation. | -| `respectSamplingDecision` | `boolean` | `true` | If `true`, skip propagation when the upstream trace is not sampled. | +| Option | Type | Default | Description | +| ------------------------- | --------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `enabled` | `boolean` | `false` | Enable trace propagation. | +| `respectSamplingDecision` | `boolean` | `true` | When `true`, non-sampled requests send only `traceparent` (sampled flag preserved) and omit `tracestate` and `baggage`; `false` always sends the full trace context. On versions before `2.112.3`, `true` skipped all trace headers for non-sampled requests. | ## Using a vendor tracing SDK -Many tracing SDKs are built on top of OpenTelemetry. They work with this guide as long as a W3C-compliant propagator is registered. Some vendor SDKs inject only their proprietary headers by default and need extra configuration to also emit the standard `traceparent` header. Check your vendor's OTel integration docs for the exact setup. +Many tracing SDKs are built on top of OpenTelemetry, but they differ in whether their propagator emits the standard `traceparent` header by default: + +| Vendor setup | Works with `tracePropagation`? | Required configuration | +| --------------------------------------------------------------- | -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| OpenTelemetry SDK (also Honeycomb, Grafana, New Relic via OTLP) | Yes | None — the W3C propagator is the default | +| Sentry (Node.js, including Next.js server-side) | Yes, with one flag | Set `propagateTraceparent: true` in `Sentry.init()` — Sentry's propagator omits `traceparent` by default | +| Sentry (browser) | Via Sentry's own instrumentation | Set `propagateTraceparent: true` and add your project URL (`https://.supabase.co`) to `tracePropagationTargets` — Sentry's browser SDK only attaches headers cross-origin for listed targets. For Edge Functions, also add `sentry-trace` to the function's CORS allow-list: Sentry always sends its own header, and it isn't part of `corsHeaders` | +| Datadog `dd-trace` (Node.js) | Out of the box | None — `dd-trace` injects W3C headers at the HTTP layer itself, even without `tracePropagation` | +| Datadog Browser RUM | Yes, with configuration | Add your project URL to `allowedTracingUrls` with the `tracecontext` propagator type | + +If a propagator is active but doesn't emit `traceparent`, the SDK logs a one-time console warning naming the headers the propagator wrote (version `2.112.3` and later). ## Troubleshooting @@ -106,7 +123,8 @@ The SDK never throws when it can't propagate, which keeps it safe to enable but - **No active span at request time.** The SDK reads the _current_ context. If `supabase.from(...)` is called outside `tracer.startActiveSpan(...)` (or equivalent), there's nothing to propagate. Wrap the call in a span or use OpenTelemetry's automatic instrumentation. - **`@opentelemetry/api` is not installed** in the app making the request. On `2.112.0` and later the tracing subpath imports it directly, so a missing package surfaces as a module resolution error. On `2.106.0`–`2.111.x` it's loaded dynamically and the SDK silently no-ops. - **No `TracerProvider` registered.** `@opentelemetry/api` defaults to a noop provider that produces non-recorded spans. Ensure your app calls `provider.register()` (or your vendor SDK's equivalent) before making requests. -- **The upstream trace is not sampled.** By default the SDK respects upstream sampling decisions. Set `respectSamplingDecision: false` to propagate every request regardless of sampling. +- **Your tracing SDK's propagator doesn't emit W3C `traceparent`.** Sentry's propagator, for example, only emits it when `propagateTraceparent: true` is set. From version `2.112.3` the SDK logs a one-time warning naming the headers the propagator wrote — see [Using a vendor tracing SDK](#using-a-vendor-tracing-sdk). +- **The upstream trace is not sampled** (versions before `2.112.3`). Older versions skip all trace headers when the upstream trace is not sampled. From `2.112.3`, non-sampled requests still carry `traceparent`, so log correlation keeps working by default. Set `respectSamplingDecision: false` to always send the full trace context. - **You're calling a non-Supabase host through a custom `fetch`.** Trace headers are only attached to Supabase domains (`*.supabase.co`, `*.supabase.in`, `localhost`). - **You're using the CDN (UMD) build.** Trace propagation isn't available there — the tracing runtime can't be loaded from a script tag. diff --git a/apps/www/supabase/functions/_shared/cors.ts b/apps/www/supabase/functions/_shared/cors.ts index 2ac4d89b14a..6fa1092e9de 100644 --- a/apps/www/supabase/functions/_shared/cors.ts +++ b/apps/www/supabase/functions/_shared/cors.ts @@ -1,4 +1,5 @@ export const corsHeaders = { 'Access-Control-Allow-Origin': '*', - 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type', + 'Access-Control-Allow-Headers': + 'authorization, x-client-info, apikey, content-type, x-retry-count, traceparent, tracestate, baggage', } diff --git a/examples/edge-functions/supabase/functions/_shared/cors.ts b/examples/edge-functions/supabase/functions/_shared/cors.ts index a418716bcf4..645cce8fa2a 100644 --- a/examples/edge-functions/supabase/functions/_shared/cors.ts +++ b/examples/edge-functions/supabase/functions/_shared/cors.ts @@ -8,5 +8,6 @@ export const corsHeaders = { 'Access-Control-Allow-Origin': '*', - 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type', + 'Access-Control-Allow-Headers': + 'authorization, x-client-info, apikey, content-type, x-retry-count, traceparent, tracestate, baggage', }