Усі статті
ViteHMRlocal developmentlocalhost tunneling

Vite + тунель: HMR, що справді працює

Тунелювати dev-сервер Vite загалом просто — окрім двох речей, на яких ви спіткнетеся першого разу. Сторінка завантажується, а HMR — ні. Або сторінка взагалі не завантажується, бо Vite відхиляє хостнейм тунелю. Обидва випадки розв'язуються одним рядком конфігу, коли знаєш, куди дивитися.

Проблема 1: «Blocked request. This host is not allowed.»

У Vite 5+ за замовчуванням додано захист host-check. Якщо ви відкриваєте URL тунелю й бачите Blocked request. This host is not allowed, це перевірка відхиляє ваш хостнейм *.portpreview.dev.

Виправлення в vite.config.ts:

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

Провідна крапка робить це збігом за суфіксом. Можна вказати там конкретний хостнейм тунелю, якщо хочете суворості, але для щоденної розробки суфікс зручний — URL сесій тунелю змінюються.

Проблема 2: HMR завантажується, потім тихо вмирає

HMR працює по WebSocket. Коли Vite роздає на localhost:5173, а браузер завантажує з https://abc123.portpreview.dev, HMR-клієнт намагається під'єднатися до URL, який йому назвав сервер, — локального. Тунель його не пробрасує, тож WebSocket падає, і HMR тихо зупиняється.

Виправте конфіг 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
    },
  },
});

Тепер HMR-клієнт під'єднується до wss://abc123.portpreview.dev (порт 443), тунель пробрасує upgrade, і правки поширюються як очікувано. Якщо потрібне лише статичне перезавантаження й HMR через тунель не важливий, це можна пропустити — але доведеться багато тиснути cmd-R.

Навіщо взагалі тунелювати Vite

Три реальні причини:

  • Мобільне тестування. Відкрийте URL на телефоні, побачите той самий білд із HMR. Краще за шеринг екрана чи емулятори під кожен пристрій. Повний патерн див. у мобільному тестуванні через тунель.
  • Ділитися роботою в процесі. Надішліть посилання дизайнеру або PM — вони побачать вашу гілку наживо. Без кроку деплою.
  • Флоу OAuth і вебхуків, що потребують HTTPS. Роздавані Vite фронтенди, що спілкуються зі Stripe (лише redirect), Auth0 тощо. Вибір розглянуто у mkcert vs тунель.

SvelteKit, Astro, SolidStart — усі на Vite

Конфіги вище застосовні до будь-якого фреймворку, що роздає Vite як dev-сервер. У SvelteKit vite.config.js тієї ж форми. В Astro власний astro.config.mjs із підоб'єктом vite — помістіть ті самі опції server туди. SolidStart, Nuxt 3 (через Vite) і Qwik City: той самий патерн.

Production-білди — інша історія

Усе вище — конфіг dev-сервера. Коли ви запускаєте vite preview або роздаєте зібраний бандл, ніщо з цього не має значення, бо немає ні HMR, ні мідлвару host-check. На цьому етапі тунель просто пробрасує статичні файли.

Кілька дрібниць

  • Суворий host-CORS. Якщо фронтенд звертається до API на іншому origin (інший порт localhost або окремий API-тунель), налаштуйте CORS на боці API. Vite за замовчуванням не проксує, доки ви не вкажете це через server.proxy.
  • Застосунки з активним WebSocket. HMR Vite — це один WebSocket. Якщо застосунок використовує інший WebSocket для стану гри, чату чи живих даних, він окремий і теж має бути налаштований у клієнтському коді на URL тунелю.
  • URL тунелю в env-файлах. Ми кладемо активний URL тунелю в .env.local як VITE_PUBLIC_URL, коли фронтенду потрібно знати власну публічну адресу. import.meta.env.VITE_PUBLIC_URL читає його в клієнті.

Покроково

  1. Додайте блок server.host, allowedHosts і hmr до конфігу Vite.
  2. Перезапустіть dev-сервер.
  3. Виконайте npx portpreview 5173 (або порт, який використовує Vite).
  4. Відкрийте HTTPS-URL у браузері або на телефоні.
  5. Відредагуйте компонент. Переконайтеся, що HMR оновлює сторінку без повного перезавантаження.

Якщо HMR не оновлює, відкрийте консоль браузера — Vite логує спробу WebSocket-з'єднання, і ви побачите URL, до якого він звертався. Це покаже, чи спрацювала зміна hmr.clientPort/protocol.

Приєднайтеся до списку очікування PortPreview заради тунелів, що за замовчуванням зберігають WebSocket-upgrade.

Поширені запитання

Чому Vite блокує URL мого тунелю з «host is not allowed»?
У Vite 5+ додано host-check, що за замовчуванням відхиляє невідомі хостнейми. Додайте allowedHosts: ['.portpreview.dev'] (або ваш домен тунелю) до конфігу server, щоб приймати запити тунелю.
Як змусити HMR Vite працювати через тунель?
Задайте hmr.clientPort: 443 і hmr.protocol: 'wss' у vite.config.ts. Це каже HMR-клієнту під'єднуватися через HTTPS/WSS тунелю, а не намагатися дістатися localhost напряму, що не працює ззовні вашої машини.
Чи працює це для SvelteKit, Astro і Nuxt?
Так. Будь-який фреймворк, що використовує Vite як dev-сервер, приймає той самий конфіг server.host, allowedHosts і hmr. Astro вкладає його під vite в astro.config.mjs; інші кладуть у звичайний файл конфігу Vite.