Todos os artigos
ViteHMRlocal developmentlocalhost tunneling

Vite + túnel: HMR que realmente funciona

Tunelar um servidor de desenvolvimento do Vite funciona bem — exceto por duas coisas que vão te derrubar na primeira vez. A página carrega mas o HMR não. Ou a página não carrega de jeito nenhum porque o Vite recusa o hostname do seu túnel. Ambas são uma linha de config quando você sabe onde olhar.

Problema 1: "Blocked request. This host is not allowed."

O Vite 5+ adicionou por padrão uma proteção de checagem de host. Se você abre a URL do seu túnel e vê Blocked request. This host is not allowed, é essa checagem recusando seu hostname *.portpreview.dev.

Correção no vite.config.ts:

export default defineConfig({
  server: {
    host: true,                            // listen on 0.0.0.0
    allowedHosts: ['.portpreview.dev'],   // accept any subdomain
  },
});

O ponto inicial transforma isso em uma correspondência por sufixo. Você pode colocar ali seu hostname de túnel específico se preferir ser estrito, mas para o dia a dia o sufixo é conveniente — as URLs de sessão do túnel rotacionam.

Problema 2: o HMR carrega e depois morre em silêncio

O HMR roda sobre WebSocket. Quando o Vite serve em localhost:5173 mas seu navegador carrega de https://abc123.portpreview.dev, o cliente HMR tenta se conectar à URL que o servidor informou — a local. O túnel não a encaminha, então o WebSocket falha e o HMR para em silêncio.

Corrija a config do HMR:

export default defineConfig({
  server: {
    host: true,
    allowedHosts: ['.portpreview.dev'],
    hmr: {
      clientPort: 443,            // browser connects on 443 (HTTPS)
      protocol: 'wss',            // secure WebSocket through the tunnel
    },
  },
});

Agora o cliente HMR se conecta a wss://abc123.portpreview.dev (porta 443), o túnel encaminha o upgrade e as edições se propagam como esperado. Se você só precisa de recarga estática e não liga para HMR pelo túnel, pode pular isto — mas vai usar muito cmd-R.

Por que você ia querer tunelar o Vite

Três razões reais:

  • Testes em mobile. Abra a URL num celular, veja o mesmo build com HMR. Melhor que compartilhar tela ou emuladores por dispositivo. Veja testes em mobile com um túnel para o padrão completo.
  • Compartilhar trabalho em andamento. Mande um link para um designer ou PM, eles veem sua branch ao vivo. Sem etapa de deploy.
  • Fluxos de OAuth e webhooks que precisam de HTTPS. Frontends servidos pelo Vite que falam com Stripe (só redirect), Auth0 ou similares. mkcert vs. túnel cobre a escolha.

SvelteKit, Astro, SolidStart usam Vite

As configs acima valem para qualquer framework que entrega o Vite como servidor de desenvolvimento. O vite.config.js do SvelteKit tem o mesmo formato. O Astro tem o próprio astro.config.mjs com um subobjeto vite — coloque as mesmas opções de server ali. SolidStart, Nuxt 3 (via Vite) e Qwik City: o mesmo padrão.

Builds de produção são outra história

Tudo acima é config do servidor de desenvolvimento. Quando você roda vite preview ou serve um bundle compilado, nada disso importa porque não há HMR nem middleware de checagem de host. O túnel está apenas encaminhando arquivos estáticos nesse ponto.

Algumas coisas menores

  • CORS de host estrito. Se seu frontend acessa uma API em outra origem (outra porta de localhost, ou um túnel de API separado), configure CORS no lado da API. O Vite não faz proxy por padrão a menos que você diga via server.proxy.
  • Apps com muito WebSocket. O HMR do Vite é um WebSocket. Se seu app usa outro WebSocket para estado de jogo, chat ou dados ao vivo, esse é separado e também precisa ser configurado no seu código cliente para usar a URL do túnel.
  • URL do túnel em arquivos env. Colocamos a URL do túnel ativo no .env.local como VITE_PUBLIC_URL quando o frontend precisa saber o próprio endereço público. import.meta.env.VITE_PUBLIC_URL a lê no cliente.

Passo a passo

  1. Adicione o bloco server.host, allowedHosts e hmr à sua config do Vite.
  2. Reinicie o servidor de desenvolvimento.
  3. Execute npx portpreview 5173 (ou a porta que o Vite usa).
  4. Abra a URL HTTPS no navegador ou num celular.
  5. Edite um componente. Confirme que o HMR atualiza a página sem recarga completa.

Se o HMR não atualizar, abra o console do navegador — o Vite registra a tentativa de conexão WebSocket e você verá a URL que ele tentou. Isso diz se a mudança de hmr.clientPort/protocol surtiu efeito.

Entre na lista de espera do PortPreview para túneis que preservam upgrades de WebSocket por padrão.

Perguntas frequentes

Por que o Vite bloqueia minha URL de túnel com "host is not allowed"?
O Vite 5+ adicionou uma checagem de host que recusa hostnames desconhecidos por padrão. Adicione allowedHosts: ['.portpreview.dev'] (ou seu domínio de túnel) à config de server para aceitar requisições do túnel.
Como faço o HMR do Vite funcionar através de um túnel?
Defina hmr.clientPort: 443 e hmr.protocol: 'wss' no vite.config.ts. Isso diz ao cliente HMR para conectar pelo HTTPS/WSS do túnel em vez de tentar alcançar localhost diretamente, o que falha de fora da sua máquina.
Isso funciona para SvelteKit, Astro e Nuxt?
Sim. Qualquer framework que use o Vite como servidor de desenvolvimento adota a mesma config de server.host, allowedHosts e hmr. O Astro a aninha sob vite no astro.config.mjs; os outros a colocam no arquivo de config do Vite normal.