Tunelowanie serwera deweloperskiego Vite działa dobrze — poza dwiema rzeczami, które podłożą ci nogę za pierwszym razem. Strona się ładuje, ale HMR nie. Albo strona w ogóle się nie ładuje, bo Vite odrzuca nazwę hosta tunelu. Oba to jednolinijkowe poprawki konfiguracji, gdy wiesz, gdzie patrzeć.
Problem 1: „Blocked request. This host is not allowed."
Vite 5+ dodał domyślnie ochronę host-check. Jeśli otworzysz adres tunelu i zobaczysz Blocked request. This host is not allowed, to ta kontrola odrzuca twoją nazwę hosta *.portpreview.dev.
Poprawka w vite.config.ts:
export default defineConfig({
server: {
host: true, // listen on 0.0.0.0
allowedHosts: ['.portpreview.dev'], // accept any subdomain
},
});
Wiodąca kropka czyni z tego dopasowanie po sufiksie. Możesz wpisać tam konkretną nazwę hosta tunelu, jeśli wolisz być rygorystyczny, ale na co dzień sufiks jest wygodny — adresy sesji tunelu się zmieniają.
Problem 2: HMR się ładuje, potem cicho umiera
HMR działa po WebSocket. Gdy Vite serwuje pod localhost:5173, ale przeglądarka ładuje z https://abc123.portpreview.dev, klient HMR próbuje połączyć się z adresem, który podał mu serwer — lokalnym. Tunel tego nie przekazuje, więc WebSocket zawodzi i HMR cicho się zatrzymuje.
Popraw konfigurację 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
},
},
});
Teraz klient HMR łączy się z wss://abc123.portpreview.dev (port 443), tunel przekazuje upgrade, a edycje propagują się zgodnie z oczekiwaniami. Jeśli potrzebujesz tylko statycznego przeładowania i nie zależy ci na HMR przez tunel, możesz to pominąć — ale będziesz dużo używać cmd-R.
Dlaczego w ogóle tunelować Vite
Trzy realne powody:
- Testy mobilne. Otwórz adres na telefonie, zobacz ten sam build z HMR. Lepsze niż współdzielenie ekranu czy emulatory na urządzenie. Zobacz testy mobilne z tunelem, by poznać pełny wzorzec.
- Udostępnianie pracy w toku. Wyślij link projektantowi lub PM-owi, zobaczą twoją gałąź na żywo. Bez kroku wdrożenia.
- Przepływy OAuth i webhooków wymagające HTTPS. Frontendy serwowane przez Vite, które rozmawiają ze Stripe (tylko redirect), Auth0 itp. mkcert vs. tunel omawia wybór.
SvelteKit, Astro, SolidStart używają Vite
Powyższe konfiguracje dotyczą każdego frameworka, który dostarcza Vite jako serwer deweloperski. vite.config.js SvelteKita ma ten sam kształt. Astro ma własny astro.config.mjs z podobiektem vite — wstaw tam te same opcje server. SolidStart, Nuxt 3 (przez Vite) i Qwik City: ten sam wzorzec.
Buildy produkcyjne to inna historia
Wszystko powyżej to konfiguracja serwera deweloperskiego. Gdy uruchamiasz vite preview lub serwujesz zbudowany bundle, nic z tego nie ma znaczenia, bo nie ma HMR ani middleware host-check. Tunel przekazuje wtedy tylko statyczne pliki.
Kilka drobiazgów
- Rygorystyczny host-CORS. Jeśli frontend uderza do API na innym origin (inny port localhost lub osobny tunel API), ustaw CORS po stronie API. Vite domyślnie nie proxuje, dopóki nie powiesz mu tego przez
server.proxy. - Aplikacje mocno korzystające z WebSocket. HMR Vite to jeden WebSocket. Jeśli twoja aplikacja używa innego WebSocket do stanu gry, czatu lub danych na żywo, jest on osobny i też musi być skonfigurowany w kodzie klienta na adres tunelu.
- Adres tunelu w plikach env. Aktywny adres tunelu umieszczamy w
.env.localjakoVITE_PUBLIC_URL, gdy frontend musi znać własny publiczny adres.import.meta.env.VITE_PUBLIC_URLodczytuje go w kliencie.
Krok po kroku
- Dodaj blok
server.host,allowedHostsihmrdo konfiguracji Vite. - Zrestartuj serwer deweloperski.
- Wykonaj
npx portpreview 5173(lub port, którego używa Vite). - Otwórz adres HTTPS w przeglądarce lub na telefonie.
- Zedytuj komponent. Potwierdź, że HMR aktualizuje stronę bez pełnego przeładowania.
Jeśli HMR nie aktualizuje, otwórz konsolę przeglądarki — Vite loguje próbę połączenia WebSocket i zobaczysz adres, którego próbował. To mówi, czy zmiana hmr.clientPort/protocol zadziałała.
Dołącz do listy oczekujących PortPreview, by mieć tunele zachowujące upgrade'y WebSocket domyślnie.