Visitar URL original
Skew ProtectionSkip to content

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:

FrameworkHow the deployment ID travelsWhat Vercel pins
Next.js, AstroThe framework adds a ?dpl= query parameter or an x-deployment-id header to the requests that it makes from the browserFramework-managed requests
SvelteKit, Nuxt, Qwik, React RouterYour browser sends a __vdpl cookie with every request to the same domainFramework-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.

  1. Ensure your project has the Enable access to System Environment Variables setting enabled
  2. Select the project in the Vercel dashboard
  3. Open Settings in the sidebar
  4. Select Advanced in the sidebar
  5. Scroll down to Skew Protection and enable the switch
  6. You can optionally set a custom maximum age (see limitations)
  7. Redeploy your latest production deployment.
A screenshot of the Skew Protection section containing an enabled/disabled toggle switch.
A screenshot of the Skew Protection section containing an enabled/disabled toggle switch.

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.

This setting is not needed for Vercel 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.

  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:

FormatExampleWhat it matches
Bare hostnameexample.comRequests originating from example.com only
Hostname with subdomainapp.example.comRequests originating from app.example.com only
Wildcard*.example.comRequests originating from any direct subdomain of example.com
All domains*Requests originating from any domain

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.

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.

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 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 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:

  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.
A screenshot of the popover menu containing a menu item for Skew Protection Threshold.

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.

A screenshot of a query of requests filtered by active skew protection.
A screenshot of a query of requests filtered by active skew protection.

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:

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.

Skew Protection for prebuilt deployments with a custom deployment ID requires Next.js version greater than 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.

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:

.vercel/output/config.json
{
  "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.

  • dpl query string parameter:
    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:
    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:
    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>');
    }

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 for setup instructions.

For more information on prebuilt workflows, see 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.

Older versions of Next.js require additional next.config.js configuration.

View config for 13.4.7 to 14.1.3
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 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.

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.

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:

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.

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:

middleware.ts
// ... 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:

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:

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>;
}

The __vdpl cookie pins requests to a deployment only while that deployment is younger than the 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 removes it. Choose a Maximum Age that covers the time between your deployments plus your longest session.

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.

Last updated September 16, 2026

Was this helpful?

supported.