· NGSRV Team

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.