AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Vercel Deployment Password Gate

skill-stealth-factory-skills-vercel-deployment-password-gate · by stealth-factory

A free DIY reimplementation of Vercel's $150/mo Advanced Deployment Protection add-on — all three features (Password Protection, private/prod deployments, Deployment Protection Exceptions) plus named automation bypass tokens — a middleware gate for ANY framework on Vercel (Next.js proxy, or SvelteKit/Nuxt/Astro/Remix/static via Routing Middleware). Gates previews by default, production opt-in; fu…

No reviews yet
0 installs
8 views
0.0% view→install

Install

$ agentstack add skill-stealth-factory-skills-vercel-deployment-password-gate

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access Used
  • Filesystem access Used
  • Shell / process execution No
  • Environment & secrets Used
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-stealth-factory-skills-vercel-deployment-password-gate)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
17d ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Vercel Deployment Password Gate? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Vercel deployment password gate

A free, portable password wall for preview deployments by default — and for production too, opt-in (see "Gating production too"). Humans see a brandable unlock form once (a signed 1-year cookie keeps them in); automation passes with named bypass tokens via header or query parameter. In the default preview-only posture, production ships no middleware function at all (Mode B) or a one-boolean short-circuit (Mode A), so the gate's production cost is zero. Only a scrypt hash of the password is stored; unlock cookies are keyed per-credential, so rotating the password or removing a bypass token revokes exactly the cookies it issued.

What this reimplements

A free, DIY reimplementation of Vercel's Advanced Deployment Protection add-on — $150/mo on Pro (30-day minimum before you can cancel), included on Enterprise, and apparently not sold on Hobby at all — rebuilt in one middleware file. Vercel bundles three features into the add-on — all three are supported here:

| Advanced Deployment Protection | This skill | | --- | --- | | Password Protection | ✅ Unlock form + DEPLOY_GATE_PASSWORD_HASH (scrypt), mirroring the platform's semantics: enter once per deployment URL, and changing the password invalidates the cookies it issued. One difference: Vercel's change takes effect on existing deployments immediately; ours applies to new builds, so redeploy to revoke now. | | Private Production Deployments (password on the production domain too) | ✅ Opt-in — previews by default, production via "Gating production too". Trade-off: the middleware then runs in prod, so the zero-prod-cost property is gone. | | Deployment Protection Exceptions (unprotect specific preview domains) | ✅ DEPLOY_GATE_UNPROTECTED_HOSTS — comma-separated hosts that skip the gate and are public. Same axis as Vercel's (the domain); the matcher is a different thing (path-level: /api, static assets). See "Unprotect specific domains". |

Plus an equivalent of Protection Bypass for Automation: named, individually revocable tokens, same header / query-param / set-cookie UX. This is parity, not an improvement — Vercel's own bypass also supports multiple named, individually revocable secrets ("You can create multiple bypass secrets per project", docs updated 2026-04-30), and theirs additionally clears Firewall and bot-protection challenges, which a DIY token cannot. The reason to use ours is that it works with our gate; if you're on platform protection, use theirs.

Not reimplemented: Shareable Links (a per-recipient bypass token is the closest analogue — no TTL), Trusted IPs/Passport (out of scope), and Vercel Authentication, which can't be — that session lives on vercel.com.

Two things $150/mo can't buy you. The unlock page is yours — Vercel's password screen is Vercel-branded with no documented theming hook (its whole config surface across dashboard, API, and Terraform is deploymentType + password), so matching a client's brand, logo, and design system is only possible DIY. And on Hobby, where the docs indicate the add-on isn't sold, this is very likely the only password option at all.

What the platform does better: it runs before your code (protects static assets and every route, nothing to misconfigure), it can't fail open on a missing env var, and it's Vercel's problem to maintain. This gate is a speed bump, not auth — see the gotcha of the same name.

Release-method agnostic: everything keys off the Vercel environment (VERCEL_TARGET_ENV), never branch names — it composes with any promotion/branch/deploy model.

Step 0 — check whether you need this at all

| Need | Right tool | | --- | --- | | Only the Vercel team views previews | Vercel Authentication (Deployment Protection → Standard). Free on all plans, zero code, team members pass invisibly via their Vercel login. Prefer this when it fits. | | External stakeholders | Vercel Authentication + Shareable Links (all plans; Hobby is capped at one link per account, Pro+ lifts the cap). Still zero code. | | Anyone-with-a-password, for free | This skill. Vercel's Password Protection is Enterprise-only, or Pro + $150/mo for the Advanced Deployment Protection add-on (which you must keep ≥30 days before you can cancel) — both verified against Vercel's docs 2026-07-17; re-check pricing. On Hobby it appears unbuyable (the docs list it as "Enterprise, or a paid add-on for Pro", and say Hobby gets only Vercel Authentication) — inferred from the plan listings, not stated outright, so the DIY gate is very likely the only password option there. | | The password page must carry your/your client's branding | This skill. Vercel's password screen is Vercel's — deploymentType + password is its whole documented config surface, with no theming hook. This gate renders your own HTML. | | Non-Next framework, static export, or SPA on Vercel | Still this skill — use the framework-agnostic template via Vercel Routing Middleware (see "Pick your template"), which runs platform-level before the app or static assets. |

A DIY gate cannot detect "is this visitor logged into Vercel" — that session lives on vercel.com and is only checkable by platform-level Vercel Authentication, which runs before your code. Don't try to hybridize; pick per the table.

Already on Vercel Authentication, need limited third-party access?

The DIY gate can't help here (the platform wall blocks third parties before your code runs). Use the platform's own bypass methods instead:

  1. Shareable Links — the purpose-built answer. Minted per deployment

URL/alias with optional TTL, individually revocable, no shared secret. Create from the deployment's Share dialog in the dashboard, or via API (PATCH /aliases/{id}/protection-bypass, ttl). Available on Hobby too, but capped at one link per account there; Pro+ lifts the cap (verified 2026-07-17).

  1. Protection Bypass for Automation — secrets in a crafted URL:

https:///?x-vercel-protection-bypass=&x-vercel-set-bypass-cookie=true persists a bypass cookie. You can create multiple named secrets per project, each revocable independently (docs updated 2026-04-30), and they also clear Firewall/bot-protection challenges. Caveat: secrets in URLs end up in logs, so prefer the header where the caller supports it.

Pick your template

Two templates, identical behavior, env vars, and helper scripts — pick by framework:

| Project | Template | Installs as | | --- | --- | --- | | Next.js | [templates/deploy-gate.ts](./templates/deploy-gate.ts) — no deps beyond next/server + node:crypto | proxy.ts (Mode B) or lib/deploy-gate.ts (Mode A) | | Anything else on Vercel (SvelteKit, Nuxt, Astro, Remix, static/SPA) | [templates/deploy-gate.vercel.ts](./templates/deploy-gate.vercel.ts) — uses Vercel Routing Middleware; one dep (@vercel/functions); config.runtime must stay "nodejs" (edge is the default and lacks node:crypto) | root middleware.ts, next to package.json |

The skill's mechanics (VERCEL_TARGET_ENV gating, env-var management via vercel env, build-time strip) are Vercel-platform-wide, not framework-specific.

How the gate works (both modes)

Single self-contained file:

  • Gates every remote non-production Vercel deployment — preview **and any

custom environment (e.g. a named "staging"). The signal is VERCEL_TARGET_ENV (falling back to VERCEL_ENV when absent), NOT VERCEL_ENV: VERCEL_ENV only ever reports production/preview/development and collapses every custom environment into one of those buckets, so a custom target can read VERCEL_ENV=production and slip through ungated. VERCEL_TARGET_ENV carries the custom name. It fails open when that value is production or development; when it's unset, it fails open only for a genuine local dev server (NODE_ENV === "development") or** when nothing is configured — a Vercel deploy with System Env Vars disabled also reads unset but runs NODE_ENV=production, so a configured one there still gates (see the Mode B lifecycle note).

  • No valid cookie → responds 401 with an inline HTML password form (no extra

routes/pages added to the app). Form POSTs to /__deploy-unlock.

  • Human auth: DEPLOY_GATE_PASSWORD_HASH stores s2:: (scrypt, memory-hard)

never the plaintext. Submitted passwords are run through scrypt and compared constant-time. (Legacy fallback: a plaintext DEPLOY_GATE_PASSWORD also works.)

  • Automation auth (mimics Vercel's Protection Bypass for Automation):

DEPLOY_GATE_BYPASS_TOKENS stores JSON {"":"", ...} — plaintext by design (automation must read tokens back; they're generated random, never human-reused). Send a token via the x-deploy-gate-bypass header (passes through + sets the cookie) or query parameter (303 redirect to the cleaned URL — token stripped from the address bar — with the cookie set, so one crafted link = click-once access for a service that can't set headers). Bypass accepts tokens only — the human password never works in the header or query param: verifying a password costs a memory-hard scrypt run, so accepting it per-request would hand attackers a CPU-DoS amplifier (and passwords don't belong in URLs). The password unlocks solely via the form.

  • Unlock cookies are HMACs keyed on the credential that minted them:

rotating the password kills password-issued cookies; removing a bypass token kills that token's cookies. maxAge 1 year → "unlocks once, stays unlocked".

  • Absent config fails open (a fresh clone never bricks its previews);

present-but-malformed DEPLOY_GATE_PASSWORD_HASH fails CLOSED (503) — a typo must not silently publish a preview the operator meant to protect. A legacy DEPLOY_GATE_PASSWORD longer than the 256-char cap also fails closed (it would hash into a config the unlock form's length cap can never match — gated with no way in), and the 503 body names both causes. Malformed bypass-token JSON is ignored with a warning (password still works).

Why a cookie, not localStorage: the decision happens server-side in the proxy before any JavaScript runs; the token must travel with the request. localStorage physically cannot gate SSR. Same "enter once" UX.

Env-var names (and the legacy aliases)

All config vars use the DEPLOY_GATE_ prefix, because the gate protects production too — the old PREVIEW_-prefixed names implied preview-only and were misleading once production gating landed:

| Purpose | Current name | Legacy alias (still honoured) | | --- | --- | --- | | Password hash | DEPLOY_GATE_PASSWORD_HASH | PREVIEW_PASSWORD_HASH | | Plaintext password (legacy scheme) | DEPLOY_GATE_PASSWORD | PREVIEW_PASSWORD | | Automation bypass tokens | DEPLOY_GATE_BYPASS_TOKENS | PREVIEW_GATE_BYPASS_TOKENS | | Unprotected-host allowlist | DEPLOY_GATE_UNPROTECTED_HOSTS | (new — no alias) |

The gate reads the current name first and falls back to the legacy alias with a one-time warning if only the old one is set — a rename can't fail open, because absent config is intentionally fail-open (an existing install that still has PREVIEW_PASSWORD_HASH keeps working; migrate at leisure). The current name wins if both are set. To migrate, add the DEPLOY_GATE_* var and remove the PREVIEW_* one; the alias support is a courtesy, not a permanent contract.

Mode A — app already has middleware.ts / proxy.ts

The middleware function already runs on every matched request, so the gate adds one env-var boolean in production — no new invocations, no meaningful cost.

> ⚠️ If the host file is legacy middleware.ts, migrate it to proxy.ts > first (npx @next/codemod@canary middleware-to-proxy . — the tag Next's proxy docs use for this codemod, or rename the file > + the exported function and fix test imports). middleware.ts runs on the > Edge runtime even in Next 16, where node:crypto does not exist — the > gate 500s every request. Caught in a real install (piaf-web, Next 16.2.7, > 2026-07-17): Error: Failed to load external module node:crypto.

  1. Copy [templates/deploy-gate.ts](./templates/deploy-gate.ts) **next to your

host proxy file** and import it relatively. If the host proxy is at the project root (proxy.ts), put the helper at lib/deploy-gate.ts; if the host uses a src/ layout (src/proxy.ts), put it at src/lib/deploy-gate.ts — a root ./lib/deploy-gate import from src/proxy.ts resolves to src/lib/… and won't find a root lib/, so the build fails.

  1. Wire it into the existing proxy() / middleware() function — check first,

attach the cookie to whatever response the pipeline produces last. Pass the request protocol so the unlock cookie persists on a local http:// dev run:

```ts import { previewGate, withUnlockCookie } from "./lib/deploy-gate";

export async function proxy(request: NextRequest) { const gate = await previewGate(request); if (gate.block) return gate.block;

// Strip the bypass token from the headers forwarded upstream, so your app / // request logging never sees it (Mode B's proxy.ts does this for you). Pass // THESE cleaned headers into your pipeline — don't reuse the raw request. const headers = new Headers(request.headers); headers.delete("x-deploy-gate-bypass"); const cleaned = new NextRequest(request.nextUrl, { headers, method: request.method, body: request.body, duplex: "half", // required whenever a (stream) body is forwarded, else POSTs throw });

const response = await yourExistingLogic(cleaned); const secure = request.nextUrl.protocol === "https:"; return gate.setCookie ? withUnlockCookie(response, gate.setCookie, secure) : response; } ```

(If your host logic doesn't take a request argument — it reads globals or NextResponse.next()s — set the cleaned headers on the continue-response instead: NextResponse.next({ request: { headers } }).)

The setCookie path matters: a header-bypassed request must CONTINUE through the host pipeline (i18n redirects, rewrites, analytics cookies) — returning a bare pass-through from the gate would skip all of it (caught by review on the piaf-web install).

  1. Check the host matcher: it must not exclude /__deploy-unlock, and if it

excludes /api (piaf-web's did), decide deliberately — un-gated API routes on previews are usually a hole. Include /api in the matcher and skip only the host's page-routing logic for API paths.

  1. Do NOT wire up the removal script — the host middleware must ship to

production for its own duties.

Mode B — app has NO middleware (strict zero prod cost)

The gate file is the app's real, checked-in proxy.ts; a build step strips it from production builds only:

  1. Copy [templates/deploy-gate.ts](./templates/deploy-gate.ts) to proxy.ts

at the same level as your app/pages directory — the project root, or src/proxy.ts if the app uses a src/ directory. ⚠️ Next only loads the proxy at that level: a root proxy.ts in a src/-layout app is silently ignored, and every preview then deploys ungated even with the password set — the worst failure mode, because nothing errors. (It's checked in — it IS the middleware; lintable, typecheckable.) If the app customises pageExtensions (e.g. .page.ts), Next expects the proxy named to match — proxy.page.ts — per Next's proxy docs; a plain proxy.ts is ignored (same silent-ungate). Name the file accordingly and point the removal script's CANDIDATES at it.

  1. Copy [templates/remove-proxy-on-prod.mjs](./templates/remove-proxy-on-prod.mjs)

to `scripts/remove-

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.