Vite поддерживает запуск dev-сервера по HTTPS через встроенный сервер на базе Node.js. HTTPS особенно важен при разработке приложений, использующих:
По умолчанию Vite запускает сервер через HTTP:
vite
или:
npm run dev
Адрес сервера обычно выглядит так:
http://localhost:5173
Для включения HTTPS используется параметр
server.https.
Минимальная конфигурация:
// vite.config.js
import { defineConfig } from 'vite'
export default defineConfig({
server: {
https: true
}
})
После запуска адрес изменится:
https://localhost:5173
В этом режиме Vite создаёт временный self-signed сертификат.
Самоподписанные сертификаты подходят для локальной разработки, но имеют ограничения:
Поэтому в реальных проектах обычно используют собственные сертификаты.
Vite позволяет передавать полноценные TLS-сертификаты через объект
https.
Наиболее распространённый вариант:
// vite.config.js
import { defineConfig } from 'vite'
import fs from 'node:fs'
export default defineConfig({
server: {
https: {
key: fs.readFileSync('./certs/server.key'),
cert: fs.readFileSync('./certs/server.crt')
}
}
})
Обычно используются следующие файлы:
certs/
├── server.key
├── server.crt
Где:
server.key — приватный ключ;server.crt — сертификат.Иногда дополнительно применяется цепочка сертификатов:
certs/
├── server.key
├── server.crt
├── ca.crt
При наличии собственного центра сертификации можно указать
ca:
import { defineConfig } from 'vite'
import fs from 'node:fs'
export default defineConfig({
server: {
https: {
key: fs.readFileSync('./certs/server.key'),
cert: fs.readFileSync('./certs/server.crt'),
ca: fs.readFileSync('./certs/ca.crt')
}
}
})
Это особенно важно для:
Создание приватного ключа:
openssl genrsa -out server.key 2048
Создание сертификата:
openssl req -new -x509 \
-key server.key \
-out server.crt \
-days 365
После генерации появятся:
server.key
server.crt
Современные браузеры требуют наличие Subject Alternative Name (SAN). Без него сертификат считается некорректным.
Пример конфигурации OpenSSL:
[req]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn
x509_extensions = v3_req
[dn]
CN = localhost
[v3_req]
subjectAltName = @alt_names
[alt_names]
DNS.1 = localhost
IP.1 = 127.0.0.1
Создание сертификата:
openssl req -x509 \
-nodes \
-days 365 \
-newkey rsa:2048 \
-keyout server.key \
-out server.crt \
-config openssl.cnf
Наиболее удобный инструмент для локальной разработки —
mkcert.
Он создаёт локальный доверенный CA и автоматически добавляет его в систему.
Установка:
mkcert -install
Создание сертификатов:
mkcert localhost 127.0.0.1 ::1
Результат:
localhost+2.pem
localhost+2-key.pem
Подключение в Vite:
import { defineConfig } from 'vite'
import fs from 'node:fs'
export default defineConfig({
server: {
https: {
key: fs.readFileSync('./localhost+2-key.pem'),
cert: fs.readFileSync('./localhost+2.pem')
}
}
})
Преимущества mkcert:
Vite использует WebSocket-соединение для Hot Module Replacement.
При HTTPS HMR автоматически переключается на WSS:
wss://localhost:5173
Иногда требуется ручная настройка:
export default defineConfig({
server: {
https: true,
hmr: {
protocol: 'wss',
host: 'localhost'
}
}
})
Это особенно важно при:
Часто локальная разработка ведётся через домен:
https://myapp.local
Для этого:
Пример:
export default defineConfig({
server: {
host: 'myapp.local',
https: {
key: fs.readFileSync('./certs/myapp.key'),
cert: fs.readFileSync('./certs/myapp.crt')
}
}
})
При запуске внутри контейнера сертификаты обычно монтируются как volume:
services:
vite:
volumes:
- ./certs:/app/certs
Конфигурация:
https: {
key: fs.readFileSync('/app/certs/server.key'),
cert: fs.readFileSync('/app/certs/server.crt')
}
В production-like окружениях HTTPS часто завершается на nginx или Traefik.
Схема:
Browser
↓ HTTPS
Nginx
↓ HTTP
Vite Dev Server
В таком случае HTTPS внутри Vite может не использоваться.
Конфигурация nginx:
server {
listen 443 ssl;
ssl_certificate /etc/nginx/cert.pem;
ssl_certificate_key /etc/nginx/key.pem;
location / {
proxy_pass http://localhost:5173;
}
}
Node.js HTTPS-сервер поддерживает HTTP/2 через дополнительные настройки TLS.
Некоторые reverse proxy автоматически активируют HTTP/2:
listen 443 ssl http2;
Для Vite это может быть полезно при тестировании:
Если HMR работает через отдельный порт, используется:
export default defineConfig({
server: {
https: true,
hmr: {
clientPort: 443
}
}
})
Особенно полезно при:
Node.js поддерживает формат .pfx.
Пример:
import fs from 'node:fs'
export default defineConfig({
server: {
https: {
pfx: fs.readFileSync('./certs/server.pfx'),
passphrase: 'secret'
}
}
})
Такой формат распространён в Windows-инфраструктуре.
Node.js позволяет управлять TLS-параметрами:
https: {
key: fs.readFileSync('./server.key'),
cert: fs.readFileSync('./server.crt'),
ciphers: `
TLS_AES_256_GCM_SHA384:
TLS_CHACHA20_POLY1305_SHA256:
TLS_AES_128_GCM_SHA256
`.replace(/\s+/g, '')
}
Подобная конфигурация используется редко, но может потребоваться для:
https: {
key: fs.readFileSync('./server.key'),
cert: fs.readFileSync('./server.crt'),
minVersion: 'TLSv1.2'
}
Поддерживаются:
TLSv1TLSv1.1TLSv1.2TLSv1.3Одна из самых распространённых ошибок:
NET::ERR_CERT_AUTHORITY_INVALID
Причины:
Чаще всего проблема решается через mkcert.
Причины:
Проверка сертификата:
openssl x509 -in server.crt -text -noout
Проверка ключа:
openssl rsa -in server.key -check
Проверка сертификата:
curl -v https://localhost:5173
Игнорирование ошибок:
curl -k https://localhost:5173
Часто HTTPS включается только для development:
export default defineConfig(({ mode }) => ({
server: {
https: mode === 'development'
}
}))
Или:
https: process.env.VITE_HTTPS === 'true'
Для корректной работы путей:
import path from 'node:path'
import fs from 'node:fs'
https: {
key: fs.readFileSync(
path.resolve(__dirname, 'certs/server.key')
),
cert: fs.readFileSync(
path.resolve(__dirname, 'certs/server.crt')
)
}
Приватные ключи нельзя хранить в репозитории.
.gitignore:
certs/
*.key
*.pem
*.pfx
Пример:
certs/
├── dev/
├── stage/
├── local/
Конфигурация:
const certDir = `./certs/${process.env.APP_ENV}`
https: {
key: fs.readFileSync(`${certDir}/server.key`),
cert: fs.readFileSync(`${certDir}/server.crt`)
}
При HTTPS браузер блокирует небезопасные WebSocket-соединения:
ws://
Требуется использовать:
wss://
Это касается:
Service Worker работает только в secure context:
https://
Исключение:
localhost
При разработке PWA HTTPS становится обязательным.
Cookie с флагом:
Secure
не передаются по HTTP.
Для тестирования авторизации HTTPS обязателен:
Set-Cookie: token=123; Secure; HttpOnly
WebAuthn API требует защищённого соединения.
Без HTTPS не работают:
Переход с HTTP на HTTPS меняет origin:
http://localhost:5173
https://localhost:5173
Из-за этого могут возникать ошибки CORS.
Иногда требуется обновление whitelist:
origin: [
'https://localhost:5173'
]
Браузеры блокируют HTTP-ресурсы внутри HTTPS-страницы.
Проблемный пример:
<script src="http://localhost/api.js"></script>
Корректный вариант:
<script src="https://localhost/api.js"></script>
Или:
<script src="//localhost/api.js"></script>
import { defineConfig } from 'vite'
import fs from 'node:fs'
import path from 'node:path'
export default defineConfig({
server: {
host: 'localhost',
port: 5173,
https: {
key: fs.readFileSync(
path.resolve(__dirname, './certs/server.key')
),
cert: fs.readFileSync(
path.resolve(__dirname, './certs/server.crt')
),
minVersion: 'TLSv1.2'
},
hmr: {
protocol: 'wss',
host: 'localhost',
clientPort: 5173
}
}
})