How to expose localhost:4200 for client previews
ngsrv creates a public preview link for your local service. Port 4200 is the Angular CLI default — every ng serve lands there — which makes it the port most agencies and freelancers want to share with clients during a sprint.
ngsrv helps developers expose local services, share preview links, test webhooks, and get feedback faster. This post is the short version for Angular teams.
TL;DR
# Already running ng serve? Open a second terminal:
ngsrv http 4200 --subdomain client-preview
# -> https://client-preview.tnl.ngsrv.com
Send that URL to your client. They open a normal HTTPS link in their browser, see your local Angular app, and leave comments while you keep coding.
Why port 4200
Angular CLI (ng serve) listens on 4200 by default. So does Storybook in some Angular setups. The same workflow works for any other port — ng serve --port 5000 becomes ngsrv http 5000 — but 4200 is what most teams hit first.
Step 1 — Have ng serve running
ng serve
# ✔ Compiled successfully.
# ** Angular Live Development Server is listening on localhost:4200 **
Leave it running.
Step 2 — Install and authenticate ngsrv
# macOS
brew install ngsrv/tap/ngsrv
# Linux
curl -fsSL https://get.ngsrv.com | bash
# Windows
irm https://get.ngsrv.com/windows | iex
Then, with the token from your dashboard:
ngsrv token <YOUR_TOKEN>
Step 3 — Tunnel with a reserved subdomain
ngsrv http 4200 --subdomain client-preview
You get back:
forwarding https://client-preview.tnl.ngsrv.com -> http://localhost:4200
Reserved subdomains stay the same between restarts on Pro and above, so the link you sent on Monday still works on Friday.
Step 4 — Tell Angular about the new hostname
Angular CLI rejects requests with a different Host: header by default. You'll see:
Invalid Host header
Fix: pass the disable flag:
ng serve --disable-host-check
Or in angular.json, under the serve options for your project:
{
"serve": {
"options": {
"disableHostCheck": true
}
}
}
Don't ship that to production — it's a dev-only setting and ngsrv adds its own auth at the edge.
If you're using Angular dev-server with WebSocket HMR (which is the default), enable it through ngsrv by not blocking WS in any of your security policies. ngsrv proxies WebSockets natively.
Step 5 — Lock the preview down
Two cheap protections for client previews:
Header token. A simple per-client secret in the URL:
tunnels:
- name: preview
port: 4200
subdomain: client-preview
security_policies:
- type: header_required
header: "X-Preview-Token"
value: "kj4f-rt9z-p1ld"
Wrap it in a short link so the client only sees https://link.your-agency.com/preview and never touches the raw token.
IP allowlist. For clients with a fixed office IP range:
security_policies:
- type: ip_allowlist
ips: ["203.0.113.0/24"]
Both policies are on the free tier.
What your client experiences
A regular HTTPS site. The ngsrv edge handles TLS so the padlock is real. There's no warning interstitial, no "free tier" watermark, no ngsrv branding overlay. It's just your app.
When to switch to a custom domain
*.tnl.ngsrv.com is fine for internal use. When a URL becomes part of the deliverable for a client engagement, swap in your own domain:
tunnels:
- name: preview
port: 4200
domain: preview.your-agency.com
See /docs/cli/domains for the DNS setup. Custom domains are included on Pro.