
Compare Preview and Production Before You Merge
Catch preview deployment drift in status codes, redirects, headers, cookie attributes, canonical URLs, and robots metadata before production.
A preview can render the right page and still return the wrong status, skip a redirect, drop a security header, set a weaker cookie, or point its canonical URL at the preview host. Build checks do not see those differences because they live at the deployment boundary.
The short answer
After the preview is live, send the same bounded request to preview and production. Compare behavior, not body content. Gate the merge on status and final-route errors, then decide which header and metadata differences should be warnings for your application.
npx deployproof \
--preview https://feature-auth.example.workers.dev \
--production https://example.com \
/ /login /api/healthA preview URL is an environment, not a contract
Cloudflare's Workers preview URL documentation describes versioned and aliased URLs that can be used in CI and pull requests. GitHub deployment environments can expose a deployment URL and apply protection rules. Those mechanisms make a preview reachable and govern when a deployment may proceed. They do not establish that the preview behaves like the current production site.
Compare the response surface users and crawlers receive
DeployProof records the status, redirect chain, final path and query, selected cache and security headers, cookie names and attributes, and public HTML metadata. Hostnames are intentionally ignored for final-route comparison, so preview.example.test/docs can match example.com/docs.
Redirects deserve their own comparison. MDN's redirect guide distinguishes permanent and temporary status codes and notes their method-handling differences. A preview returning 302 while production returns 308 can affect caching, crawlers, and non-GET requests even when both land on the same page in a browser.
{
"production": "https://example.com",
"routes": [
"/",
"/login",
{
"path": "/api/health",
"expect": {
"status": 200,
"contentType": "application/json"
}
}
],
"timeoutMs": 10000,
"maxRedirects": 5,
"maxBodyBytes": 512000
}Compare cookie security without exposing sessions
Cookie values should not appear in a CI log. DeployProof reduces each Set-Cookie header to the cookie name and normalized attributes. That is enough to catch a missing Secure, HttpOnly, or SameSite attribute while keeping session values out of memory beyond the response parser and out of every report. See MDN's Set-Cookie reference for how those attributes change browser behavior.
Readable response bodies are capped at 500 KB by default and parsed only for the title, canonical link, and robots meta tag. Bodies are never included in human, JSON, GitHub, or SARIF output. Requests time out and redirect chains have a fixed limit.
Turn the comparison into a merge check
name: Preview proof
on: [pull_request]
jobs:
compare:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: loke-dev/deployproof@master
with:
preview: ${{ steps.deploy.outputs.deployment-url }}
production: https://example.com
strict: trueHuman output is designed for local investigation. GitHub format emits one annotation per difference. JSON works for dashboards and custom policy. SARIF can be uploaded to code scanning. Errors always fail; --strict also fails on warnings while notices remain informational.
DeployProof
preview https://feature-auth.example.workers.dev
production https://example.com
✓ / matches production
● /login
! DP005 Cookie names or security attributes differ
preview [httponly, samesite]
production [httponly, samesite, secure]
1 matching · 0 errors · 1 warning · 0 noticesControl expected drift instead of ignoring the check
Some differences are intentional. A preview may have a stricter robots directive, a different cache lifetime, or platform-specific content encoding. Keep expected status and content type explicit per route. Use ignoreHeaders only for headers that are understood and documented. Do not turn off strict mode merely because the first run finds real environment drift.
What this check does not replace
A behavioral diff is not a browser test, an accessibility audit, a database migration test, or an observability system. It answers a narrower release question: for these public routes, does the preview expose the same HTTP contract as production? That makes it useful beside unit, integration, and end-to-end tests rather than instead of them.
DeployProof is open source in the DeployProof repository. Add the production URL and a small route set first. Expand the contract when a real incident teaches you which behavior matters.
Sources and further reading
- Workers preview URLs · Cloudflare
- Deployment environments · GitHub
- Redirections in HTTP · MDN
- Set-Cookie header · MDN
- DeployProof source and tests · GitHub