---
title: "Vite \"Blocked request\" on ts.net: cause and fix"
description: "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."
url: "https://tslink.md/docs/vite-blocked-request-ts-net"
locale: "en"
product_version: "0.2.0"
source: "https://github.com/anydoor7/tslink/blob/v0.2.0/docs/agent-quickstart.md"
---

> Documentation index: https://tslink.md/llms.txt · Installed binary is authoritative: `tslink manifest`.

If Vite shows this on a Tailscale address:

```text
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`](https://vite.dev/config/server-options) 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](https://github.com/vercel-labs/portless/issues/310) shows the error, and a pull request in [jhin](https://github.com/Astroreen/jhin/pull/23) 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`](https://vite.dev/config/preview-options), which defaults to the `server.allowedHosts` value.

## Fix 2: open the dev server through a TSLink address

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](https://nextjs.org/docs/app/api-reference/config/next-config-js/allowedDevOrigins) 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](https://github.com/jonshern/Scouthosting/pull/131) and [kingdom-sites pull request 23](https://github.com/KingdomSitesOrg/kingdom-sites/pull/23).

## When not to use TSLink

* **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](https://tslink.md/docs/several-apps-one-machine.md) 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.

## Related pages

* [Use your coding agent's web UI from your phone](https://tslink.md/docs/agent-ui-phone.md): the full phone flow, including enrollment, URL retrieval and cleanup.
* [Let your coding agent share a private preview](https://tslink.md/docs/agent-integrations.md): connect Claude Code, Codex, Cursor or Gemini CLI so the agent runs these commands itself.
* [Several apps on one machine, one ts.net name each](https://tslink.md/docs/several-apps-one-machine.md): one name per dev server.
* [tailscale serve --set-path broke my app?](https://tslink.md/docs/serve-set-path-breaks-app.md): the other common failure of a path mount.

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](https://github.com/anydoor7/tslink), **not made or endorsed by Tailscale**.
