Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

dev: the Host check on /__nuxt_dev__/* ignores vite.server.allowedHosts, so the error overlay is silently 403'd on hosts-file aliases

Open
#1,620 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
3/5
Estimated time
1-2 days
Newbie friendliness
40/100
Issue type
Bug
Clarity
Mostly clear
Activity status
Active
Tech stack
nodejs, typescript
Domain
cli, security

Research direction

Start in the dev server's #syncAllowedHosts and isAllowedHost logic, plus #rejectDisallowedHost, to see how the host allow-set is built and why vite.server.allowedHosts is never read there. The overlay mount() fetch in the client is the other half: a non-OK response is silently dropped. Done means a Host listed in vite.server.allowedHosts gets a 200 on /__nuxt_dev__/progress, with the security trade-off for true settled first in the thread.

Written by the indexing model from the issue text.

Description

Environment
  • @nuxt/cli 4.0.0 and 4.0.1 (same code path), nuxt 4.6.0 / 4.6.1, vite 8, node 24, macOS, Chrome 155
  • nuxt dev --https --host=0.0.0.0 --port=3000, app opened at a hosts-file alias (https://local.example.test:3000, 127.0.0.1)
  • nuxt.config: vite: { server: { allowedHosts: true } }
What happens
  1. A client error is reported (vue:error, app:error, window error or unhandledrejection -> nuxt:dev:client-error). The server answers with the HMR event nuxt:dev:error carrying /__nuxt_dev__/error-overlay/<id>.
  2. The overlay plugin fetch()es that URL with Host: local.example.test:3000. The CLI answers 403 Forbidden: this host is not allowed. Pass it to --host, or --public to allow any host. from #rejectDisallowedHost (host check, see #1484).
  3. mount() treats a non-OK response as 'no overlay' (res.ok ? res.text() : undefined) and .catch(() => {})s the rest, so nothing is logged. The developer sees a red 403 in the network tab and never sees the error. On https://localhost:3000 the overlay appears.

The same guard also rejects /__nuxt_dev__/error/{events,history,open}, /__nuxt_dev__/progress, and every path while the server is loading or a load failed (#serve), so an overlay injected into SSR HTML cannot subscribe to the channel either.

Why

#syncAllowedHosts only knows the --host value and listener.getURLs(), and isAllowedHost is an exact match on that set. vite.server.allowedHosts is never consulted, although the CLI already writes and merges it in #createListener. A project that declared its dev hostnames (or opted out of Vite's check with true) is still rejected by a second, independent allow-list. The only opt-out, --public, also sets devServer.cors = { origin: '*' }; --host=<alias> changes the bind interface and supports one name.

Proposal
  1. In #syncAllowedHosts, also honor nuxt.options.vite.server.allowedHosts: true skips the check (the effect --public has on the check, nothing else); an array is added to the allow-set, with Vite's leading-dot meaning (.example.com = domain + subdomains) in isAllowedHost.
  2. Optional: NUXT_DEV_ALLOWED_HOSTS (or a flag) so the list is also known before nuxt.config is loaded (the eager bind, the loading page and the load-failure page).
  3. Overlay client: console.warn with the status when the overlay fetch is not OK, so a blocked overlay is not silent.
  4. Longer term: split --public into 'allow any Host' and 'CORS *', or document that allowedHosts does not reach the CLI endpoints.

Security note: the check exists for DNS rebinding. The channel is created with allowedHosts: true (useErrorChannel), so the CLI check is the only rebinding defense for POST /__nuxt_dev__/error/open. An explicit list is therefore the conservative form, true should stay an explicit opt-in, and default behaviour stays unchanged.

Reproduction
  1. Any Nuxt 4.6 app with vite: { server: { allowedHosts: true } } in nuxt.config, started with nuxt dev --https --host=0.0.0.0 --port=3000 (--https.cert/--https.key for a certificate that covers the alias).
  2. curl -sk -o /dev/null -w '%{http_code}\n' -H 'Host: local.example.test:3000' https://127.0.0.1:3000/__nuxt_dev__/progress returns 403, with the body Forbidden: this host is not allowed. Pass it to `--host`, or `--public` to allow any host.
  3. The same request with -H 'Host: localhost:3000' returns 200 (the progress stream).
  4. In a browser on a hosts-file alias (127.0.0.1 local.example.test), trigger any client error. The network tab shows GET /__nuxt_dev__/error-overlay/<id> with 403, the console shows nothing, and no overlay appears. While the server is starting or restarting, document requests from the alias also get the plain-text 403 (no refresh), where localhost gets the self-reloading loading screen.
Patch sketch (about 25 lines; applies to 4.0.0 and 4.0.1)
#syncAllowedHosts(options) {
  const extra = resolveExtraAllowedHosts(this.#currentNuxt?.options.vite?.server?.allowedHosts, process.env.NUXT_DEV_ALLOWED_HOSTS);
  this.#allowAnyHost = !!options.public || extra.any;
  this.#allowedHosts.clear();
  if (this.#allowAnyHost) return;
  /* existing: --host value + listener URLs */
  for (const host of extra.hosts) this.#allowedHosts.add(host);
}

isAllowedHost: after allowedHosts.has(hostname), also accept entries starting with . when hostname === entry.slice(1) || hostname.endsWith(entry).

Happy to send a PR if the direction is acceptable.

Dominant language
TypeScript
Stars
401
Forks
123
Avg merge
1d 1h
Merged PRs (30d)
71

Getting set up

Open in Codespaces

Starts the project's dev container in your browser, under your own GitHub account.

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from nuxt/cli

All issues in nuxt/cli

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.