This episode takes Echo to the production network: manual TLS with your own certificate, Auto TLS via Let's Encrypt, HTTP/2, reverse proxying with nginx and caddy, middleware.Proxy for load balancing, and safe X-Forwarded header handling.

Plain HTTP has no place in production anymore. This episode takes Echo to the production network: securing connections with TLS, automating certificates, and placing the application behind a proxy — the architecture you'll encounter in almost every real deployment.
Episode 15 covers manual TLS, Auto TLS via Let's Encrypt, HTTP/2, reverse proxying with nginx and caddy, middleware.Proxy, and safe X-Forwarded-* header handling.
For internal environments or testing, e.StartTLS is enough. The certificate can be generated with openssl:
openssl req -x509 -newkey rsa:2048 \
-keyout key.pem -out cert.pem \
-days 365 -nodes \
-subj "/CN=localhost"Run the server with the certificate:
e.Logger.Fatal(e.StartTLS(":443", "cert.pem", "key.pem"))The e.StartTLS(":443", "cert.pem", "key.pem") call handles the whole TLS handshake. Self-signed certificates trigger browser warnings — use them only for testing.
Echo supports Auto TLS, which requests and renews certificates from Let's Encrypt automatically. This eliminates the manual work of renewing certificates:
e.AutoTLSManager.HostPolicy = autocert.HostWhitelist("api.example.com")
e.AutoTLSManager.Cache = autocert.DirCache("/var/www/.cache")
go func() {
e.Logger.Fatal(e.StartAutoTLS(":443"))
}()
e.Logger.Fatal(e.Start(":80"))The combination of StartAutoTLS on port 443 and Start on port 80 is required: Let's Encrypt uses HTTP to validate the domain before issuing a certificate. The certificates are stored in a cache so they aren't re-requested on every boot.
With TLS active, net/http and Echo support HTTP/2 automatically. There's no extra configuration — modern clients negotiate HTTP/2 during the handshake, bringing the benefits of multiplexing and header compression.
A common architecture: nginx receives traffic, handles TLS and static caching, then forwards to Echo:
server {
listen 443 ssl;
server_name api.example.com;
ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}This nginx configuration forwards requests to Echo at 127.0.0.1:8080 while inserting the X-Forwarded-* headers the application needs.
Echo can also act as a proxy to several backend instances. middleware.Proxy with a round-robin balancer:
e.Use(middleware.ProxyWithConfig(middleware.ProxyConfig{
Balancer: middleware.NewRoundRobinBalancer([]*middleware.ProxyTarget{
{URL: mustParseURL("http://backend-1:8080")},
{URL: mustParseURL("http://backend-2:8080")},
}),
}))Incoming requests are distributed alternately to backend-1 and backend-2. This is the same pattern used by service meshes like Envoy.
The X-Forwarded-For, X-Forwarded-Proto, and X-Forwarded-Host headers are sent by the proxy to tell the application where the request came from. The problem: clients can also send these headers and spoof the IP address.
The main rule: trust headers only from proxies you actually control. With an internal middleware.Proxy, use middleware.ExtractIPWithConfig to read the real IP from the header safely:
e.Use(middleware.ExtractIPWithConfig(middleware.ExtractIPConfig{
Skipper: func(c echo.Context) bool { return false },
}))Behind nginx or caddy, make sure the proxy overwrites the header before forwarding it, rather than appending a second value. Otherwise, attackers can spoof the IP used by logging and the rate limiter.
Episode 15 takes Echo to the production network: manual TLS with e.StartTLS, Auto TLS from Let's Encrypt without manual maintenance, automatic HTTP/2, reverse proxying with nginx, middleware.Proxy for load balancing, and X-Forwarded-* header handling safe from spoofing.
Key takeaways:
e.StartTLS uses a manual certificate; self-signed is only for testing.StartAutoTLS automates Let's Encrypt certificates.middleware.Proxy does load balancing between instances.X-Forwarded-* headers are only trusted from proxies you control.In episode 16 next, we'll discuss WebSocket, streaming & SSE — WebSocket connections with gorilla/websocket inside an Echo handler, Server-Sent Events for notifications, response streaming, and chat and long-lived connection use cases.