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

如何在ListenAndServeTLS前路由HTTPS请求并根据Host选证书与mux

实现基于Host动态选择证书与Mux的HTTPS服务

可以仅通过Go标准库的http包实现这个需求,核心思路是利用tls.Config的GetCertificate方法动态返回证书,同时自定义主Handler根据请求Host路由到对应Mux。

核心实现思路

  1. 动态证书选择:tls.Config的GetCertificate方法会在TLS握手阶段被调用,此时能获取到客户端请求的ServerName(即请求的Host),据此返回对应的证书文件。
  2. 多Mux路由:为不同Host创建独立的http.ServeMux,在主Handler中根据请求的r.Host将请求转发到对应Mux处理。

代码示例

package main

import (
	"crypto/tls"
	"net/http"
	"sync"
)

// 缓存已加载的证书,避免重复读取文件
var certCache = struct {
	sync.RWMutex
	certs map[string]*tls.Certificate
}{
	certs: make(map[string]*tls.Certificate),
}

// 加载指定Host对应的证书
func loadCert(host string) (*tls.Certificate, error) {
	// 先读缓存
	certCache.RLock()
	cert, exists := certCache.certs[host]
	certCache.RUnlock()
	if exists {
		return cert, nil
	}

	// 根据Host匹配证书路径,实际场景按需调整规则
	certFile := host + ".crt"
	keyFile := host + ".key"
	newCert, err := tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return nil, err
	}

	// 写入缓存
	certCache.Lock()
	certCache.certs[host] = &newCert
	certCache.Unlock()
	return &newCert, nil
}

func main() {
	// 为不同Host创建独立的路由Mux
	muxHost1 := http.NewServeMux()
	muxHost1.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("Hello from host1.example.com"))
	})

	muxHost2 := http.NewServeMux()
	muxHost2.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("Hello from host2.example.com"))
	})

	// 主Handler:根据请求Host转发到对应Mux
	mainHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		switch r.Host {
		case "host1.example.com":
			muxHost1.ServeHTTP(w, r)
		case "host2.example.com":
			muxHost2.ServeHTTP(w, r)
		default:
			http.Error(w, "Unknown host", http.StatusBadRequest)
		}
	})

	// 配置TLS规则,核心是GetCertificate方法
	tlsConfig := &tls.Config{
		GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
			// 利用TLS握手时的ServerName(即请求Host)加载对应证书
			return loadCert(info.ServerName)
		},
	}

	// 创建并启动HTTP服务
	server := &http.Server{
		Addr:      ":443",
		Handler:   mainHandler,
		TLSConfig: tlsConfig,
	}

	// 这里证书参数传空,因为TLSConfig已通过GetCertificate动态获取证书
	err := server.ListenAndServeTLS("", "")
	if err != nil && err != http.ErrServerClosed {
		panic(err)
	}
}

关键细节说明

  • 证书缓存:用带互斥锁的缓存结构,避免每次TLS握手都重复读取证书文件,提升服务性能。
  • SNI依赖:info.ServerName来自客户端TLS握手时的SNI扩展,现代浏览器和HTTP客户端均支持该特性,确保能正确获取请求Host。
  • Mux隔离:不同Host对应独立的Mux,可实现完全隔离的路由规则和业务逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 19:50:26