Preview Deployments: A Workflow That Actually Helps
Preview deployments are usually explained as “a URL for every pull request.” That description is accurate and almost useless, because it skips the two facts that decide whether the feature helps or hurts you: who can reach that URL, and what search engines do with it.
Both answers are documented, both are the opposite of what most people assume, and one of them is a trap that only shows up if you deploy with the CLI instead of Git.
What Cloudflare Pages actually creates
Every preview deployment gets two addresses, and they behave differently:
| Address | Example | Behaviour |
|---|---|---|
| Immutable deployment URL | 373f31e2.your-project.pages.dev | Unique per deployment, never reused |
| Branch alias | staging.your-project.pages.dev | Always points at the latest deployment for that branch |
Cloudflare’s documentation is explicit that the hash-based address is permanent: it “may always be visited in the future.” The branch alias moves forward with each push.
Branch names are normalised before they become hostnames — lowercased, with non-alphanumeric characters converted to hyphens, so fix/API-Timeouts becomes fix-api-timeouts.your-project.pages.dev. Worth knowing before you name a branch after something you would not want in a URL.
One detail that contradicts the usual assumption: preview deployments are unlimited. Cloudflare documents that you “can have an unlimited number of preview deployments active on your project at a time.” The quota that exists is on builds (500 per month on the free plan, one concurrent), not on stored previews.
Public by default, noindex by default
These are the two defaults that matter, and they pull in opposite directions.
Preview deployments are public. The documentation says it plainly: “By default, preview deployments are enabled and available publicly.” Anyone with the URL can load it. There is no password, no login, no obscurity beyond a random hash.
Preview deployments are also noindexed. Also from the docs: “By default, every preview deployment generated by Cloudflare Pages includes the X-Robots-Tag: noindex HTTP response header.”
That combination is a reasonable design. The URL is unguessable, so casual traffic does not find it; the header means search engines will not index it even if something links to it. For most teams this removes the fear that a half-finished page gets indexed.
But notice what noindex does not do. It does not make the page private. It does not stop a competitor reading your draft. It does not stop a client or a partner from sharing the link. And it says nothing about what is in the deployment — if you deploy from a branch whose build reads a staging API key, that preview is serving real credentials over a public URL.
The comparison, using each vendor’s own terminology
The three big platforms agree on the concept and disagree on the details:
| Cloudflare Pages | Netlify | Vercel | |
|---|---|---|---|
| Preview URL | <hash>.<project>.pages.dev | deploy-preview-42--site.netlify.app | <project>-git-<branch>-<scope>.vercel.app |
| Public by default | Yes | Yes | “publicly accessible by default” |
| noindex on previews | Documented, on by default | Documented for deploy previews and old branch deploys | Not documented |
| Protect previews | Cloudflare Access (project toggle) | Password or team login | Deployment Protection |
Two things stand out. First, Netlify’s preview hostname uses a double hyphen before the site name — a detail that gets misquoted constantly, and it matters if you are writing a matcher or a CSP rule. Second, Vercel’s documentation does not state an equivalent noindex behaviour for previews. Absence of documentation is not proof of absence of the header, but it does mean you should verify it yourself with curl -sI rather than assume parity.
Protecting previews — and the limit of that protection
Cloudflare Access can sit in front of preview deployments, enabled per project. The catch is in the documentation and it is easy to miss: enabling the policy protects preview URLs and, in Cloudflare’s words, “not your *.pages.dev domain or custom domain.”
So the toggle does exactly what it says and nothing more. Your production site was already public; that is not a regression. But if you were hoping to also close off the pages.dev hostname — a very common request, since it duplicates your site on a domain you do not control — that is a separate job.
Netlify and Vercel both offer equivalent protection, with the same shape of caveat: the free tiers give you authentication against your own team, while password protection is a paid feature.
The Direct Upload problem nobody writes about
Most preview deployment advice assumes a Git-connected project, because that is where the feature was designed to live. A pull request exists, the platform attaches a preview to it, a reviewer clicks the link.
If you deploy with wrangler pages deploy ./dist --branch=staging, you get the preview deployment and the branch alias — the documentation confirms this — but there is no pull request for it to attach to. There is no review thread. Nobody is notified. The deployment appears in a list you have to remember to open.
Worse, Cloudflare documents that Direct Upload projects cannot be converted to Git integration later. You are choosing a deployment model, permanently, on day one. The workflow that makes previews valuable is partly the notification and the attached review, and that part comes from Git, not from Pages.
When preview deployments are not worth it
If you are the only person who writes, deploys, and reads the site, previews solve a problem you do not have. Nobody is going to review your branch. The main real use is checking a risky change — a header rule, a redirect map, a layout rewrite — against the live environment before it becomes the live environment, and for that a local npm run dev gets you 90% of the way.
The genuine cases: more than one contributor; a client or stakeholder who needs to see changes; a deploy process that touches configuration you cannot safely test locally.
A workflow that survives a solo schedule
Three habits, none of which require Git:
Name branches after their purpose, not their author. staging and pr-42 produce clean aliases. ds/blog-post-final-v2 produces a URL that is awkward to type and tells you nothing.
Verify the noindex header yourself once. One command, and you will know rather than believe:
curl -sI https://373f31e2.your-project.pages.dev/ | grep -i x-robots-tag
If you need to extend it — say you want noindex, nofollow — _headers does apply to preview hostnames. Cloudflare’s own example rule targets https://:version.:project.pages.dev/* directly.
Delete previews you have finished with. They are unlimited, which means nobody will ever clean them up for you, and an old preview is a snapshot of your site at a moment you were not checking it.
The feature is real. It is also more of a deployment primitive than a review workflow, and it only becomes a workflow once something tells a human to go and look.
Written by TestedHost. Every recommendation on this site comes from running the setup described, on a live deployment — not from a vendor spec sheet. Spotted something out of date? Tell us.