Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions crates/next-core/src/next_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ pub struct NextConfig {
#[bincode(with = "turbo_bincode::serde_self_describing")]
sass_options: Option<serde_json::Value>,
trailing_slash: Option<bool>,
disable_preflight_fetch: Option<bool>,
asset_prefix: Option<RcStr>,
base_path: Option<RcStr>,
skip_proxy_url_normalize: Option<bool>,
Expand Down Expand Up @@ -1709,6 +1710,11 @@ impl NextConfig {
Vc::cell(self.skip_trailing_slash_redirect.unwrap_or(false))
}

#[turbo_tasks::function]
pub fn disable_preflight_fetch(&self) -> Vc<bool> {
Vc::cell(self.disable_preflight_fetch.unwrap_or(false))
}

/// Returns the final asset prefix. If an assetPrefix is set, it's used.
/// Otherwise, the basePath is used.
#[turbo_tasks::function]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
title: disablePreflightFetch
description: Disable client-side preflight fetches during prefetching.
---

{/* The content of this doc is shared between the app and pages router. You can use the `<PagesOnly>Content</PagesOnly>` component to add content that is specific to the Pages Router. Any shared content should not be wrapped in a component. */}

By default, Next.js may issue additional "preflight" requests during client-side prefetching (for example, a background `HEAD` request) to avoid downloading full payloads or to validate a route before fetching more data.

If you need to **disable** these preflight requests, open `next.config.js` and set `disablePreflightFetch`:

```js filename="next.config.js"
module.exports = {
disablePreflightFetch: true,
}
```
2 changes: 2 additions & 0 deletions packages/next/src/build/define-env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,8 @@ export function getDefineEnv({
'process.env.__NEXT_DYNAMIC_ON_HOVER': Boolean(
config.experimental.dynamicOnHover
),
'process.env.__NEXT_DISABLE_PREFLIGHT_FETCH':
config.disablePreflightFetch ?? false,
'process.env.__NEXT_OPTIMISTIC_CLIENT_CACHE':
config.experimental.optimisticClientCache ?? true,
'process.env.__NEXT_MIDDLEWARE_PREFETCH':
Expand Down
36 changes: 21 additions & 15 deletions packages/next/src/client/components/segment-cache/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1350,22 +1350,28 @@ export async function fetchRouteOnCacheMiss(
// NOTE: We could embed the route tree into the HTML document, to avoid
// a second request. We're not doing that currently because it would make
// the HTML document larger and affect normal page loads.
const headResponse = await fetch(url, {
method: 'HEAD',
})
if (headResponse.status < 200 || headResponse.status >= 400) {
// The target page responded w/o a successful status code
// Could be a WAF serving a 403, or a 5xx from a backend
//
// Note that we can't use headResponse.ok here, because
// Response#ok returns `false` with 3xx responses.
rejectRouteCacheEntry(entry, Date.now() + 10 * 1000)
return null
}
if (!process.env.__NEXT_DISABLE_PREFLIGHT_FETCH) {
const headResponse = await fetch(url, {
method: 'HEAD',
})
if (headResponse.status < 200 || headResponse.status >= 400) {
// The target page responded w/o a successful status code
// Could be a WAF serving a 403, or a 5xx from a backend
//
// Note that we can't use headResponse.ok here, because
// Response#ok returns `false` with 3xx responses.
rejectRouteCacheEntry(entry, Date.now() + 10 * 1000)
return null
}

urlAfterRedirects = headResponse.redirected
? new URL(headResponse.url)
: url
urlAfterRedirects = headResponse.redirected
? new URL(headResponse.url)
: url
} else {
// When preflight fetches are disabled, skip the extra request and
// proceed with the original URL.
urlAfterRedirects = url
}

response = await fetchPrefetchResponse(
addSegmentPathToUrlInOutputExportMode(urlAfterRedirects, segmentPath),
Expand Down
1 change: 1 addition & 0 deletions packages/next/src/server/config-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -693,6 +693,7 @@ export const configSchema: zod.ZodType<NextConfig> = z.lazy(() =>
expireTime: z.number().optional(),
target: z.string().optional(),
trailingSlash: z.boolean().optional(),
disablePreflightFetch: z.boolean().optional(),
transpilePackages: z.array(z.string()).optional(),
turbopack: zTurbopackConfig.optional(),
typescript: z
Expand Down
10 changes: 10 additions & 0 deletions packages/next/src/server/config-shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1013,6 +1013,15 @@ export interface NextConfig {
*/
trailingSlash?: boolean

/**
* Disables the client-side "preflight" fetches that Next.js performs during
* prefetching (for example, background `HEAD` requests). When `true`, Next.js
* will skip issuing these preflight requests.
*
* @default false
*/
disablePreflightFetch?: boolean

/**
* Next.js comes with built-in support for environment variables
*
Expand Down Expand Up @@ -1427,6 +1436,7 @@ export const defaultConfig = Object.freeze({
basePath: '',
sassOptions: {},
trailingSlash: false,
disablePreflightFetch: false,
i18n: null,
productionBrowserSourceMaps: false,
excludeDefaultMomentLocales: true,
Expand Down
6 changes: 5 additions & 1 deletion packages/next/src/shared/lib/router/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -609,8 +609,12 @@ function fetchNextData({
if (inflightCache[cacheKey] !== undefined) {
return inflightCache[cacheKey]
}
// Background prefetches use `HEAD` by default to avoid downloading the full
// payload. This can be disabled via next.config.js (`disablePreflightFetch`).
const shouldUseHead =
isBackground && !process.env.__NEXT_DISABLE_PREFLIGHT_FETCH
return (inflightCache[cacheKey] = getData(
isBackground ? { method: 'HEAD' } : {}
shouldUseHead ? { method: 'HEAD' } : {}
))
}

Expand Down
Loading