---
title: Skew Protection
product: vercel
url: /docs/skew-protection
canonical_url: "https://vercel.com/docs/skew-protection"
last_updated: 2026-09-16
type: conceptual
prerequisites:
  []
related:
  - /docs/environment-variables/system-environment-variables
  - /docs/deployments/managing-deployments
  - /docs/microfrontends
  - /docs/deployment-retention
  - /docs/query
summary: "Learn how Vercel's Skew Protection ensures that the client and server stay in sync for any particular deployment."
install_vercel_plugin: npx plugins add vercel/vercel-plugin
---

# Skew Protection

> Skew Protection is available on Enterprise and Pro plans
>
> Those with the owner, admin, member role can access this feature

[Version skew](https://www.industrialempathy.com/posts/version-skew/) occurs when different versions of your application run on client and server, causing application errors and other unexpected behavior. For example, imagine your newest deployment modifies the data structure by adding a required field to a user's profile. Older clients wouldn't expect this new field, leading to errors when they submit it.


<!-- docsgraph:related -->
## Related pages

> **For AI agents:** Follow these links to understand how this page connects to the rest of the Vercel ecosystem. For the full cross-link map (inbound, outbound, prerequisites, and semantic neighbors), see the .graph.md link below.

- [Automatic mitigation of Google and Bing crawl delay, via Vercel’s Skew Protection](https://vercel.com/changelog/automatic-mitigation-of-crawler-delay-via-skew-protection)
- [Optimized CDN caching and deploying of immutable static assets](https://vercel.com/changelog/optimized-cdn-caching-and-deploying-of-immutable-static-assets)
- [Skew Protection max age now supports the full deployment lifetime](https://vercel.com/changelog/skew-protection-max-age-now-supports-the-full-deployment-lifetime)
- [Skew Protection now supports prebuilt deployments](https://vercel.com/changelog/skew-protection-now-supports-prebuilt-deployments)
- [How to add per-request CSP nonces to CDN-cached HTML on Vercel](https://vercel.com/kb/guide/csp-nonces-with-cdn-cache) — Use Routing Middleware and a self-fetch to add a fresh CSP nonce to cached HTML without rendering the page again on ever
- [How to handle order-ahead traffic spikes on Vercel](https://vercel.com/kb/guide/handle-order-ahead-traffic-spikes-on-vercel) — Order-ahead traffic spikes hit the same routes every lunch hour. Learn how to scale, cache, queue, and test a restaurant
- [Deploy to Vercel with Self-Hosted Git Pipelines \\(GitLab & Bitbucket\\)](https://vercel.com/kb/guide/how-can-i-use-gitlab-pipelines-with-vercel) — Learn how to use GitLab Pipelines to deploy to Vercel including support for self-managed GitLab.
- [How to reduce build costs on Vercel](https://vercel.com/kb/guide/how-reduce-build-costs) — Find which projects drive your Build CPU Minutes, then lower them by skipping unneeded builds, shortening each build, an
- [How to deploy a Saleor storefront on Vercel](https://vercel.com/kb/guide/how-to-deploy-a-saleor-storefront-on-vercel) — Deploy Saleor's Paper storefront template on Vercel, including environment variables, GraphQL caching, checkout, and pro
- [Life of a Vercel request: Application-aware routing](https://vercel.com/blog/life-of-a-request-application-aware-routing)
- [Next.js 16.3 support on Vercel](https://vercel.com/blog/vercel-supports-next-js-16-3)
- [Skew Protection is now generally available](https://vercel.com/changelog/skew-protection-is-now-generally-available)

Full cross-link map for this page: [/docs/skew-protection.graph.md](/docs/skew-protection.graph.md)
<!-- /docsgraph:related -->

Vercel's Skew Protection resolves this problem at the platform and framework layer by using [version locking](https://www.industrialempathy.com/posts/version-skew/#version-locking), which ensures client and server use the exact same version. In our example, outdated clients continue to communicate with servers that understand the old data structure, while updated clients use the most recent deployment.

![Image](https://7nyt0uhk7sse4zvn.public.blob.vercel-storage.com/front/docs/deployments-basics/nested-layouts-light.png)

By implementing Skew Protection, you can reduce user-facing errors during new rolling releases and boost developer productivity, minimizing concerns about API compatibility across versions.

## How it works

When Skew Protection is enabled with a [supported framework](#supported-frameworks), the framework automatically includes the deployment ID in **framework-managed requests** from the client. These include:

- **Static assets**: JavaScript bundles, CSS files, and images loaded by the framework
- **Client-side navigations**: Route transition data fetches and Server Actions
- **Prefetches**: Route and data prefetch requests triggered by the framework

Vercel routes these requests to the deployment that served the initial page. How the deployment ID travels depends on the framework:

| Framework | How the deployment ID travels | What Vercel pins |
| --- | --- | --- |
| Next.js, Astro | The framework adds a `?dpl=` query parameter or an `x-deployment-id` header to the requests that it makes from the browser | Framework-managed requests |
| SvelteKit, Nuxt, Qwik, React Router | Your browser sends a `__vdpl` cookie with every request to the same domain | Framework-managed requests and your custom `fetch()` calls |

With Next.js and Astro, every other request carries no deployment ID, so the latest deployment serves it. See [Pinning requests without a deployment ID](#pinning-requests-without-a-deployment-id).

All tabs of a browser share one `__vdpl` cookie. When another tab loads a page on a newer deployment, requests that rely on the cookie route to that deployment in every tab.

### Document navigations

**Skew Protection doesn't pin full-page navigations.** When the browser makes a top-level document request, such as a hard refresh, entering a URL in the address bar, or opening a link in a new tab, Vercel serves the latest production deployment. This applies whichever way the request carries the deployment ID: the `?dpl=` query parameter, the `x-deployment-id` header, or the `__vdpl` cookie.

For most applications, this is the ideal behavior. Users get the latest version on their next full-page navigation while their current page stays stable.

However, some applications have long-running sessions that must stay on one deployment. See [Pinning requests without a deployment ID](#pinning-requests-without-a-deployment-id) for how to pin every request from an open page.

## Enable Skew Protection

Projects created after November 19th 2024 using one of the [supported frameworks](#supported-frameworks) already have Skew Protection enabled by default.

For older projects, you can enable Skew Protection in your project's settings.

1. Ensure your project has the [Enable access to System Environment Variables](/docs/environment-variables/system-environment-variables#enable-system-environment-variables) setting enabled
2. Select the project in the Vercel dashboard
3. Open **Settings** in the sidebar
4. Select [**Advanced**](https://vercel.com/d?to=%2F%5Bteam%5D%2F%5Bproject%5D%2Fsettings%2Fadvanced\&title=Go+to+Advanced+settings) in the sidebar
5. Scroll down to **Skew Protection** and enable the switch
6. You can optionally [set a custom maximum age](#configure-maximum-age) (see [limitations](#limitations))
7. [Redeploy](/docs/deployments/managing-deployments#redeploy-a-project) your latest production deployment.

![A screenshot of the Skew Protection section containing an enabled/disabled toggle switch.](https://7nyt0uhk7sse4zvn.public.blob.vercel-storage.com/front/docs/projects/skew-protection-light.jpg)

## Allowed domains for cross-site fetch

By default, Skew Protection ignores the deployment ID on cross-origin requests. If another site fetches assets from your project with a `?dpl=` parameter or `x-deployment-id` header, Skew Protection does not pin the request to the specified deployment. Instead, the request is routed to the latest production deployment.

This causes problems when your project serves assets that are fetched cross-origin by other domains. For example, if a client application embeds assets from your project at build time, the asset URLs are baked into the HTML with the deployment ID from that build. When your project later redeploys, those pinned asset URLs route to the new deployment where the old assets no longer exist, resulting in 404 errors and broken JavaScript and CSS.

> **💡 Note:** This setting is not needed for [Vercel Microfrontends](/docs/microfrontends) because microfrontend projects share a domain, making all requests same-origin.

The **Allowed Domains for Cross-Site Fetch** setting solves this by specifying which external domains can make skew-protected requests to your project. Configure this on the project that **serves** the assets, not the project that consumes them.

### Configure allowed domains

1. Select the project that serves the cross-site assets in the Vercel dashboard
2. Open **Settings** in the sidebar
3. Select **Advanced** in the sidebar
4. Scroll down to **Skew Protection** and find **Allowed Domains for Cross-Site Fetch**
5. Click **Add Domain** and enter a hostname (for example, `app.example.com`), a wildcard (for example, `*.example.com`), or `*` to allow all domains
6. Click **Save**

You can add up to 12 domains. Each entry accepts:

| Format                  | Example           | What it matches                                                 |
| ----------------------- | ----------------- | --------------------------------------------------------------- |
| Bare hostname           | `example.com`     | Requests originating from `example.com` only                    |
| Hostname with subdomain | `app.example.com` | Requests originating from `app.example.com` only                |
| Wildcard                | `*.example.com`   | Requests originating from any direct subdomain of `example.com` |
| All domains             | `*`               | Requests originating from any domain                            |

> **💡 Note:** Wildcard entries (`*.example.com`) only match one level of subdomain. For example, `*.example.com` matches `app.example.com` but not `staging.app.example.com`. Use `*` to allow requests from any domain.

Inputs are normalized automatically: full URLs are reduced to their hostname, ports and paths are stripped, and values are lowercased.

> **💡 Note:** When using cross-site Skew Protection, set the maximum age to a value greater than your typical deployment interval. If the serving project redeploys and the pinned deployment ages out of the maximum age window, cross-site requests will fail even with allowed domains configured.

## Configure Maximum Age

The **Maximum Age** setting controls how long a deployment remains eligible for Skew Protection. If a client requests a deployment that no longer exists or is older than the configured maximum age (via the `?dpl=` query parameter, `x-deployment-id` header, or `__vdpl` cookie), Vercel ignores the requested deployment and serves the request from the deployment currently assigned to the domain.

To configure the maximum age:

1. Select the project in the Vercel dashboard
2. Open **Settings** in the sidebar
3. Select [**Advanced**](https://vercel.com/d?to=%2F%5Bteam%5D%2F%5Bproject%5D%2Fsettings%2Fadvanced\&title=Go+to+Advanced+settings) in the sidebar
4. Scroll down to **Skew Protection** and adjust the **Maximum Age** value

The default maximum age is 12 hours on Pro and one day on Enterprise, measured from when the deployment was created. This strikes a good balance between protecting from version skew for frequently deployed projects and preventing old deployments from being accessed. However, if your users keep the website open for extended periods without refreshing, such as dashboards, monitoring tools, or long-running workflows, you may want to increase this value. You can configure a maximum age up to your project's [Deployment Retention](/docs/deployment-retention) limit.

When you set a large maximum age, older deployments stay accessible for longer. If one of those deployments has a bug or security issue, you can either set a [Custom Skew Protection Threshold](#custom-skew-protection-threshold) or [delete the deployment](/docs/deployments/managing-deployments#delete-a-deployment) to stop clients from reaching it.

You can also use [Deployment Retention](/docs/deployment-retention) to delete old deployments automatically. When your retention period is short, you can safely set the maximum age to the same value, since deployments are deleted before they could age out of the protection window. You only need a low maximum age if your deployment retention is long and you want to limit how far back Skew Protection reaches.

## Custom Skew Protection Threshold

If a deployment has a bug or security issue, you can set a threshold to stop it and any deployments older than it from serving requests to active clients.

Once you deploy a fix, you can set a Skew Protection threshold with the following steps:

1. Select the deployment that fixed the problem in the deployment list
2. Select the  button (near the **Visit** button)
3. Click **Skew Protection Threshold**
4. Click **Set** to apply the changes

![A screenshot of the popover menu containing a menu item for Skew Protection Threshold.](https://7nyt0uhk7sse4zvn.public.blob.vercel-storage.com/front/docs/skew-protection/configure-skew-protection-light.png)

## Monitor Skew Protection

You can observe how many requests are protected from version skew by creating a [query](/docs/query) in the **Observability** section of the Vercel dashboard.

For example, on the **Requests** event type, filter where the [`Skew Protection`](/docs/query/reference#group-by-and-where-fields) field is `active`.

You can view CDN Requests that are successfully fulfilled without the need for skew protection by filtering where `Skew Protection` is `inactive`.

![A screenshot of a query of requests filtered by active skew protection.](https://7nyt0uhk7sse4zvn.public.blob.vercel-storage.com/front/docs/projects/skew-protection-monitoring-query-light-colinUpdate.png)

## Custom Deployment ID

When using prebuilt deployments (via `vercel deploy --prebuilt`), you can configure a custom deployment ID to ensure Skew Protection works correctly. This is necessary when building your application outside of Vercel's build environment (via `vercel build`) to ensure the ID matches once deployed to Vercel.

### Next.js

For Next.js applications, configure the `deploymentId` in your `next.config.js`:

```typescript filename="next.config.js"
/** @type {import('next').NextConfig} */
const nextConfig = {
  // Use a string value for the deployment ID
  deploymentId: process.env.GIT_HASH || Date.now().toString(),
};

module.exports = nextConfig;
```

User-configured deployment IDs have the following requirements:

- Cannot start with the `dpl_` prefix
- Must be unique per project
- Have a maximum length of 32 characters
- Can only contain alphanumeric characters (a-z, A-Z, 0-9), hyphens (-), and underscores (\_)

If you attempt to use a deployment ID that already exists, the deployment will fail.

> **💡 Note:** Skew Protection for prebuilt deployments with a custom deployment ID requires Next.js version greater than [v16.2.0-canary.15](https://github.com/vercel/next.js/releases/tag/v16.2.0-canary.15) and Vercel CLI version greater than `vercel@50.3.3`. Custom deployment ID configuration is currently only available for Next.js and Build Output API. Other frameworks (SvelteKit, Qwik, Astro) do not support custom deployment IDs at this time.

### Build Output API

For projects using the [Build Output API](/docs/build-output-api), you can configure the `deploymentId` in your `config.json` file:

Generate your `config.json` file programmatically to include a unique `deploymentId`:

```json filename=".vercel/output/config.json"
{
  "version": 3,
  "deploymentId": "<generated-at-build-time>"
}
```

## Supported frameworks

Skew Protection is available with zero configuration when Vercel runs your build. The following frameworks are supported:

- [Next.js](#skew-protection-with-next.js)
- [SvelteKit](#skew-protection-with-sveltekit)
- [Qwik](#skew-protection-with-qwik)
- [Astro](#skew-protection-with-astro)
- [Nuxt](#skew-protection-with-nuxt)
- [React Router](#skew-protection-with-react-router)

Other frameworks can implement Skew Protection by checking if `VERCEL_SKEW_PROTECTION_ENABLED` has value `1`
and then appending the value of `VERCEL_DEPLOYMENT_ID` to each request using **one of the following** options.

- `dpl` query string parameter:

  ```ts filename="option1.ts"
  const query =
    process.env.VERCEL_SKEW_PROTECTION_ENABLED === '1'
      ? `?dpl=${process.env.VERCEL_DEPLOYMENT_ID}`
      : '';

  const res = await fetch(`/get${query}`);
  ```

- `x-deployment-id` header:

  ```ts filename="option2.ts"
  const headers =
    process.env.VERCEL_SKEW_PROTECTION_ENABLED === '1'
      ? { 'x-deployment-id': process.env.VERCEL_DEPLOYMENT_ID }
      : {};

  const res = await fetch('/get', { headers });
  ```

- `__vdpl` cookie:

  ```ts filename="option3.ts"
  export default function handler(req, res) {
    if (
      process.env.VERCEL_SKEW_PROTECTION_ENABLED === '1' &&
      req.headers['sec-fetch-dest'] === 'document'
    ) {
      res.setHeader('Set-Cookie', [
        `__vdpl=${process.env.VERCEL_DEPLOYMENT_ID}; Path=/; SameSite=Strict; Secure; HttpOnly`,
      ]);
    }
    res.end('<h1>Hello World</h1>');
  }
  ```

### Skew Protection with Next.js

> **💡 Note:** If you're building outside of Vercel using `vercel build` and then deploying with `vercel
>   deploy --prebuilt`, Skew Protection requires a custom deployment ID so the
> build-time ID matches the one Vercel assigns at deploy time.See [Custom Deployment ID](#custom-deployment-id) for setup instructions.For more information on prebuilt workflows, see [When not to use --prebuilt](/docs/cli/deploy#when-not-to-use---prebuilt).

If you are using Next.js 14.1.4 or newer and building on Vercel, there is no additional configuration needed to [enable Skew Protection](#enable-skew-protection).

Older versions of Next.js require additional [`next.config.js`](https://nextjs.org/docs/app/api-reference/config/next-config-js) configuration.

**View config for 13.4.7 to 14.1.3**

<br />

```typescript filename="next.config.js"
/** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    useDeploymentId: true,
    // Optionally, use with Server Actions
    useDeploymentIdServerActions: true,
  },
};

module.exports = nextConfig;
```

The `useDeploymentId` configuration enables Skew Protection for all framework-managed static file requests from your Next.js app such as for JavaScript and CSS files. You can also opt-into Skew Protection for [Next.js Server Actions](https://nextjs.org/docs/app/getting-started/mutating-data) with `useDeploymentIdServerActions`.

### Skew Protection with SvelteKit

If you are using SvelteKit, you will need to install `@sveltejs/adapter-vercel` version 5.2.0 or newer to [enable Skew Protection](#enable-skew-protection).

Older versions can be upgraded by running `npm i -D @sveltejs/adapter-vercel@latest`.

### Skew Protection with Qwik

If you are using Qwik 1.5.3 or newer, there is no additional configuration needed to [enable Skew Protection](#enable-skew-protection).

Older versions can be upgraded by running `npm i @builder.io/qwik@latest`.

### Skew Protection with Astro

If you are using Astro, you will need to install `@astrojs/vercel` version 9.0.0 or newer to [enable Skew Protection](#enable-skew-protection).

```js {8} filename="astro.config.mjs"
import { defineConfig } from 'astro/config';
import vercel from '@astrojs/vercel';

export default defineConfig({
  // ...
  output: 'server',
  adapter: vercel({
    skewProtection: true,
  }),
});
```

Older versions can be upgraded by running `npm i -D @astrojs/vercel@latest`.

### Skew Protection with Nuxt

If you are using Nuxt, there is no additional configuration needed to [enable Skew Protection](#enable-skew-protection). Simply enable Skew Protection in the Vercel dashboard and Nuxt will automatically handle the deployment ID for framework-managed requests.

### Skew Protection with React Router

If you are using [React Router as a framework](/docs/frameworks/frontend/react-router), there is no additional configuration needed to [enable Skew Protection](#enable-skew-protection).

Each time Vercel serves a page, it sets a `__vdpl` cookie with the ID of the deployment that served the page. Your browser sends the cookie with every later request to the same domain, so assets, data requests, and your custom `fetch()` calls all resolve to that deployment. As with other frameworks, a [full-page navigation](#document-navigations) loads the latest deployment.

## Pinning requests without a deployment ID

Frameworks that use the `__vdpl` cookie pin every request the page makes. Next.js and Astro add the deployment ID only to the requests that the framework makes from the browser. The latest deployment serves the others:

- **Custom `fetch()` calls**: Requests that your own client code sends
- **Server-side requests to your own domain**: For example, when a Next.js Server Action calls `redirect()`, Next.js fetches the destination page on the server

After a new deployment, these requests reach a newer version than the open page. In the redirect example, the browser receives a page from the newer deployment. Next.js 16.2 and later then load the destination with a full-page navigation on the latest deployment. Earlier versions render the newer deployment's page inside the open page, which can fail when the two versions differ.

For most applications, this is acceptable. Some sessions must stay on one deployment, such as a live exam, a call, or a multi-step checkout. For these, set the `__vdpl` cookie. It covers every request from the page, including the Server Action redirect.

When Vercel receives a request with the `__vdpl` cookie set, it routes that request to the deployment ID stored in the cookie. Your browser sends the cookie with every request to the same domain, and Next.js forwards it when a Server Action redirects. The requests of an open page then stay on that deployment after a new deployment goes live. A [full-page navigation](#document-navigations), such as a hard refresh, is the exception: it loads the latest deployment even with the cookie set.

To pin a single custom `fetch()` call without the cookie, add the deployment ID to that request yourself, in one of the formats listed in [Supported frameworks](#supported-frameworks).

### Setting the cookie in Routing Middleware

You can set the `__vdpl` cookie in [Routing Middleware](/docs/routing-middleware) to pin the requests of an open page:

```ts filename="middleware.ts"
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
  const response = NextResponse.next();
  const deploymentId = process.env.VERCEL_DEPLOYMENT_ID;
  const isPageLoad = request.headers.get('sec-fetch-dest') === 'document';

  if (deploymentId && (isPageLoad || !request.cookies.get('__vdpl'))) {
    response.cookies.set('__vdpl', deploymentId, {
      path: '/',
      httpOnly: true,
      sameSite: 'strict',
    });
  }

  return response;
}
```

With this middleware, each full-page load sets the `__vdpl` cookie to the deployment that serves the page. A request that arrives without the cookie sets it to the deployment that serves that request. Later requests from the page, including client-side navigations and your custom `fetch()` calls, route to that deployment.

Your browser keeps one `__vdpl` cookie for the domain. A full-page load in another tab replaces it, and custom `fetch()` calls from the older tab then route to the newer deployment.

### Scoping the pin to specific routes

After the middleware sets the cookie, it applies to every request to the same domain. If only some routes start a pinned session, add a [matcher](/docs/routing-middleware#path-matching) so the middleware sets the cookie only on those paths:

```ts filename="middleware.ts"
// ... same middleware function as above

export const config = {
  matcher: ['/exam/:path*', '/session/:path*'],
};
```

### Clearing the pin

When the user's session completes, clear the `__vdpl` cookie so Vercel no longer pins later requests to that deployment. You can do this from a [Server Action](https://nextjs.org/docs/app/getting-started/mutating-data):

```ts filename="app/actions.ts"
'use server';

import { cookies } from 'next/headers';

export async function clearDeploymentPin() {
  const cookieStore = await cookies();
  cookieStore.delete('__vdpl');
}
```

Then call the action from the client when the session ends:

```tsx filename="app/components/session-complete.tsx"
'use client';

import { clearDeploymentPin } from '../actions';

export function SessionComplete() {
  async function handleComplete() {
    await clearDeploymentPin();
    window.location.href = '/';
  }

  return <button onClick={handleComplete}>Finish session</button>;
}
```

> **💡 Note:** The `__vdpl` cookie pins requests to a deployment only while that deployment
> is younger than the [Maximum Age](#configure-maximum-age). Vercel measures the
> age from the deployment's creation time, not from when you set the cookie.
> After that, Vercel serves the latest deployment, even with the cookie set. The
> same applies if you delete the deployment or your [retention
> policy](/docs/deployment-retention) removes it. Choose a Maximum Age that
> covers the time between your deployments plus your longest session.

## Limitations

Skew Protection is available for all deployment environments for Pro and Enterprise teams. You can configure a custom maximum age up to, but not exceeding, your project's [retention policy](/docs/deployment-retention).

Vercel automatically adjusts the maximum age to 60 days for requests from Googlebot and Bingbot to handle any delay between document crawl and render.

Deployments that have been deleted either manually or automatically using a [retention policy](/docs/deployment-retention) will not be accessible through Skew Protection.

## More resources

- [Version Skew Protection blog](/blog/version-skew-protection)
- [Version Skew](https://www.industrialempathy.com/posts/version-skew/)


---

[View full sitemap](/docs/sitemap)


If you encounter incorrect information, can’t find what you need, need a capability Vercel doesn’t currently offer, or have any other feedback to report, please do it by following the instructions at https://vercel.com/feedback.md.
