Pin the toolchain and install from the lockfile
Commit the lockfile and install with npm ci, which fails if package-lock.json and package.json disagree and never rewrites either file. Pin the Node.js version too: Netlify reads .nvmrc or .node-version before falling back to its own default.
node --version > .nvmrc
git add package-lock.json .nvmrc
# locally, in CI and as the host's install command
npm ci
Reproduce the host's build locally and compare the output
Delete node_modules and the build output, run npm ci and the same build command the host runs, then serve the output directory with a static server, not the dev server. On Vercel, compare the result with the deployment's Source tab (or /_src on the deployment URL) and the Resources tab, which lists the functions, middleware and assets that were actually built.
Rebuild once without the cache
If the clean local build is correct and production is not, rule out the cache. On Vercel, redeploy with 'Use existing Build Cache' unchecked, run vercel --force, or set VERCEL_FORCE_NO_BUILD_CACHE=1. On Netlify, deploy the latest branch commit with the clear cache option.
vercel --force
Make old tabs survive a deploy
Serve HTML with Cache-Control: no-cache, and reload the page once when a chunk fails to load. On Vercel, Skew Protection pins framework-managed asset and navigation requests to the deployment that served the page. It supports Next.js, SvelteKit, Qwik, Astro and Nuxt on the Pro and Enterprise plans.
window.addEventListener('vite:preloadError', () => {
window.location.reload()
})
Smoke-test the production URL after every deploy
Fetch the HTML, one real asset it references, and one function or data endpoint, with a timeout. curl -f fails on any HTTP status of 400 or above, so a 404 or 504 fails the script and the release. Adjust the asset pattern to your framework's output path.
set -e
URL=https://your-domain.example
curl -fsS "$URL/" -o /tmp/index.html
ASSET=$(grep -oE '/assets/[^"]+\.js' /tmp/index.html | head -1)
curl -fsS "$URL$ASSET" -o /dev/null
curl -fsS --max-time 10 "$URL/api/health"
Deploy the backend first and keep changes additive
Ship the API or database change before the frontend that depends on it. Keep old fields and endpoints working until no deployed client uses them. Make every function return a response on every path, including errors, so a failure shows up as a status code instead of a timeout.
If pushes stop deploying, check the automation
On Vercel, check the production deployment tile for an Undo Rollback button. Use it, or promote a deployment, to turn auto-assignment back on. Then check the repository's webhooks for the host's entry (disconnect and reconnect the repository if it is missing), the Git login connection, the production branch setting, the Ignored Build Step script, and git.deploymentEnabled in vercel.json.
vercel promote <deployment-url>