Skew Protection
Pin a client's requests to the deployment that served its page, avoiding version-skew errors after a new deployment goes live.
Skew Protection
Version skew happens when a user's browser is running the client bundle from
one deployment while their follow-up requests hit a newer deployment that went
live mid-session. The classic symptom is a ChunkLoadError — a code-split
JavaScript chunk that no longer exists on the new deployment — along with broken
client/server API contracts.
Skew Protection fixes this by pinning a client's framework-managed requests (static chunks, navigations, prefetches) to the exact deployment that served the initial page load, for as long as that deployment is recent. Full-page navigations still get the latest deployment.
Enabling
Skew Protection is on by default for every project. Every successful deployment stays reachable, so pinned requests keep resolving.
To opt out, set skew_protection: false in your ploy.yaml:
kind: dynamic
build: pnpm build
out: dist
skew_protection: false # disable skew protectionNext.js
For Next.js projects there is nothing to configure. Ploy injects
NEXT_DEPLOYMENT_ID into the build, which Next.js reads natively to pin its own
framework-managed requests — asset loads, client-side navigations, and
prefetches all resolve to the deployment that served the page. Next.js also
triggers a full reload automatically if it detects a deployment mismatch.
Do not set deploymentId in your next.config. Next.js gives an explicit
config value precedence over NEXT_DEPLOYMENT_ID, so a custom value would be
sent on framework requests instead of the Ploy deployment id and those
requests would fail to resolve. Leave it unset and let Ploy manage it.
Your own client fetches
Next.js pins the requests it makes for you, but not the custom fetch calls you
write. To pin those, use ployFetch:
"use client";
import { ployFetch } from "@meetploy/nextjs/config";
const res = await ployFetch("/api/profile");Full-page navigations (hard refresh, opening a link in a new tab) intentionally load the latest deployment. Skew Protection keeps the current page stable; the next full navigation picks up new deployments.
Other frameworks
Skew Protection works with any project — the edge honors a pin on every request. Pinning is only automatic for Next.js; for other frameworks and workers, attach the deployment id yourself. When enabled, these environment variables are available at both build time and runtime:
| Variable | Value |
|---|---|
PLOY_DEPLOYMENT_ID | The current deployment's id (always injected). |
PLOY_SKEW_PROTECTION | "1" when skew protection is enabled. |
Send the id on a request using either the ?dpl= query parameter or the
x-deployment-id header:
const res = await fetch(`/api/data?dpl=${process.env.PLOY_DEPLOYMENT_ID}`);
// or
const res = await fetch("/api/data", {
headers: { "x-deployment-id": process.env.PLOY_DEPLOYMENT_ID },
});How it works
- Build — Ploy injects the deployment id into the build (
NEXT_DEPLOYMENT_IDfor Next.js, plusPLOY_DEPLOYMENT_IDandPLOY_SKEW_PROTECTIONfor other frameworks). - Client — the framework (Next.js automatically) or your code attaches the
deployment id to requests as
?dpl=orx-deployment-id. - Edge — Ploy routes a pinned request to that exact deployment, as long as
it is a successful, skew-protected deployment on the same project and branch
the host resolved to, created within the max-age window (24 hours by default).
Otherwise the request returns
404, which prompts the framework to reload to the latest version.
Next Steps
- Configuration - Full
ploy.yamlreference - Environment Variables - Configure worker environment variables
- Next.js - Next.js integration
How is this guide?
Last updated on