You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vite配置多主机名证书报错:socket.setSecureContext is not a function

解决Vite开发服务器多主机名动态证书配置的socket.setSecureContext错误

问题背景

开发阶段需要根据访问的主机名自动切换对应SSL证书(用于保证Cookie安全等场景),无需修改服务器核心配置,编写了Vite插件代码后,证书能正常加载,但运行时抛出错误:

Uncaught TypeError: socket.setSecureContext is not a function

原配置代码如下:

import { defineConfig } from 'vite'
import fs from 'fs'
import tls from 'tls'
import mkcert from 'vite-plugin-mkcert'
import react from '@vitejs/plugin-react-swc'
import path from 'path'
import tailwindcss from 'tailwindcss'

const myPlugin = () => ({
  name: 'configure-server-certs',
  configureServer(server) {
    server.host = '0.0.0.0'
    const certificates: Record<string, { key: Buffer; cert: Buffer }> = {
      'admin-127-0-0-1.sslip.io': {
        cert: fs.readFileSync('./certs/admin-127-0-0-1.sslip.io.pem'),
        key: fs.readFileSync('./certs/admin-127-0-0-1.sslip.io-key.pem'),
      },
      'dark-127-0-0-1.sslip.io': {
        cert: fs.readFileSync('./certs/dark-127-0-0-1.sslip.io.pem'),
        key: fs.readFileSync('./certs/dark-127-0-0-1.sslip.io-key.pem'),
      },
      'light-127-0-0-1.sslip.io': {
        cert: fs.readFileSync('./certs/light-127-0-0-1.sslip.io.pem'),
        key: fs.readFileSync('./certs/light-127-0-0-1.sslip.io-key.pem'),
      },
    }

    server.httpServer?.on('secureConnection', socket => {
      const hostname = socket.servername
      console.log(hostname)
      const cert = certificates[hostname]
      console.log(cert)
      if (cert) {
        const ctx = tls.createSecureContext(cert)
        socket.setSecureContext(ctx)
      } else {
        console.log(`No certificate found for ${hostname}`)
      }
    })
  },
})

export default defineConfig({
  server: {
    host: '0.0.0.0',
    port: 5173,
  },
  plugins: [mkcert(), myPlugin(), react()],
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src'),
    },
  },
  css: {
    postcss: {
      plugins: [tailwindcss()],
    },
  },
})

问题原因

secureConnection事件触发时,TLS握手已经完成,此时的TLSSocket实例不再允许调用setSecureContext方法——该方法仅能在握手开始前使用。直接修改已建立连接的socket上下文不符合Node.js TLS API的设计逻辑。

正确解决方案

通过配置SNICallback(Server Name Indication回调),在TLS握手阶段动态返回对应主机名的证书上下文。修改后的插件代码如下:

import { defineConfig } from 'vite'
import fs from 'fs'
import tls from 'tls'
import mkcert from 'vite-plugin-mkcert'
import react from '@vitejs/plugin-react-swc'
import path from 'path'
import tailwindcss from 'tailwindcss'

const myPlugin = () => ({
  name: 'configure-server-certs',
  configureServer(server) {
    server.host = '0.0.0.0'
    
    // 预创建所有证书的SecureContext实例,提升性能
    const certContexts: Record<string, tls.SecureContext> = {
      'admin-127-0-0-1.sslip.io': tls.createSecureContext({
        cert: fs.readFileSync('./certs/admin-127-0-0-1.sslip.io.pem'),
        key: fs.readFileSync('./certs/admin-127-0-0-1.sslip.io-key.pem'),
      }),
      'dark-127-0-0-1.sslip.io': tls.createSecureContext({
        cert: fs.readFileSync('./certs/dark-127-0-0-1.sslip.io.pem'),
        key: fs.readFileSync('./certs/dark-127-0-0-1.sslip.io-key.pem'),
      }),
      'light-127-0-0-1.sslip.io': tls.createSecureContext({
        cert: fs.readFileSync('./certs/light-127-0-0-1.sslip.io.pem'),
        key: fs.readFileSync('./certs/light-127-0-0-1.sslip.io-key.pem'),
      }),
    }

    // 获取服务器的TLS配置,设置SNI回调
    const tlsOptions = server.httpServer?.options as tls.SecureContextOptions & tls.TLSServerOptions
    if (tlsOptions) {
      // 保存默认上下文(来自mkcert插件)
      const defaultContext = tlsOptions.SNICallback?.('', null) || tls.createSecureContext(tlsOptions)
      
      // 自定义SNI回调
      tlsOptions.SNICallback = (servername, cb) => {
        console.log(`Received SNI request for: ${servername}`)
        const context = certContexts[servername]
        if (context) {
          cb(null, context)
        } else {
          console.log(`No certificate found for ${servername}, using default`)
          cb(null, defaultContext)
        }
      }
    }
  },
})

export default defineConfig({
  server: {
    host: '0.0.0.0',
    port: 5173,
  },
  plugins: [mkcert(), myPlugin(), react()],
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src'),
    },
  },
  css: {
    postcss: {
      plugins: [tailwindcss()],
    },
  },
})

关键修改说明

  1. 预创建SecureContext:提前为每个证书生成SecureContext实例,避免每次请求都重复读取文件和创建上下文,提升性能。
  2. 使用SNICallback:在TLS握手阶段,客户端会发送SNI(访问的主机名),通过该回调匹配对应的证书上下文,符合Node.js TLS API的使用规范。
  3. 保留默认上下文:当没有匹配到对应主机名的证书时,回退到mkcert插件生成的默认证书,避免连接失败。

内容的提问来源于stack exchange,提问作者user672009

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 21:43:15