Guide · July 21, 2026 · 10 min read
How to Send a Website to a Client for Review (Without ngrok or a Staging Server)
Almost nobody says this out loud: "send the site for review" is really two different jobs that people jam together, and the right way to do it depends entirely on which one you have.
Job A — the output is static. Plain HTML/CSS/JS, a design mockup, or the built output of a framework: an Astro or Hugo site, a Vite/Next static export, a page an AI generated for you. It doesn't need a server to run. This covers most "hey, can you take a look" moments.
Job B — the thing is a running app. Server-side rendering per request, a database, auth, a live API, websockets, server-side form handling you operate. It genuinely needs a process running somewhere.
Most people reach for a tunnel like ngrok, or spin up a staging server, for both. For Job A that's the wrong tool — you're exposing your laptop to the internet just to serve files that could live on a URL that's always up. Tunnels and staging servers are only unavoidable for Job B, and even then a preview deploy usually beats a tunnel. Sort out which job you have first; the rest of this follows from that.
What's the simplest way to send a website to a client for review?
For a static site or page, ranked by how little work it is:
1. A drag-and-drop static host. Netlify Drop is the reference answer for a multi-file build: drag the built folder into the browser, get an instant HTTPS URL that's always up, no CLI, no server, no tunnel. Cloudflare Pages also supports dashboard drag-and-drop or wrangler pages deploy ./dist, with a generous free tier — worth knowing the ceilings: the dashboard upload allows up to 1,000 files, Wrangler up to 20,000, and any single asset maxes out at 25 MiB. tiiny.host is the consumer-friendly version — upload a folder, zip, or single file and get a link, with password protection on paid plans. One caveat with Netlify Drop: an anonymous deploy is temporary, so claim it into a free account to keep the link alive past the review.
2. A single-page link host. If the deliverable is one self-contained HTML file — an AI-built page, a proposal, a report, a deck exported to HTML — a link host is the least-effort path of all. No build, no folder, no CLI: paste or drop the HTML and get a URL. Purpose-built link hosts (more on one below) add privacy and view tracking. (A GitHub Gist served through htmlpreview works for a throwaway look at your own markup, but it's unbranded and unsuited to real client work — treat it as disposable testing, not a review method.)
3. Git-backed preview deploys. If there's a repo, Vercel, Netlify, and Cloudflare Pages auto-generate a unique preview URL per branch or pull request. This is the best option for an ongoing project, because every push gives the client a fresh reviewable link without you doing anything.
The through-line across all three: the client should get a plain clickable https:// link that opens instantly, works on their phone, and is still alive next Tuesday. Anything that fails one of those — dies when your laptop sleeps, throws a click-through warning, makes them unzip something — is the wrong pick, no matter how quick it felt on your end.
Why are ngrok tunnels and "just email the files" bad for client review?
ngrok is a genuinely good tool for the job it's built for — exposing a local running service to a remote machine or webhook during development. Client review is not that job.
As of mid-2026 ngrok's free tier gives you an assigned dev domain (persistent per account, better than the old random ephemeral URLs), but free HTTP/S endpoints still serve an interstitial warning page — the "You are about to visit…" screen — that's only removed on a paid plan. Check the current numbers on ngrok's pricing page, but the free tier also caps endpoints, monthly requests, and transfer. For client review specifically, that adds up to problems:
- The interstitial spooks non-technical clients. An unbranded security-warning page standing between them and your polished work reads as "is this safe?" before they've seen a pixel.
- It's only alive while your machine and the process are running. Client opens the link Monday morning after you closed the laptop Friday, and it's a dead link. To them, your work looks broken.
- Dev-server-over-tunnel breakage. Dev servers may need host or origin configuration to accept a tunnel domain — Vite exposes
server.host/server.allowedHosts, Next.js uses its own controls likeallowedDevOrigins— and HMR websockets often misbehave through a tunnel. Check your framework's tunnel guidance, or you'll end up debugging your review link instead of collecting feedback. - Caps bite mid-review. An image-heavy site can throttle against the request or bandwidth limit right when the client is clicking around.
"Just email the files" is worse. The client double-clicks index.html, which opens over file://, where ES modules, fetch, many web fonts, and any root-relative path (/assets/…) silently fail. They see a half-rendered page and assume you shipped something broken. Add the rest: no version control ("which zip was the latest?"), attachments bouncing on size limits, no way to open it on a phone, and zero signal on whether they ever looked. It looks amateur because it is.
Static site vs a running app — does the review method change?
Yes, decisively. This is the fork the whole thing hangs on.
Static (Job A) never needs a tunnel or a staging server. Deploy the built output to a static host or a link host and you're done. This includes SPAs and static-site generators — after npm run build, your dist/ / out/ / build/ folder is just static files. A "React app" is usually static the moment it's built. (A static page can even submit a form to Formspree, Basin, or Netlify Forms — that alone doesn't make it Job B.) If you catch yourself about to tunnel a static site, stop; you're doing manual labor for something a drag-and-drop host does for free.
A running app (Job B) won't execute on a static host — it needs a process. Three honest options, best first:
- Preview deploy to a real platform. Pick the platform that already matches your stack — Render or Railway for a long-running server and database, Fly.io for containers, Cloudflare Workers or Vercel/Netlify functions for serverless request handlers. They give you an always-on URL that survives your laptop closing. This is what people mean by "staging" — but verify the details that bite: background-process support, database availability, cold starts, and free-tier limits vary a lot between them, and a serverless function is not a drop-in home for every app.
- A tunnel — ngrok, Cloudflare Tunnel, VS Code port forwarding, Tailscale Funnel — only if the app genuinely can't be deployed yet or has heavy local dependencies. Accept that it's live only while you run it: fine for a scheduled review call, bad for "look whenever you get a sec." Cloudflare's quick tunnels are the honest free replacement for ngrok here — no account needed and no interstitial warning page.
- If interaction isn't essential, record a Loom walking through the app instead of exposing it at all.
There's a common middle ground worth naming: a lot of "apps" are a static frontend talking to a hosted API. Deploy the frontend statically (Job A) pointed at your dev or staging API, and there's no tunnel to manage. Recognizing this collapses most "I need a server" situations straight back to the simple path.
How do you collect the client's feedback on the page?
Match the tool to how technical and how willing the client is. The biggest mistake here is over-tooling a client who was always going to just reply by email anyway.
- Point-and-click visual annotation — comments pinned to elements on the live page. Markup.io has a free tier and lets you comment on any live URL or an upload; it's the lowest-friction one to try. BugHerd is the agency-grade paid upgrade (sidebar widget feeding a kanban board). Marker.io shines if you push issues into Jira, Trello, or Linear. Pastel, Ruttl, Userback, and Atarim round out the category. The strength: they turn a vague "the button feels off" into a comment pinned to the exact button, with browser and OS metadata attached. The cost: the client has to learn a widget, and plenty won't.
- Async video. A Loom walkthrough from you, and the client replies with timestamped comments. This is the lowest cognitive load for a non-technical client, and you get viewing activity for free.
- A reply form on the page. A plain form wired to Formspree or Basin, or even a
mailto:. Zero new logins for the client — they type in the page and send. - Still in Figma? Just use Figma comments. Don't deploy anything.
The honest tradeoff: annotation tools produce the cleanest, most actionable feedback but add friction and cost; a form or a Loom reply gets more clients to actually respond. Sophisticated or repeat clients can handle an annotation tool. For a one-off or a non-technical client, a reply form plus a scheduled call will get you further than the fancier setup.
How do you know if the client actually reviewed it?
This is the most underrated requirement, and it's exactly where plain static hosts leave you guessing.
Netlify, Vercel, and Cloudflare Pages tell you the deploy succeeded — not whether the link was opened. To get even aggregate views you have to bolt on Plausible, Fathom, or Google Analytics yourself, and even then that's page-level analytics, not per-share "the link was opened Tuesday and stayed open four minutes."
And be honest about what any of these signals prove. A recorded open is not proof a specific person looked, that a human looked at all, or that anyone read a word — link scanners and previewers pre-fetch URLs, and links get forwarded. Longer and repeated visits are stronger signals than a single ping, but they're signals, not receipts.
Tools built for tracked sharing still beat flying blind. DocSend does per-page tracking on a link — but it's deck- and PDF-oriented and priced for sales teams. Loom shows viewing activity; whether you get a named viewer depends on the sharing and sign-in settings. BugHerd and Pastel show activity indicators. And don't lean on email read receipts: they're widely stripped or blocked, and Apple Mail Privacy Protection pre-fetches tracking pixels, so an "opened" event there means nothing.
The honest reality: with a plain static host you're flying blind, and that cuts both ways. You assume silence means rejection when the link was simply never opened — or you nag someone whose link already shows two long visits. To get even whether and when the link was opened, and for how long, you need a link built for tracked delivery or you wire up analytics yourself — and either way, read it as the link's activity, not proof of which person was behind it.
The mistakes worth avoiding
A quick checklist of the ways this goes wrong, most of which come straight from picking the wrong tool for the wrong job:
- Sending a tunnel link, then closing the laptop — dead link, looks broken.
- Emailing a zip — the client opens
file://and modules,fetch, and fonts break. - Hardcoded
localhostor root-absolute asset paths that work locally and break once deployed; a mixed-contenthttpasset on anhttpspage gets blocked silently. - Treating an unprotected URL as private. Anyone who receives the link can open and forward it, and
noindexonly asks search engines not to list it — it is not access control. For confidential work, use actual access protection like a password, not an obscure URL. - Tunneling the dev server instead of the build, then debugging host-header and websocket errors live.
- Standing up a full staging server with auth for a one-time passive look.
Where Pagelive fits (and where it doesn't)
If your deliverable is one self-contained HTML page — an AI-built page, a proposal, a report, a deck, a resume, a mockup exported to a single file — Pagelive is a reasonable option: pages are noindex by default, take an optional password enforced at the edge, and carry link-level view data (whether and when the link was opened, how long, opens count, country) without bolting on analytics. Re-publishing the same slug updates the page in place, so the review URL never changes. For confidential work, turn the password on — noindex alone is not access control, and anyone with the link can otherwise open it.
Be clear about where it's the wrong pick. If it's a running app — backend, database, server-side rendering, live API, persistent logins — Pagelive doesn't run apps; use a preview deploy or a Cloudflare Tunnel. If it's a multi-file static build with many routes, separate JS/CSS chunks, and an assets folder, that's Netlify Drop / Cloudflare Pages / tiiny.host territory, not a single page. And the tracking is link-level: it tells you the link was opened, not which person opened it, so if you need per-viewer identity or rich point-and-click annotation, look at DocSend or Markup.io instead. If you genuinely don't care whether anyone opened the link, Netlify Drop and Cloudflare Pages are free and completely fine.
Frequently asked
What's the simplest way to send a website to a client for review? +
For a static site, drop it on a host that gives an instant link — Netlify Drop or Cloudflare Pages — or a private, tracked link if you want to know when they reviewed it. For a running app, use a preview deploy (Vercel, Netlify) or a tunnel while your server runs.
Why are ngrok tunnels bad for client review? +
A tunnel only lives while your machine and the process run — close the laptop and the link dies. Free tiers show an interstitial warning and the URL can change. It's a dev tool for demoing a running server, not a stable link you hand a client for a week.
Does the review method change for a static site vs a running app? +
Yes. A static site (HTML/CSS/JS) can be dropped on any host or a link tool. A running app with a backend needs a real preview deploy or a tunnel while it runs — you can't just upload the files.
How do you collect a client's feedback on a page? +
Purpose-built visual-feedback tools like BugHerd or Markup.io let clients click and comment directly on the page. For lighter needs, a simple reply form on the page or a shared doc works fine.
How do you know if the client actually reviewed the page? +
A plain host won't tell you. A tracked-link tool shows whether and when the page was opened and how long they spent — so you know whether to follow up or keep waiting.
Related: publish from Claude Code · use Claude Code remotely
Send a review link — and know when they looked.
For a static review page, Pagelive gives you a private, tracked link with an optional password and a reply form for feedback. (For a running app, use a preview deploy.) Free for 5 pages, no card.