How do you configure TLS correctly in Go (crypto/tls MinVersion, certificate reloading, mTLS)?
Question 573HardGo 1.22 to 1.25
crypto/tls has safe defaults. Since Go 1.22 the default minimum version is TLS 1.2 for both clients and servers, and RSA key-exchange cipher suites are no longer offered by default. Since Go 1.24 the hybrid post-quantum key exchange X25519MLKEM768 is on by default. Usually the best configuration is a minimal one: set MinVersion explicitly for auditors and leave cipher suites alone. TLS 1.3 suites cannot be configured anyway.
For certificate rotation without a restart, use GetCertificate, which is called on each handshake. For mTLS, the server sets ClientAuth: tls.RequireAndVerifyClientCert and ClientCAs, and the client presents Certificates and trusts the server CA via RootCAs.
type certReloader struct{ cert atomic.Pointer[tls.Certificate] }
func (r *certReloader) reload(certFile, keyFile string) error {
c, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
return err // keep serving the old certificate
}
r.cert.Store(&c)
return nil
}
func (r *certReloader) get(*tls.ClientHelloInfo) (*tls.Certificate, error) {
return r.cert.Load(), nil
}
func newServer(r *certReloader, caPEM []byte, h http.Handler) (*http.Server, error) {
pool := x509.NewCertPool()
if !pool.AppendCertsFromPEM(caPEM) {
return nil, errors.New("no CA certs parsed")
}
return &http.Server{
Addr: ":8443",
Handler: h,
ReadHeaderTimeout: 5 * time.Second,
TLSConfig: &tls.Config{
MinVersion: tls.VersionTLS12,
GetCertificate: r.get,
ClientAuth: tls.RequireAndVerifyClientCert, // mTLS
ClientCAs: pool,
},
}, nil
}
// srv.ListenAndServeTLS("", "") because the certificate comes from GetCertificate
Gotchas:
- Never ship
InsecureSkipVerify: true. If you need custom verification, useVerifyConnection. - A client's
ServerNamemust match the certificate's SAN. Go stopped accepting the CommonName field for hostname checks in Go 1.15. - For client-certificate rotation, use
GetClientCertificate. - Authorize on the verified peer identity (
r.TLS.VerifiedChainsor the SPIFFE URI SAN). A valid certificate alone does not mean the caller is allowed. - Go 1.24 added FIPS 140-3 mode (
GOFIPS140).
More on Observability, Debugging & Production Operations
- Q571How do you build a minimal, secure Docker image for a Go service (multi-stage build, scratch/distroless, CGO_ENABLED=0, CA certs, time zone data)?
- Q572How do you manage configuration in Go (env vars, files, flags)? How do you validate it and reload it safely at run time?
- Q574How do you implement WebSockets in Go? How do you handle concurrent writes, ping/pong keepalives and backpressure?
- Q575What do runtime/debug.SetMaxStack, SetMaxThreads, SetGCPercent and SetMemoryLimit do, and when would you use them?
- Q576How do you monitor Go runtime health in production (runtime/metrics, goroutine counts, GC pause, heap), and what alerts would you set?
- Q577How do you detect and fix high latency caused by lock contention, GC pressure, or connection-pool exhaustion using production telemetry?