Day 65 — HTTP/2
Learning Objectives
- Enable HTTP/2 on your TLS server (it's automatic with TLS)
- Understand why H2 changes connection pool sizing assumptions
- Measure latency difference between H1 and H2
Key Concepts
HTTP/1.1 vs HTTP/2:
H1: 1 request per connection at a time (pipelining is broken in practice)
browsers open 6 connections per origin to get parallelism
H2: multiple requests multiplexed over 1 connection (streams)
browser needs only 1 connection per origin
header compression (HPACK): repeated headers cost ~10 bytes vs ~200 bytes
Why H2 changes backend pool sizing:
H1: 100 concurrent requests → 100 backend connections
H2: 100 concurrent requests → 1-10 backend connections (fewer, multiplexed)
Your connection pool maxIdleConnsPerHost should be LOWER for H2 backends
H2 gotchas:
- Requires TLS (h2c cleartext is non-standard)
- Go's net/http enables H2 automatically over TLS
- Check: log `r.Proto` to see "HTTP/2.0" vs "HTTP/1.1"
Exercise: H2-Enabled Server
package main
import (
"crypto/tls"
"fmt"
"net/http"
"time"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
proto := r.Proto
fmt.Fprintf(w, `{"protocol":%q,"path":%q}`, proto, r.URL.Path)
})
// TLS is required for H2; Go enables H2 automatically
// To DISABLE H2: set TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler))
srv := &http.Server{
Addr: ":8443",
Handler: mux,
ReadHeaderTimeout: 5 * time.Second,
// H2 enabled automatically when serving TLS
}
fmt.Println("H1/H2 server on :8443")
fmt.Println("H1 test: curl --http1.1 --cacert ca.crt https://localhost:8443/")
fmt.Println("H2 test: curl --http2 --cacert ca.crt https://localhost:8443/")
if err := srv.ListenAndServeTLS("server.crt", "server.key"); err != nil {
fmt.Println("ERROR:", err)
}
}
# Compare H1 vs H2 latency (100 requests)
time curl --http1.1 --cacert ca.crt -s -o /dev/null https://localhost:8443/ &
time curl --http2 --cacert ca.crt -s -o /dev/null https://localhost:8443/
Real-World Context
Go's net/http package enables HTTP/2 automatically when you call ListenAndServeTLS. Most teams don't notice — it just works. But your WAF middleware must work correctly with H2 (it does, because the WAF sees http.Request regardless of H1 or H2 underneath).