Caddy has an excellent default TLS configuration, but we can still improve it!

Caddy’s default TLS configuration is very good. It includes a lot of features that older and well-known servers like Nginx or Apache don’t enable by default.

From the tls module’s documentation:

Caddy implements these TLS features for you automatically. It is the only server to do so by default:

  • Session identifiers
  • Session ticket key rotation
  • OCSP stapling
  • Dynamic record sizing
  • Application-layer protocol negotiation
  • Forward secrecy
  • HTTP/2 (for the HTTP server)
  • Certificate management (including auto-renew)
  • Man-In-The-Middle detection (for HTTPS sites)

Pretty awesome isn’t it? It also enables TLS 1.2 only by default and will support TLS 1.3 soon. FYI Caddy does not have its own TLS implementation by relies on the crypto/tls module of Go.

Anyway, there are still 3 parameters that we can improve.

Certificate key type

By default, Caddy will use a 2048 bits RSA key to sign Let’s encrypt certificates. This is a sane default and great for compatibility.

I prefer elliptic curve, so I use 256 bits ECDSA certificates. I’ve been running some for over a year now with Nginx and Let’s Encrypt without issues.

Here’s how to change the key type:

tls {
	key_type p256
}

I had to remove the content of /etc/ssl/caddy in order for Caddy to regenerate with a new key after a restart.

Cipher suites

Edit (04/03/2019): Caddy 0.11.5 is out, with TLS 1.3 support (enabled by default). It removes non-AEAD ciphers by default for both TLS 1.2 and 1.3. You can’t customise TLS 1.3 ciphers with the Go implementation (Go 1.12), but you can with TLS 1.2. There is no need to specify the cipher option unless you want to enabled CBC ciphers for compatibility.

Caddy support these ciphers by default:

  • ECDHE-ECDSA-AES256-GCM-SHA384
  • ECDHE-RSA-AES256-GCM-SHA384
  • ECDHE-ECDSA-AES128-GCM-SHA256
  • ECDHE-RSA-AES128-GCM-SHA256
  • ECDHE-ECDSA-WITH-CHACHA20-POLY1305
  • ECDHE-RSA-WITH-CHACHA20-POLY1305
  • ECDHE-RSA-AES256-CBC-SHA
  • ECDHE-RSA-AES128-CBC-SHA
  • ECDHE-ECDSA-AES256-CBC-SHA
  • ECDHE-ECDSA-AES128-CBC-SHA
  • RSA-AES256-CBC-SHA
  • RSA-AES128-CBC-SHA
  • ECDHE-RSA-3DES-EDE-CBC-SHA
  • RSA-3DES-EDE-CBC-SHA

They’re pretty good for default ciphers, but we really want to avoid 3DES and RSA key exchange (I’m not talking about the certificate here).

Since we enabled ECDSA certificates earlier, we can also remove RSA from our cipher suite.

This is the one I use:

tls {
	ECDHE-ECDSA-WITH-CHACHA20-POLY1305 ECDHE-ECDSA-AES128-GCM-SHA256 ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-ECDSA-AES128-CBC-SHA ECDHE-ECDSA-AES256-CBC-SHA
}

If you use a RSA certificate, you can copy/paste this and replace ECDSA with RSA.

I keep AES CBC for now, for compatibility. I’ll probably remove it in the next few years. Note that HTTP/2 and TLS 1.3 already blacklist it.

ECDHE curve type

By default, Caddy enables X25519 and p256 curves, which is great. I like to support more curves so I added p384 and p521.

tls {
	curves X25519 p521 p384 p256
}

Bonus tip: use import

Tip

Since I have to add this tls {} block in all my vhosts, I just put it in a tls.conf file and then add import tls.conf in all my vhosts.

That’s all! Caddy is a modern and secure-by-default web server, it’s my new favorite thing! Expect more posts about it in the future.