Before a pull request merges, the reviewer should already have clicked through the change at its own address. That is the short answer to what are preview deployments: a temporary copy of the app, built from one branch, at its own link. Vercel, Netlify and Cloudflare Pages create them for you. Deciding who can open that link, and keeping production keys and data out of it, is your job.

What are preview deployments: a temporary copy of the app for one pull request

Preview deployments are temporary copies of an app that the host builds for each pull request. Each gets its own URL and updates with every new commit, so a reviewer or client can try a change before release. Some hosts delete a preview when the pull request closes; others keep it longer. A preview environment is the configuration they share.

Previews are one step in the release path I describe in DevOps for startups. They sit after the automated checks on a pull request have run (CI/CD best practices) and before the merge rules let the change into the main branch (GitHub branch protection). The host does part of the work and leaves the rest to you:

Part of a previewWhere it comes fromWho sets it
The codeThe latest commit on the branch or pull request, built by the hostThe host
The URLGenerated per deployment; Vercel’s links typically appear in pull request comments or the Vercel dashboard, and Netlify comments with it when deploy notifications are onThe host
Environment variablesWhatever the preview scope holdsYou
The databaseWhatever database address the preview scope points toYou
Third-party keys (payments, email, auth, model provider)Whatever the preview scope holdsYou
LifetimeThe host’s teardown or retention ruleThe host, sometimes with a setting you choose

Read the right-hand column and the split is plain: the host supplies the code, the address and the clock, and everything the preview can reach comes from the values you give it.

Two things share the name and are something else. A staging environment is one long-lived copy that many changes pass through, while a preview lives and dies with one pull request. The preview pane inside an AI builder is an editor feature, and when that one breaks, the page to read is when the Lovable preview is not working.

In the Production Hardening Sprint, deliverable 7.7 is to provide browser-accessible preview deployments for pull requests with isolated test configuration.

What goes wrong without it

When reviewers cannot see a change before merge, the review is a diff and nothing more, and one of these four situations follows:

SituationWhat happensWho finds out
No previews at allReview means reading a diff; layout and flow bugs passA customer, the first person to see the change running
An agency with no previewsThe client approves a description and first sees the result on the live siteThe client, so the revision round happens in production
Previews that carry production’s keysA reviewer’s test sign-up, email or checkout reaches live systemsWhoever reads the live inbox, database or payment dashboard
Previews anyone can openUnfinished features and test accounts sit on a URL anyone who has it can loadAnyone the link reaches, and search engines unless a noindex header is sent

The third row is the one that looks fine from outside, and I’ve written up how to catch a preview URL pointed at production in a couple of minutes.

At least 17 of the 21 third-party apps had no deploy gate, and so did all 5 founder apps: every push ships straight to production with nothing checking it first. Those 21 are the third-party apps I audited in June and July 2026: 11 public vibe-coded apps audited across all 12 pillars, and 10 held-out apps the engine had never seen, audited blind. Because I picked them, these counts describe those apps, not AI-built apps in general. In the same audits, the Deployment & Operations pillar averages 37.0 out of 100 across those 21 apps, scored on all 21.

How to set up PR preview environments on Vercel, Netlify, Cloudflare Pages, Render and Railway

PR preview environments take 6 steps on any host: connect the repository, create a preview scope for variables, give previews a non-production database, restrict who can open the URL, make the preview’s checks required before merge, and tear previews down when the pull request closes.

Every cell in the table below comes from each host’s documentation, read on 4 October 2026, not from set-ups run for this page.

HostHow previews are turned onWho can open them, and how to restrict itTeardownSource (read 2026-10-04)
VercelOn by default once the project’s first production deployment exists: a push to a non-production branch, a pull request, or a CLI deploy without --prodVercel Authentication is on by default for all deployments except the most recent production deployment, and a team can set its own default for new projects; without deployment protection, anyone with the link can view them. Vercel Authentication with Standard Protection is available on all plans including Hobby; Password Protection is Enterprise or a paid Pro add-onKept for the retention period: by default 30 days on Hobby and 180 days on Pro and Enterprise for pre-production deployments; the latest preview of an open branch is keptVercel’s environments documentation
NetlifyBuilt automatically when a pull request opens in a connected repository against the production branch or a branch with branch deploys enabledDefault visibility not stated on the Deploy Previews page; a password can be required for all Deploy PreviewsAvailable until the deploy is deleted, automatically or manuallyNetlify’s Deploy Previews documentation
Cloudflare PagesA new pull request on the connected GitHub repository gets a unique preview URL, for pull requests from the repository itselfPublic by default; a Cloudflare Access policy restricts previews to your Cloudflare account and the people your policy namesOld previews are deleted with wrangler pages deployment delete; the latest deployment for a branch cannot be deletedCloudflare Pages preview deployments
Renderpreviews.generation set to manual or automatic in the Blueprint file; needs a Pro workspace plan or higherNot stated in Render’s docsDestroyed when the pull request is merged or closed; optional previews.expireAfterDays, no expiry by defaultRender’s Preview Environments
RailwayPR environments switched on under Project Settings, Environments tabNot stated in Railway’s docs (its Environment RBAC, on Enterprise, covers members’ access to resources)Deleted as soon as the pull request is merged or closedRailway’s environments guide

Across the table, the Git-connected front-end hosts start building previews once the repository is connected, while the container hosts, Render and Railway, need preview or PR environments switched on and run real services for as long as each one exists. Render’s plan condition sits in its row; Railway’s page names no plan for PR environments. A self-hosted app gets the same result from a CI job that deploys each pull request to its own subdomain, and Dokploy and Coolify are self-hosted platforms whose docs describe preview deployments for pull requests. Which host to run on is a separate decision, and I compare them under choosing a deployment target.

The generic recipe, whatever the host

  1. 01 Connect the repository and turn on deploys for pull requests
  2. 02 Create a preview scope for environment variables and fill it with test values only
  3. 03 Give previews a database that is not production
  4. 04 Decide who can open the preview URL, and make sure previews send a noindex header
  5. 05 Make the preview's checks required before merge in your branch protection
  6. 06 Set teardown so previews and their database branches go when the pull request closes, or on the host's retention rule

Step 5 has a plan condition on GitHub: protected branches and rulesets work on private repositories only with GitHub Pro, Team or Enterprise, and on GitHub Free only for public ones, so on a free private repository the preview’s check reports its result but cannot be made required. Steps 2 and 3 are the next section, step 4 the one after it, and step 6 the last.

Pull requests opened by bots can get previews too, which is how a dependency upgrade gets looked at before it merges. Railway has a separate Enable Bot PR Environments toggle for pull requests opened by GitHub bots, Dependabot and Renovate included. If you are still deciding what Renovate bot is good for, plan for its pull requests to need a preview like any other.

Isolated config for preview builds: variables, keys and the database

Isolated config for preview builds means 7 values differ from production: the database URL, its admin key, payment keys in test mode, the email sandbox, a capped model-provider key, the auth redirect addresses, and the analytics environment. A preview build must never receive a production secret, because the branch’s code can read and print whatever the build is given.

What must differThe preview valueWhat can happen if it does not
Database URLA database branch per preview where the provider offers one, or one shared preview database filled with seed data; never productionReviewers’ test records land in production tables
Database service or admin keyThe key for that preview database onlyThe branch’s code holds a key to every production row
Payment keysTest-mode keys (sk_test_, pk_test_)A reviewer’s checkout runs against the live account
Email providerThe provider’s sandbox or a catch-all inboxTest sign-ups send real email to real addresses
Model provider keyA separate key with its own low spend cap (my choice)Preview traffic spends production credits
Auth redirect addressesThe preview URL pattern on the allow list, with email templates using {{ .RedirectTo }} where a redirect is passedSign-in and confirmation links land on production
Analytics and error trackingThe environment set to previewTest traffic shows up in production numbers

The database row decides the most. Neon’s docs say to “Create one branch for each pull request or preview deployment”, and a Postgres branch there starts with its parent’s data as of the moment it is created, so branch previews from a parent that holds seed data, never real customer records. Supabase’s preview branches are “Data-less by default”, and with the GitHub integration they can start with data from a seed file; its pricing page lists Branching as “Not included in free”. Whether to use branches or a second project is a separate choice between two Supabase projects or Supabase Branching, and filling either one is the job of realistic fake data for staging. The sources: Neon’s branching documentation and Supabase Branching.

For payments, test mode and live mode are separate worlds with separate keys, and the move between them belongs to the payment go-live checklist. The auth row is the easiest to forget, because nothing fails until someone clicks an email link. Supabase’s redirect URL documentation says wildcard patterns can be used “to support preview URLs from providers like Netlify and Vercel”, gives https://*-<team-or-account-slug>.vercel.app/** and https://**--my_org.netlify.app/** as the patterns, and recommends the exact path for the production site URL. The same page notes that email templates may need {{ .RedirectTo }} in place of {{ .SiteURL }} when a redirect is passed.

Here is how that row fails in practice. An agency sends a client the preview link for a pull request that changes the sign-up screen, and the preview has its own database and test keys. The client signs up on the preview, but the confirmation email’s link is built from the auth provider’s Site URL, which is set to the production address, so the click lands on the live site and the rest of the review happens on code that does not have the change. A preview is reviewable end to end only when every outside service knows its address, and the auth addresses belong in its configuration as much as its keys do.

The rule about production secrets at the top of this section is my own working rule, and it covers every row of the table, the database key included. How each host scopes and applies variables is set out host by host in environment variables explained, and the risk side, what a leaked or shared value exposes, is in environment variables as a security risk.

Preview environments on your host, and who is allowed to see them

A preview URL has 3 audiences: the team signs in through the host, a client gets a shareable link or a password without joining the team, and everyone else, crawlers included, gets a wall and a noindex header. A preview anyone can open is a second, unguarded copy of the app.

AudienceHow they get inWhat they must not see (my list)
The teamThe host loginProduction secrets, which stay out of the preview scope for everyone
The client or an outside reviewerA shareable link or a password, without joining the host teamOther clients’ previews, the host dashboard, real customer data
Everyone else, crawlers includedNothing: a wall, plus a noindex header for search enginesAny of it

The client row needs the most care on an agency project. On Vercel, a sharable link with protection on “bypasses deployment protection” for whoever has it, and an external user you share a preview with is not added to your Vercel team. Vercel’s guide to sharing a preview deployment adds that “Hobby users are limited to one collaborator at any one time”, and the Hobby plan “restricts users to non-commercial, personal use only”, so a client project belongs on a paid team. Netlify’s password options by plan are covered under who can open a Netlify Deploy Preview. Cloudflare Pages restricts previews through a Cloudflare Access policy, and Render’s and Railway’s docs state no access control for a preview URL (the master table above).

Crawlers are the other half. Vercel adds X-Robots-Tag: noindex to every preview deployment by default but leaves it off when a custom domain is assigned to a non-production branch, and Cloudflare Pages sends the same header on every preview deployment by default. Render sets the header on its single-service pull request previews, while Netlify’s Deploy Previews page, Render’s Preview Environments page and Railway’s page do not state one. Vercel’s own note is the one to remember: the header “only asks search engines not to index the deployment. Anyone with the URL can still open it.” I’d add that the production robots.txt is not the control for a preview either, since each preview serves whatever its own branch ships.

Telling which Vercel environment a URL belongs to and Vercel’s Standard Protection are both written up for the Vercel preview environment in preview vs production on Vercel.

Teardown and what previews cost

The master table gives each host’s rule. In short, Render and Railway clean up when the pull request closes, while Vercel, Netlify and Cloudflare Pages keep previews until a retention rule or a deletion removes them. Supabase deletes preview branches automatically when a pull request is merged or closed.

Container previews and database branches bill while they exist: Render bills preview resources “the same as other Render services”, prorated by the second, and Supabase charges a preview branch for the usage it incurs, outside the Spend Cap. I delete them on close and run a sweep about once a week for anything left behind. A preview per pull request also multiplies addresses, so I point third-party webhooks at staging, never at previews. What a preview and a database branch cost in dollars is in the staging article linked under the four situations above, and Vercel’s own prices are in Vercel pricing by plan.

How to verify it

A preview is verified with one test pull request and 8 checks: configuration first, then the database, payments, email and sign-in, then access and teardown. To verify a preview uses test data only, nothing it writes may appear in production, and nothing a stranger opens may load without a wall.

The order matters: check configuration before anything writes, the same order the preview-versus-production article sets for Vercel. The checks below are host-neutral, and each one names the evidence to keep.

  1. 01 Open a test pull request that changes one visible string. Expect a preview URL on the pull request or in the host's dashboard, the string on the preview and not on production. Evidence: the pull request link and both screenshots
  2. 02 Print the names, never the values, of the variables the preview build received and compare them with the preview scope. Any production-only name is a fail. Evidence: the dated name list
  3. 03 Run the two-minute database check from the staging article: compare the database values first, then write one throwaway record. Evidence: its output
  4. 04 Before any checkout, confirm the payment key the preview received starts with sk_test_ or pk_test_, then run a test-card checkout and confirm it appears in the provider's test data only. Evidence: a screenshot of the test-mode payment
  5. 05 Trigger one transactional email and confirm it lands in the sandbox or catch-all inbox. Evidence: the message in the sandbox
  6. 06 Sign up or sign in on the preview and confirm the confirmation or sign-in link returns to the preview's own address, not the production site. Evidence: the address bar after the redirect
  7. 07 Open the preview URL in a private window and expect a login or password wall, then check the response headers for noindex. Evidence: the wall screenshot and the header line
  8. 08 Close the pull request and confirm the preview and its database branch are gone, or that the host's retention rule or your own deletion removes them. Evidence: the host's deployment list and the database branch list

The key prefixes in check 4 are the ones in Stripe’s API keys documentation: sandbox keys start with pk_test_, rk_test_ and sk_test_, live keys with pk_live_, rk_live_ and sk_live_. On a host whose docs state neither a preview access control nor a noindex header, as Railway’s and Render’s Preview Environments page do not, the wall and the header in check 7 have to come from the app itself, and I’d switch them on when the environment is a preview.

In the sprint, deliverable 7.7 is verified this way: open a preview from a test pull request and verify its environment isolation. If a bad change merges anyway, the way back starts with what a rollback plan is and having one written before you need it.

Where the sprint does this

Deliverable 7.7 is the preview set-up and its isolation check described above. Deliverable 13.1, the production readiness report, delivers the result for every scope item, the work completed, and its verification evidence, and it is verified this way: account for all 123 IDs; keep failures visible until resolved and explain genuine non-applicable items. Your app’s current framework and hosting setup are our starting point; we refactor or replace components where the production work requires it. Hosting, paid tools, and API usage remain in your accounts. Every item is listed in the published scope.

Common questions about preview deployments and preview environments

How do I turn off preview deployments in Vercel?

Set git.deploymentEnabled in vercel.json. A branch map such as "deploymentEnabled": { "dev": false } stops automatic deployments for that branch, and "deploymentEnabled": false turns off automatic deployments for all branches, production included; the default is true. The options are in Vercel’s Git configuration. Often the better question is who can open the previews, not whether they exist.

How do I deploy a preview with Netlify?

Open a pull request in a connected repository, against the production branch or a branch with branch deploys enabled, and Netlify builds the Deploy Preview automatically at a deploy-preview-<number>--<site>.netlify.app address. The preview reads its variables from the Deploy Previews context, one of the deploy contexts Netlify stores values for, so test keys go there.

What is a preview server?

A preview server is either a local server that serves the production build of your app for a last look before you push, or Netlify’s Preview Server deploy context, and neither is a preview deployment. Netlify’s environment variables overview describes that context as the values “for any Preview Servers running for that site”, inheriting the Local development values by default.

What are the types of environments in testing?

The four you will meet in a small web app’s release path are local, preview, staging and production. Local is your own machine. Preview is one temporary copy per pull request. Staging is one long-lived copy that mirrors production’s set-up with test data. Production is what customers use.