Files
Luke SandbergandGitHub dde2980379 Scope Safari ?ts= cache-buster to CSS/font assets only (Pages Router) (#92580)
### What?

Refactors the Safari `?ts=` cache-busting workaround in the Pages Router so it **only appears on CSS and font URLs**, not on script tags or script preload links.

### Why?

The `?ts=` timestamp was originally added to all preloaded assets as a workaround for a [Safari caching bug](https://bugs.webkit.org/show_bug.cgi?id=187726) (see [#5860](https://github.com/vercel/next.js/issues/5860)). When it appears on `<script>` tags, the Turbopack runtime's `getAssetSuffixFromScriptSrc()` reads the executing script's query string and infers it as the `ASSET_SUFFIX`, which then leaks onto all static asset URLs — including images. This causes `next/image` validation errors because the image URL gets an unexpected `?ts=` parameter.

Fixes #92118

### How?

Instead of maintaining parallel `assetQueryString` (with `?ts=`) and `scriptAssetQueryString` (without `?ts=`) paths, this PR:

1. **`assetQueryString`** carries only the deployment token (`?dpl=...`) and is used for all script-related URLs (script tags, script preloads)
2. **`safariCacheBuster`** is a new, separate field (`?ts=<timestamp>` or `""`) that is only combined with `assetQueryString` at the 3 CSS/font URL sites via a `joinQueryStrings()` helper
3. Removes the `scriptAssetQueryString` / `scriptMutableAssetQueryString` / `cssAssetQueryString` fields entirely

The Safari cache-buster is computed the same way as before (dev server only, Safari user-agent check) — it just no longer contaminates the base query string.

### Test plan

- [x] Updated existing test in `test/e2e/app-document/rendering.test.ts` to assert `?ts=` appears only on `<link rel="preload" as="style">` and `<link rel="preload" as="font">`, and does NOT appear on `<script>` or `<link rel="preload" as="script">`
- [x] `pnpm --filter=next types` passes

<!-- NEXT_JS_LLM_PR -->
2026-04-14 16:45:23 -07:00
..