Skew Protection
Skew Protection is available on Enterprise and Pro plans
Those with the owner, admin, member role can access this feature
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.
Vercel's Skew Protection resolves this problem at the platform and framework layer by using 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.


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.
When Skew Protection is enabled with a supported framework, 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.
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.
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 for how to pin every request from an open page.
Projects created after November 19th 2024 using one of the supported frameworks already have Skew Protection enabled by default.
For older projects, you can enable Skew Protection in your project's settings.
- Ensure your project has the Enable access to System Environment Variables setting enabled
- Select the project in the Vercel dashboard
- Open Settings in the sidebar
- Select Advanced in the sidebar
- Scroll down to Skew Protection and enable the switch
- You can optionally set a custom maximum age (see limitations)
- Redeploy your latest production deployment.


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.
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.
- Select the project that serves the cross-site assets in the Vercel dashboard
- Open Settings in the sidebar
- Select Advanced in the sidebar
- Scroll down to Skew Protection and find Allowed Domains for Cross-Site Fetch
- Click Add Domain and enter a hostname (for example,
app.example.com), a wildcard (for example,*.example.com), or*to allow all domains - 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 |
Inputs are normalized automatically: full URLs are reduced to their hostname, ports and paths are stripped, and values are lowercased.
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:
- Select the project in the Vercel dashboard
- Open Settings in the sidebar
- Select Advanced in the sidebar
- 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 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 or delete the deployment to stop clients from reaching it.
You can also use 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.
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:
- Select the deployment that fixed the problem in the deployment list
- Select the button (near the Visit button)
- Click Skew Protection Threshold
- Click Set to apply the changes


You can observe how many requests are protected from version skew by creating a query in the Observability section of the Vercel dashboard.
For example, on the Requests event type, filter where the Skew Protection 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.


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.
For Next.js applications, configure the deploymentId in your 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.
For projects using the Build Output API, you can configure the deploymentId in your config.json file:
Generate your config.json file programmatically to include a unique deploymentId:
{
"version": 3,
"deploymentId": "<generated-at-build-time>"
}Skew Protection is available with zero configuration when Vercel runs your build. The following frameworks are supported:
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.
-
dplquery string parameter:option1.tsconst query = process.env.VERCEL_SKEW_PROTECTION_ENABLED === '1' ? `?dpl=${process.env.VERCEL_DEPLOYMENT_ID}` : ''; const res = await fetch(`/get${query}`); -
x-deployment-idheader:option2.tsconst headers = process.env.VERCEL_SKEW_PROTECTION_ENABLED === '1' ? { 'x-deployment-id': process.env.VERCEL_DEPLOYMENT_ID } : {}; const res = await fetch('/get', { headers }); -
__vdplcookie:option3.tsexport 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>'); }
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.
Older versions of Next.js require additional next.config.js configuration.
View config for 13.4.7 to 14.1.3
/** @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 with useDeploymentIdServerActions.
If you are using SvelteKit, you will need to install @sveltejs/adapter-vercel version 5.2.0 or newer to enable Skew Protection.
Older versions can be upgraded by running npm i -D @sveltejs/adapter-vercel@latest.
If you are using Qwik 1.5.3 or newer, there is no additional configuration needed to enable Skew Protection.
Older versions can be upgraded by running npm i @builder.io/qwik@latest.
If you are using Astro, you will need to install @astrojs/vercel version 9.0.0 or newer to enable Skew Protection.
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.
If you are using Nuxt, there is no additional configuration needed to enable Skew Protection. Simply enable Skew Protection in the Vercel dashboard and Nuxt will automatically handle the deployment ID for framework-managed requests.
If you are using React Router as a framework, there is no additional configuration needed to 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 loads the latest deployment.
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, 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.
You can set the __vdpl cookie in Routing Middleware to pin the requests of an open page:
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.
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 so the middleware sets the cookie only on those paths:
// ... same middleware function as above
export const config = {
matcher: ['/exam/:path*', '/session/:path*'],
};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:
'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:
'use client';
import { clearDeploymentPin } from '../actions';
export function SessionComplete() {
async function handleComplete() {
await clearDeploymentPin();
window.location.href = '/';
}
return <button onClick={handleComplete}>Finish session</button>;
}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.
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 will not be accessible through Skew Protection.
Was this helpful?