You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 2ad0528
Browse filesBrowse the repository at this point in the historyBrowse files
authored
feat(nextjs): Never emit trace meta tags from a prerender (#25141)
Under `cacheComponents` the SDK did not enable
`experimental.clientTraceMetadata` (#21141). A shell that Next.js
prerenders during one request is cached and served to later visitors,
and a `sentry-trace` tag inside it would attach all of them to that one
request. ISR pages had the same problem, worked around on the client by
removing the tags on routes with `generateStaticParams` (#18192).
Next.js computes the trace tags of a document inside the `prerender
route (app)` span when it prerenders, and outside of it when it renders
for a request, including when it resumes a shell. The server SDK now
marks that span on the OpenTelemetry context, and its propagator hands
out nothing on a marked context. A prerendered shell never contains a
trace, so any tag that reaches the browser belongs to the current
request. The mark sits on the context rather than the span because an
unsampled request makes the span non-recording, with no attributes to
recognize it by, and a `sampled=0` tag in the shell would drop every
later pageload. Checked in Next.js 15.0 through 16.4.
With that, `clientTraceMetadata` is enabled again under
`cacheComponents`, and the client-side ISR workaround and the
`isrRoutes` manifest field are removed. The guard covers ISR shells too,
including `revalidate` routes the file scan never found.
The guard is applied to the tracer provider and propagator the Node SDK
registers, and `withSentry` of `@sentry/nextjs/cloudflare` applies it to
its own setup, since ISR pages revalidate inside a request of the
Worker. A custom OpenTelemetry setup does not get it automatically. If
it registers `SentryPropagator`, it can register the exported
`NextSentryPropagator` instead and pass its tracer provider to
`markPrerendersOnTracerProvider`. Setups with only the OpenTelemetry W3C
propagators, which includes `@vercel/otel` by default, write no
`sentry-trace` tag, so nothing changes for them. The docs follow in
sentry-docs.
An ISR page with `revalidate = 1` makes the e2e suite exercise the guard
at runtime; the other ISR shells come from the build, where the SDK does
not run.
On its own this PR keeps the current client behaviour: tags in `<head>`
are continued and are now always fresh. The tag of a resumed shell
arrives with the resumed part, which the client only reads in #25142.
<img width="2240" height="1332" alt="figure-shell"
src="https://github.com/user-attachments/assets/809726dd-cddb-4fab-bfee-83bcdc35dbab"
/>
Refs #24944, see #24298
---------
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: s1gr1d <32902192+s1gr1d@users.noreply.github.com>
Copy file name to clipboardExpand all lines: CHANGELOG.md
+6Lines changed: 6 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -31,6 +31,12 @@
31
31
});
32
32
```
33
33
34
+
- **feat(nextjs): Never emit trace meta tags from a prerender ([#25141](https://github.com/getsentry/sentry-javascript/pull/25141))**
35
+
36
+
Pageloads of apps with `cacheComponents` enabled are connected to their server request again. Pages that Next.js prerenders no longer carry trace information, so a cached page can no longer connect visitors to a request that was not theirs. This also applies to ISR pages.
37
+
38
+
If you set up OpenTelemetry yourself and register `SentryPropagator`, register `NextSentryPropagator` instead and pass your tracer provider to `markPrerendersOnTracerProvider`, so that your prerendered pages carry no trace information either.
39
+
34
40
Work in this release was contributed by @adityareddy-dev, @jpdefo, @Jxxunnn, @philipatkinson, @Shubham-Padkonde, @mikamikasuki, and @mketiku. Thank you for your contributions!
35
41
36
42
- **feat(deno): Add incoming request options to `denoServeIntegration`**
Copy file name to clipboardExpand all lines: dev-packages/e2e-tests/test-applications/nextjs-16-cacheComponents/tests/cacheOrigin-pageloadConnection.spec.ts
// TODO: Remove the inverse assertion in cacheComponents.spec.ts ("Prerendered shell does not stitch the pageload onto a stale trace") when this test passes.
5
4
test('connects the pageload trace to the runtime server request',async({ page })=>{
6
-
test.fail();
5
+
// The resumed part of the document, which carries the tag, arrives after the SDK started. Enabled
6
+
// once the pageload instrumentation waits for it.
7
+
test.skip(true,'Needs the deferred pageload start');
7
8
8
9
// The shell is prerendered, but the page's dynamic part (`DynamicContent` awaits `headers()`)
9
10
// runs on the server on every request and produces the `GET /pageload-tracing` transaction.
Copy file name to clipboardExpand all lines: dev-packages/e2e-tests/test-applications/nextjs-16-streaming-cacheComponents/tests/cacheComponents.spec.ts
Copy file name to clipboardExpand all lines: dev-packages/e2e-tests/test-applications/nextjs-16-streaming-cacheComponents/tests/cacheOrigin-pageloadConnection.spec.ts
// TODO: Remove the inverse assertion in cacheComponents.spec.ts ("Prerendered shell does not stitch the pageload onto a stale trace") when this test passes.
5
4
test('connects the pageload trace to the runtime server request',async({ page })=>{
6
-
test.fail();
5
+
// The resumed part of the document, which carries the tag, arrives after the SDK started. Enabled
6
+
// once the pageload instrumentation waits for it.
7
+
test.skip(true,'Needs the deferred pageload start');
7
8
8
9
// The shell is prerendered, but the page's dynamic part (`DynamicContent` awaits `headers()`)
9
10
// runs on the server on every request and produces the `GET /pageload-tracing` root span.
0 commit comments