Horizon

Share a Next.js dev server

Share a Next.js dev server (next dev) through a Horizon tunnel, with the allowedDevOrigins setting that stops the blocked cross-origin warning.

Open your next dev server from a phone, a teammate's laptop or a webhook sender, with an HTTPS URL that reaches your machine.

Before you begin

  • Node.js 18 or later
  • A Horizon account and the CLI (see Getting started)
  • A reserved subdomain for -s. Reserve one on the Subdomains page.
  • A Next.js app that runs with next dev

Allow the tunnel origin

Next.js blocks cross-origin requests to dev-only assets and endpoints by default. A browser on https://my-app.hrzn.run sends that hostname as its Origin, so Next.js doesn't recognize it. Add the hostname to allowedDevOrigins.

next.config.js
module.exports = {
  allowedDevOrigins: ["my-app.hrzn.run"],
}

Write the hostname only. Next.js ignores the scheme, port and path, so leave out https://. Restart next dev after you edit the file.

Start your app

Next.js listens on port 3000 by default.

npm run dev

Start a tunnel

Use -s with a subdomain you reserved. Reserved subdomains are a paid feature, see Pricing.

hrzn tunnel http://localhost:3000 -s my-app
Output
HORIZON: Tunnel connected
  URL          https://my-app.hrzn.run (reserved)
  Forwarding   http://localhost:3000
  Request log  https://hrzn.run/dashboard/tunnels/my-app

Keep this terminal open.

Open the URL

Open https://my-app.hrzn.run in a browser. A first-time browser visitor sees the Before you continue page. Select Continue to site.

Check it works

The page loads and the Horizon terminal prints one line per request:

Output
  GET     200  /

Edit a page file and save. The browser updates without a reload. WebSockets pass through the tunnel, and Next.js uses one for this.

Server Actions

Horizon sets Host to localhost:3000. The browser sends Origin: https://my-app.hrzn.run. Next.js compares the host in Origin with the app's host, which it takes from x-forwarded-host first and from host second. Horizon forwards the public host in X-Forwarded-Host, so the two match and you need no extra setting.

If an action fails, see Troubleshooting.

Troubleshooting

Blocked cross-origin request to Next.js dev resource

The full message starts with Blocked cross-origin request to Next.js dev resource /_next/... from "my-app.hrzn.run". The tunnel hostname isn't in allowedDevOrigins. Add it as shown above and restart next dev. The browser then gets a 403 with the body Unauthorized for those requests until you do.

x-forwarded-host header with value ... does not match origin header

Next.js prints `x-forwarded-host` header with value `...` does not match `origin` header with value `...` from a forwarded Server Actions request. Aborting the action. The two hosts differ, so Next.js stops the action. Compare the two values in the message. If the origin value is my-app.hrzn.run, add it to serverActions.allowedOrigins:

next.config.js
module.exports = {
  allowedDevOrigins: ["my-app.hrzn.run"],
  experimental: {
    serverActions: {
      allowedOrigins: ["my-app.hrzn.run"],
    },
  },
}

x-forwarded-host or host headers are not provided

Next.js prints this when a Server Actions request has neither header. Horizon sends both, so look for another proxy between the browser and your app.

The URL changed after a restart

You started the tunnel without -s, so Horizon gave you a new random subdomain. Restart with -s my-app, then update allowedDevOrigins if the hostname differs.

Next steps

On this page