Опции hmr: настройка порта и протокола

Механизм HMR (Hot Module Replacement) в Vite отвечает за обновление модулей без полной перезагрузки страницы. При изменении файла браузер получает уведомление через WebSocket-соединение и применяет обновление мгновенно.

Базовая схема работы выглядит следующим образом:

  1. Dev-сервер Vite запускается локально.
  2. Браузер подключается к серверу HMR через WebSocket.
  3. При изменении файлов сервер отправляет уведомления клиенту.
  4. Клиент обновляет только изменённые модули.

По умолчанию Vite автоматически определяет адрес HMR-сервера, протокол и порт. Однако в сложных инфраструктурах этого недостаточно:

  • reverse proxy;
  • Docker;
  • WSL2;
  • HTTPS через nginx;
  • удалённая разработка;
  • работа через IP вместо localhost;
  • туннели вроде ngrok;
  • нестандартные порты;
  • Kubernetes и контейнерные окружения.

Для таких случаев используется объект server.hmr.

Пример базовой структуры:

import { defineConfig } from 'vite'

export default defineConfig({
  server: {
    hmr: {
      protocol: 'ws',
      host: 'localhost',
      port: 24678
    }
  }
})

Как работает HMR-соединение

При открытии страницы Vite внедряет в клиентский код WebSocket-клиент.

Пример подключения:

ws://localhost:5173/

или:

wss://localhost:5173/

После подключения браузер начинает ожидать сообщения:

{
  "type": "update",
  "updates": [...]
}

Если соединение разрывается:

  • HMR перестаёт работать;
  • обновления модулей не применяются;
  • браузер может начать делать полную перезагрузку;
  • в консоли появляются ошибки WebSocket.

Наиболее распространённая ошибка:

WebSocket connection to 'ws://localhost:5173/' failed

Именно для устранения подобных проблем и настраиваются параметры hmr.


Опция hmr.port

Назначение

Параметр port задаёт порт WebSocket-сервера HMR.

Пример:

export default defineConfig({
  server: {
    hmr: {
      port: 3001
    }
  }
})

В этом случае HMR будет использовать:

ws://localhost:3001/

а основной dev-сервер может продолжать работать на другом порту.


Отличие server.port от hmr.port

Это разные параметры.

server.port

Порт HTTP-сервера Vite:

server: {
  port: 5173
}

Используется для:

  • загрузки страницы;
  • отдачи модулей;
  • обработки запросов браузера.

hmr.port

Порт WebSocket HMR:

server: {
  hmr: {
    port: 24678
  }
}

Используется исключительно для WebSocket-подключения.


Работа на разных портах

Конфигурация:

export default defineConfig({
  server: {
    port: 5173,

    hmr: {
      port: 24678
    }
  }
})

Результат:

Назначение Адрес
HTTP сервер http://localhost:5173
WebSocket HMR ws://localhost:24678

Когда требуется настройка hmr.port

Reverse Proxy

Одна из самых частых причин.

Например:

Browser
   ↓
nginx
   ↓
Vite

nginx может проксировать HTTP-трафик, но не WebSocket.

В результате:

  • страница открывается;
  • модули загружаются;
  • HMR не работает.

Пример конфигурации:

server: {
  hmr: {
    port: 443
  }
}

Здесь HMR использует порт HTTPS-прокси.


Docker

В Docker контейнере внутренний порт может отличаться от внешнего.

Пример:

Container: 5173
Host: 3000

Без настройки HMR браузер попытается подключиться к:

ws://localhost:5173/

Но этот порт может быть недоступен извне.

Исправление:

server: {
  hmr: {
    host: 'localhost',
    port: 3000
  }
}

WSL2

В WSL2 Vite часто запускается внутри Linux-подсистемы.

Типичная проблема:

WebSocket connection failed

Причина — браузер Windows не может подключиться к внутреннему Linux-адресу.

Решение:

server: {
  host: '0.0.0.0',

  hmr: {
    host: 'localhost',
    port: 5173
  }
}

Удалённая разработка

При работе через:

  • VPS;
  • SSH Tunnel;
  • cloud IDE;
  • Gitpod;
  • Codespaces;

адрес WebSocket должен быть доступен извне.

Пример:

server: {
  hmr: {
    host: 'dev.example.com',
    port: 443
  }
}

Опция hmr.protocol

Назначение

Параметр protocol определяет WebSocket-протокол.

Возможные значения:

Значение Описание
ws обычный WebSocket
wss WebSocket поверх HTTPS

Использование ws

Обычный небезопасный WebSocket.

Пример:

server: {
  hmr: {
    protocol: 'ws'
  }
}

Подключение:

ws://localhost:5173/

Обычно используется:

  • локально;
  • без HTTPS;
  • в dev-режиме на localhost.

Использование wss

Безопасный WebSocket через TLS.

Пример:

server: {
  hmr: {
    protocol: 'wss'
  }
}

Подключение:

wss://example.com/

Почему wss обязателен для HTTPS

Браузеры запрещают смешанный контент.

Если страница открыта через HTTPS:

https://example.com

то WebSocket через ws:// будет заблокирован.

Ошибка:

Mixed Content:
The page was loaded over HTTPS,
but attempted to connect to the insecure WebSocket endpoint

Поэтому для HTTPS нужен:

hmr: {
  protocol: 'wss'
}

Настройка HTTPS + HMR

Пример полной конфигурации

import { defineConfig } from 'vite'

export default defineConfig({
  server: {
    https: true,

    hmr: {
      protocol: 'wss',
      host: 'localhost',
      port: 5173
    }
  }
})

Конфигурация с nginx

Схема:

Browser
  ↓ HTTPS
nginx
  ↓ HTTP
Vite

Конфигурация Vite:

server: {
  hmr: {
    protocol: 'wss',
    host: 'example.com',
    port: 443
  }
}

nginx должен поддерживать upgrade-запросы WebSocket.

Пример:

location / {
    proxy_pass http://localhost:5173;

    proxy_http_version 1.1;

    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}

Без этого HMR работать не будет.


Использование clientPort

Назначение

Иногда серверный порт и клиентский порт отличаются.

Для этого существует:

hmr.clientPort

Пример

server: {
  hmr: {
    port: 5173,
    clientPort: 443
  }
}

Здесь:

  • сервер слушает 5173;
  • браузер подключается к 443.

Это особенно полезно при reverse proxy.


Использование host

Назначение

Параметр определяет адрес HMR-сервера.

Пример:

hmr: {
  host: '192.168.0.10'
}

Работа по локальной сети

Если устройство открывает Vite с телефона:

http://192.168.0.10:5173

то HMR тоже должен использовать IP:

server: {
  host: '0.0.0.0',

  hmr: {
    host: '192.168.0.10'
  }
}

Иначе браузер телефона попытается подключиться к своему собственному localhost.


Автоматическое определение HMR

По умолчанию Vite сам пытается определить:

  • протокол;
  • порт;
  • host;
  • origin.

Обычно этого достаточно для:

  • localhost;
  • стандартной разработки;
  • простых SPA;
  • отсутствия proxy.

Но автоматическое определение часто ломается в:

  • Docker;
  • HTTPS;
  • WSL2;
  • Kubernetes;
  • nginx;
  • Traefik;
  • удалённых IDE.

Пример конфигурации для Docker + HTTPS

export default defineConfig({
  server: {
    host: '0.0.0.0',
    port: 5173,

    hmr: {
      protocol: 'wss',
      host: 'dev.example.com',
      clientPort: 443
    }
  }
})

Пример конфигурации для локальной сети

export default defineConfig({
  server: {
    host: '0.0.0.0',

    hmr: {
      protocol: 'ws',
      host: '192.168.1.15',
      port: 5173
    }
  }
})

Пример конфигурации для nginx reverse proxy

export default defineConfig({
  server: {
    hmr: {
      protocol: 'wss',
      host: 'example.com',
      clientPort: 443
    }
  }
})

Отладка проблем HMR

Проверка WebSocket

Во вкладке DevTools:

Network → WS

должно отображаться активное WebSocket-соединение.


Проверка адреса подключения

В консоли браузера можно увидеть:

[vite] connecting...

или:

WebSocket connection to ... failed

Адрес покажет:

  • неправильный порт;
  • неверный host;
  • ошибочный протокол.

Частые ошибки

Неправильный протокол

Страница:

https://

HMR:

ws://

Результат:

Mixed Content

Неправильный host

Например:

localhost

на мобильном устройстве.

Телефон попытается подключиться к своему localhost.


Заблокированный порт

Firewall или Docker могут блокировать порт HMR.


nginx без WebSocket upgrade

Очень распространённая ошибка.

Без:

proxy_set_header Upgrade $http_upgrade;

WebSocket не будет работать.


Полная конфигурация HMR

import { defineConfig } from 'vite'

export default defineConfig({
  server: {
    host: '0.0.0.0',
    port: 5173,

    hmr: {
      protocol: 'wss',
      host: 'dev.example.com',
      port: 5173,
      clientPort: 443,
      timeout: 30000
    }
  }
})

Параметры объекта hmr

Параметр Назначение
protocol протокол WebSocket
host адрес подключения
port серверный порт HMR
clientPort порт клиента
path путь WebSocket
timeout timeout соединения
overlay показ overlay ошибок

Опция overlay

Управляет отображением ошибок поверх страницы.

Пример:

hmr: {
  overlay: true
}

При ошибках синтаксиса Vite показывает overlay в браузере.

Отключение:

hmr: {
  overlay: false
}

Опция timeout

Задаёт timeout подключения.

Пример:

hmr: {
  timeout: 30000
}

Полезно при:

  • медленных VPN;
  • удалённых соединениях;
  • нестабильной сети;
  • reverse proxy.

Взаимодействие HMR и HTTPS-сертификатов

При использовании self-signed сертификатов браузер может блокировать WebSocket.

Симптом:

WebSocket connection failed

Решения:

  • доверенный сертификат;
  • mkcert;
  • локальный CA;
  • ручное подтверждение сертификата в браузере.

Практика настройки

Простая локальная разработка

server: {
  hmr: {
    protocol: 'ws'
  }
}

HTTPS localhost

server: {
  https: true,

  hmr: {
    protocol: 'wss'
  }
}

Docker

server: {
  host: '0.0.0.0',

  hmr: {
    host: 'localhost',
    clientPort: 3000
  }
}

nginx reverse proxy

server: {
  hmr: {
    protocol: 'wss',
    host: 'example.com',
    clientPort: 443
  }
}

LAN разработка

server: {
  host: '0.0.0.0',

  hmr: {
    host: '192.168.1.10'
  }
}