Go 的 http.Server 通过 ListenAndServeTLS 或配置 TLSConfig 启用 HTTPS,需提供 PEM 格式证书与私钥;生产环境须显式限制 TLS 版本与密码套件;反向代理下需检查 X-Forwarded-Proto 头确保重定向正确。
http.Server 怎么启用 HTTPSGo 原生不区分 HTTP/HTTPS,靠是否传入 tls.Config 和调用 ListenAndServeTLS 来启用 TLS。直接调用 http.ListenAndServe 只能跑 HTTP。
关键点:必须同时提供证书文件和私钥文件,且格式为 PEM;路径必须可读;私钥不能有密码保护(Go 不支持交互式解密)。
ListenAndServeTLS(addr, certFile, keyFile string) 是最简方式,内部自动加载证书http.Server 结构体 + TLSConfig
fullchain.pem,就传它;不要只传 cert.pem,否则客户端可能校验失败tls.Config
默认 tls.Config 兼容性太宽,会启用已知不安全的协议版本和密码套件。生产环境必须显式收紧。
server := &http.Server{
Addr: ":443",
Handler: mux,
TLSConfig: &tls.Config{
MinVersion: tls.VersionTLS12,
CurvePreferences: []tls.CurveID{tls.CurveP256, tls.X25519},
CipherSuites: []uint16{
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
},
PreferServerCipherSuites: true,
},
}
注意:CipherSuites 列表里没包含任何带 RC4、3DES、SHA1 或 SSLv3 的套件;PreferServerCipherSuites: true 确保服务端优先级生效;MinVersion: tls.VersionTLS12 显式关闭 TLS 1.0/1.1。
acme/autocert 包能自动申请和续期,但容易在本地开发或内网环境卡住——它依赖公网可达的 HTTP-01 挑战,且要求 80 端口可访问。
autocert,用 mkcert 生成本地可信证书更可
靠autocert,必须确保 HostPolicy 返回 nil 或明确允许的域名,否则 400Cache 必须实现(如用 autocert.DirCache("/var/www/.cache")),否则每次重启都重申请/tmp 在某些容器中是 tmpfs)http.Redirect 在 HTTPS 下可能跳回 HTTP当 Go 服务部署在反向代理(如 Nginx、ALB)后面时,客户端实际连的是代理的 HTTPS,但代理转发给 Go 的是 HTTP 请求。此时 r.TLS 为 nil,Request.URL.Scheme 是 http,导致 http.Redirect 生成错误跳转地址。
解决方法是信任代理设置的头:
func isSecure(r *http.Request) bool {
if r.TLS != nil {
return true
}
if r.Header.Get("X-Forwarded-Proto") == "https" {
return true
}
return false
}
// 重定向时
if !isSecure(r) {
http.Redirect(w, r, "https://"+r.Host+r.RequestURI, http.StatusMovedPermanently)
return
}
这个逻辑必须在所有需要判断协议的地方复用;仅靠 r.TLS != nil 在代理场景下完全不可靠。
真正难处理的不是配 TLS,而是让整个请求链路(客户端 → 代理 → Go)对协议、头、证书的信任关系保持一致。漏掉一个环节,就可能出现混合内容、跳转降级或证书警告。