How to configure TLS in Varnish

Varnish has TLS support, as of Varnish version 9. This means incoming connections to Varnish can now happen using HTTPS. Varnish can also communicate with backends using HTTPS.

This tutorial explains the different ways to configure native TLS support.

Option 1: Use the -A option

The varnnishd program offers a -A argument to enable TLS support by linking to a TLS configuration file.

varnishd example

This is what the varnishd command could look like with both regular HTTP and HTTPS enabled:

/usr/sbin/varnishd \
    -a :80 \
    -A /etc/varnish/tls.cfg
...

The -A argument reads the /etc/varnish/tls.cfg file where TLS-related configurations are defined.

TLS config file

This is what that tls.cfg file could look like:

frontend = {
    host = ""
    port = "443"
}

pem-file = "/etc/varnish/certs/mycert.pem"

The frontend directive in the config file opens port 443 for incoming HTTPS requests for all hosts and interfaces.

The pem-file directive references the TLS certificate that should be loaded. The TLS config file can have multiple pem-file entries and Varnish will use Server Name Indication (SNI) to match the right certificate.

You can define pem-file as a config block and split the certificate from the private key and the DH parameters:

pem-file = {
    cert = "/etc/varnish/certs/mycert.pem"
    private-key = "/etc/varnish/certs/myprivkey.pem"
    dhparam = "/etc/varnish/certs/dhparam.pem"
}

The TLS configuration file also offers configuration parameters to configure TLS ciphers, protocols, and other TLS-related settings:

ciphersuites = "TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256"
tls-protos = TLSv1.2 TLSv1.3
ecdh-curve = "X25519:prime256v1:secp384r1"

Option 2: Register an HTTPS listener with -a

Another way to enable support for HTTPS requests in Varnish is by adding an HTTPS listening port using the -a option. The supported protocols for -a are http, https and proxy.

For plain HTTP listening interfaces, the protocol is often not mentioned and defaults to http.

varnishd example

Here’s how you configure an HTTPS listener:

/usr/sbin/varnishd \
    -a :80 \
    -a :443,https
...

If you want a clean and consistent listener definition, you can even add the http protocol to the listener on port 80:

/usr/sbin/varnishd \
    -a :80,http \
    -a :443,https
...

Register certificates with varnishadm

It’s not because you have an HTTPS listener, that TLS is fully configured. You now need to load TLS certificates. This can be done using specific Varnish CLI commands with varnishadm.

Here’s how you load a certificate:

varnishadm tls.cert.load /etc/varnish/cert.pem

When you run varnishadm tls.cert.list, this is the output:

default	cert0	staged	localhost
a1	    cert0	staged	localhost
a1	    cert0	staged	localhost

Here’s how you activate the staged certificates:

varnishadm tls.cert.commit

When you run varnishadm tls.cert.list again, this is the output:

default	cert0	active	localhost
a1	    cert0	active	localhost
a1	    cert0	active	localhost

Now you can access your Varnish server over HTTPS and the certificates will be used.

Changing certificates at runtime

The main benefit of enabling TLS support in Varnish via -a :443,https compared to using -A /etc/varnish/tls.cfg is that certificates can be dynamically added, reloaded, discarded and rolled back at runtime.

This means Varnish does not have to be restarted and the cached content remains intact. Let’s look at a couple of scenarios where certifactes are changed at runtime.

Discard certificates

The tls.cert.discard command discards active certificates and allows ongoing sessions to complete.

Here’s an example:

Let’s assume these are my active certificates when running varnishadm tls.cert.list:

default	cert0	active	localhost
a1	    cert0	active	localhost
a1  	cert0	active	localhost

I can run the following command to discard the certificate with identifier cert0:

varnishadm tls.cert.discard cert0

When you run varnishadm tls.cert.list again, this is the state of the certificate:

default	cert0	discard	localhost
a1	    cert0	discard	localhost
a1  	cert0	discard	localhost

To commit the discard and remove the certificate, simply run varnishadm tls.cert.commit.

Rollback certificates

The tls.cert.rollback command rolls back staged certificates. This is the opposite of tls.cert.commit.

Before the rollback, you first need to load a certificate. We run varnishadm tls.cert.load /etc/varnish/cert.pem to make this happen and the varnishadm tls.cert.list output is the following:

default	cert0	staged	localhost
a1	    cert0	staged	localhost
a1	    cert0	staged	localhost

We can now perform a rollback:

varnishadm tls.cert.rollback

The any staged certificate is now gone and won’t appear in your varnishadm tls.cert.list output.

The rollback command also works for discarded certificates: because it it the opposite of a commit, discarded certificates will become active again.

Reload active certificates

When certificates expire, they need to be reloaded. The tls.cert.reload CLI command atomically reloads all certificates. A successful reload is immediately committed. Each active certificate will be re-read from disk and loaded using the same parameters as the current configuration.

If an error is encountered during a reload, all changes will be rolled back and the previous state will be preserved.

Here’s an example:

varnishadm tls.cert.reload

Reloads can only happen if there are not staged or discarded certificates. You have to commit or roll back these certificates before reloading.

Persisting certificates

Loaded and committed certificates are not persisted. When varnishd gets restarted, your certificates are gone.

You can persist your TLS certificates by storing the tls.cert.load and tls.cert.commit CLI commands in a file and letting Varnish run them automatically on startup using the -I option.

Here’s a file named cli.cfg that contains the Varnish CLI commands that need to run on startup:

start
tls.cert.load /etc/varnish/cert.pem
tls.cert.commit

Please note that the start command is run first to start the Varnish child process.

Here’s an example varnishd command that loads this file via -I:

/usr/sbin/varnishd \
    -a :80,http \
    -a :443,https \
    -I /etc/varnish/cli.cfg
...

Backend TLS

Not only can Varnish communicate with the client over TLS, but also with the backend.

Enabling backend TLS support happens in the backend definition. Here’s an example:

backend default {
	.host = "server.example.com";
	.port = "443";
	.ssl = 1; # Turn on SSL support
}

By adding the .ssl = 1; property to the backend definition, Varnish will communicate use HTTPS to communicate with the backend.