Most SSL guides focus on Nginx and Apache. But if you’re running HAProxy for load balancing, Caddy for automatic HTTPS, or Lighttpd on an embedded system, the configuration is different enough to warrant its own guide. I’ve set up SSL on all of these and the differences matter.
HAProxy: SSL Termination at the Load Balancer
HAProxy is often used as an SSL termination point — it handles HTTPS connections from clients and forwards plain HTTP (or re-encrypted HTTPS) to backend servers. This centralizes certificate management to a single place rather than every backend server.
Certificate Format for HAProxy
HAProxy needs a single PEM file that contains the private key, domain certificate, and certificate chain — in that order:
cat private.key certificate.crt ca-bundle.crt > combined.pem
That’s the opposite order from Nginx’s fullchain (where the cert comes first, then intermediates). HAProxy specifically wants the key first.
Basic SSL Termination Configuration
frontend https_frontend
bind *:443 ssl crt /etc/haproxy/certs/combined.pem
bind *:80
http-request redirect scheme https unless { ssl_fc }
default_backend web_servers
backend web_servers
balance roundrobin
server web1 192.168.1.10:80 check
server web2 192.168.1.11:80 check
The ssl crt directive points to your combined PEM file. HAProxy reads the key, cert, and chain from that single file.
Multiple Certificates in HAProxy
For multiple domains, you can either use a directory of PEM files or specify them individually:
# Directory approach - HAProxy loads all .pem files in the directory
bind *:443 ssl crt /etc/haproxy/certs/
# Individual files
bind *:443 ssl crt /etc/haproxy/certs/site1.pem crt /etc/haproxy/certs/site2.pem
The directory approach is cleaner when managing many sites. HAProxy uses SNI to pick the right certificate for each domain.
TLS Settings in HAProxy
global
ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305
ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets
ssl-default-bind-ciphers sets ciphers for TLS 1.2. ssl-default-bind-ciphersuites sets them for TLS 1.3 (different syntax). ssl-min-ver TLSv1.2 drops TLS 1.0 and 1.1 support.
SSL Health Check with HAProxy
Check that HAProxy is serving the correct certificate:
echo | openssl s_client -connect loadbalancer.example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -subject -dates
Caddy: Automatic HTTPS With Zero Config
Caddy v2 is remarkable for SSL management. By default, it automatically provisions and renews Let’s Encrypt certificates for any domain it serves. There’s no separate Certbot, no cron jobs, no manual certificate management.
The Caddyfile: Minimal HTTPS Configuration
example.com {
root * /var/www/example.com
file_server
}
That’s the entire configuration. Caddy:
- Detects the domain name
- Obtains a Let’s Encrypt certificate via HTTP challenge
- Sets up HTTPS with modern TLS settings
- Redirects HTTP to HTTPS automatically
- Renews the certificate before it expires
If you access http://example.com, Caddy responds with a 301 redirect to https://example.com without any explicit configuration.
Using Custom Certificates in Caddy
If you have your own certificate (from a private CA, or a paid cert):
example.com {
tls /etc/caddy/certs/certificate.crt /etc/caddy/certs/private.key
root * /var/www/example.com
file_server
}
For a PFX file:
example.com {
tls {
load /etc/caddy/certs/
}
}
TLS Configuration in Caddy
Override the default TLS settings:
example.com {
tls {
protocols tls1.2 tls1.3
ciphers TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
curves x25519 secp521r1 secp384r1 secp256r1
}
}
Honestly, Caddy’s defaults are already excellent — modern ciphers, TLS 1.2+, OCSP stapling enabled. Most of the time you don’t need to configure TLS manually at all.
Caddy for Reverse Proxy with Automatic SSL
A very common Caddy use case:
example.com {
reverse_proxy localhost:3000
}
api.example.com {
reverse_proxy localhost:8080
}
Both subdomains get their own Let’s Encrypt certificates automatically. If you’re serving a Node.js app or any other HTTP service on a non-standard port, Caddy handles all the TLS in front of it. Each domain gets its own certificate and renewal cycle.
Lighttpd: SSL Configuration for Lightweight Servers
Lighttpd is less common than Nginx but appears in embedded systems, routers, and minimal server deployments. It requires mod_openssl for SSL.
server.modules += ("mod_openssl")
$SERVER["socket"] == ":443" {
ssl.engine = "enable"
ssl.pemfile = "/etc/lighttpd/ssl/combined.pem"
ssl.ca-file = "/etc/lighttpd/ssl/ca-bundle.crt"
ssl.cipher-list = "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:..."
ssl.openssl.ssl-conf-cmd = ("MinProtocol" => "TLSv1.2")
}
Like HAProxy, Lighttpd wants the key and certificate combined in one file:
cat private.key certificate.crt > /etc/lighttpd/ssl/combined.pem
HTTP to HTTPS redirect in Lighttpd:
$HTTP["scheme"] == "http" {
url.redirect = (".*" => "https://example.com$0")
}
Traefik: SSL for Docker Environments
Traefik is popular in Docker/Kubernetes environments. It integrates with Let’s Encrypt natively and reads configuration from container labels.
Static configuration (traefik.yml):
certificatesResolvers:
letsencrypt:
acme:
email: admin@example.com
storage: /letsencrypt/acme.json
httpChallenge:
entryPoint: web
Docker container label to enable automatic SSL:
# docker-compose.yml
services:
myapp:
image: myapp:latest
labels:
- "traefik.enable=true"
- "traefik.http.routers.myapp.rule=Host(`example.com`)"
- "traefik.http.routers.myapp.entrypoints=websecure"
- "traefik.http.routers.myapp.tls.certresolver=letsencrypt"
Traefik handles certificate issuance, renewal, and serving. Add a new container with the right labels and it gets HTTPS automatically.
Comparing SSL Certificate Management Across Servers
| Server | Certificate Format | Auto-Renewal | OCSP Stapling | HTTP/2 |
|---|---|---|---|---|
| Nginx | PEM (separate key/cert) | No (use Certbot) | Yes (manual config) | Yes |
| Apache | PEM (separate files) | No (use Certbot) | Yes (manual config) | Via mod_http2 |
| HAProxy | Combined PEM (key first) | No (use Certbot) | Yes (built-in) | Yes |
| Caddy | Automatic or custom PEM | Yes (built-in) | Yes (automatic) | Yes |
| Lighttpd | Combined PEM (key first) | No | Limited | No |
| Traefik | Automatic or custom PEM | Yes (built-in) | Yes (automatic) | Yes |
For new projects where SSL management overhead matters, Caddy or Traefik are genuinely compelling options. The automatic certificate provisioning removes an entire category of operational work. For existing Nginx or Apache deployments, adding Certbot is the pragmatic path.