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.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## 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.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This commit is contained in:
Katerina Skroumpelouandgithub-actions[bot] authored and GitHub committed 2026-08-11 16:43:26 +03:00
1 parent 4587d177c3
commit fc5db9bb03
4 files changed
+53 -15

No files matched your search

+21 -3
View File
@@ -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.
<Admonition type="note">
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.
</Admonition>
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',
}
```
@@ -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.
<Tabs
scrollable
@@ -71,6 +71,12 @@ await tracer.startActiveSpan('fetch-users', async (span) => {
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.
<Admonition type="note">
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).
</Admonition>
## 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://<ref>.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.
+2 -1
View File
@@ -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',
}
@@ -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',
}