TSLinkTSLink Docs

Vite "Blocked request" on ts.net: cause and fix

Vite rejects ts.net hosts with Blocked request. This host is not allowed. Add .ts.net to server.allowedHosts, or open the dev server through a TSLink address.

Last updated on

View as Markdown

If Vite shows this on a Tailscale address:

Code
Blocked request. This host ("<device>.<tailnet>.ts.net") is not allowed.
To allow this host, add "<device>.<tailnet>.ts.net" to server.allowedHosts in vite.config.js.

the cause is Vite's Host check: the request arrived with a ts.net Host header that is not in server.allowedHosts, so the dev server refused it. There are two fixes. Either add .ts.net to server.allowedHosts, or reach the dev server through a TSLink address. TSLink rewrites the Host header to the upstream by default, so Vite sees its own address. On 2026-10-22 we tested this with Vite 8.3.5 and did not change the Vite config: the page and the HMR WebSocket both worked. TSLink is an independent project, not made by Tailscale.

Fix 1: allow the host in Vite

Vite's server.allowedHosts lists the hostnames the dev server may respond to. Its default is an empty list, and localhost, domains under .localhost and IP addresses are allowed. A string that starts with a dot allows that hostname and all its subdomains:

js
// vite.config.js
import { defineConfig } from 'vite';

export default defineConfig({
  server: {
    allowedHosts: ['.ts.net'],
  },
});

.ts.net is the entry used in public reports (portless issue 310 shows the error, and a pull request in jhin adds allowedHosts: [".ts.net"]). Vite's documentation says to add only hosts whose resolution you control and warns against broad entries such as top-level domains. A tighter choice is your own tailnet suffix, for example .<tailnet>.ts.net, which allows only names under your own tailnet. If you run vite preview instead of the dev server, set preview.allowedHosts, which defaults to the server.allowedHosts value.

With no edit to vite.config.js, register the dev server (assuming it listens on 5173):

bash
tslink add web --proxy localhost:5173 --allow you@example.com
tslink url web --wait

Replace the login with your Tailscale account, and open the printed https://<name>.<tailnet>.ts.net on your phone or another device on your tailnet. When you finish, run tslink remove web. The first run may print an enrollment link (needs_login) to open once in a browser.

The reason it works is a TSLink default: its HTTP proxy rewrites the Host header to the local target and forwards WebSocket upgrades. The --preserve-host flag changes that, forwarding the node's own external Host. If you pass it, Vite will see the ts.net name again, and you need Fix 1.

What we tested

  • Environment: a fresh npm create vite@latest vanilla app, Vite 8.3.5, listening on 127.0.0.1:5197, TSLink 0.0.0-local.20261020.0149.1+3739bb9b, on 2026-10-22.
  • Without TSLink: a direct request with Host: vite-probe.<tailnet>.ts.net returned 403 with the body Blocked request. This host ("vite-probe.<tailnet>.ts.net") is not allowed.
  • Through TSLink: the app was registered with tslink add vite-probe --proxy localhost:5197 --allow <login> --ephemeral. Over the returned https://vite-probe.<tailnet>.ts.net address, / returned 200 as text/html with no Blocked text, /@vite/client and /src/main.js returned 200, and the HMR WebSocket (Sec-WebSocket-Protocol: vite-hmr) returned 101 Switching Protocols. vite.config was unchanged.
  • Cleanup: tslink remove vite-probe succeeded, the service no longer appeared in the list, and the URL stopped responding.

This covers one Vite version and the vanilla template. It did not cover plugins that add their own checks, other frameworks or a --preserve-host registration.

Next.js has its own check

Next.js has a separate setting, allowedDevOrigins. Its documentation says the dev server already allows localhost, its subdomains and the hostname it was started with, and that any other hostname needs an entry. We have not tested TSLink with Next.js. Assume you still need that entry for your ts.net name:

js
// next.config.js
module.exports = {
  allowedDevOrigins: ['*.<tailnet>.ts.net'],
};

In that syntax * stands in for exactly one label, so this matches web.<tailnet>.ts.net. Public examples of agents adding this line include Scouthosting pull request 131 and kingdom-sites pull request 23.

  • You only need the error gone on one machine. One line in vite.config.js is enough and adds no tool.
  • You want the fix to live in the project. Fix 1 travels with the repository; Fix 2 depends on TSLink's default Host handling.
  • You use Next.js. Plan to add allowedDevOrigins regardless.
  • You need a stable name across hosts or your own domain. See Several apps on one machine for Tailscale Services; TSLink only produces *.ts.net names.
  • The dev server must be reachable without Tailscale. TSLink's private apps need Tailscale on the device that opens them.

Official Tailscale, Vite and Next.js sources linked above: accessed 2026-10-22. TSLink behavior: v0.2.0. TSLink is an independent project by anydoor7, not made or endorsed by Tailscale.

Table of Contents