Recommendations/Advice for Web Caching with SSL
I've been using Varnish Cache for a few websites. However, I need advice for implementing HTTPS. I am open to alternatives to Varnish Cache. The configuration of Varnish is relatively complex, so perhaps a less advanced alternative would be better suited for my sites.
Cloudflare looks like a viable option, but from what I could see would need a business plan starting from $200/month. I might be mistaken.
Any recommendations?
Using HTTPS on Varnish isn't that hard. Although Varnish doesn't natively offer TLS, it facilitates TLS termination.
In 2015 Varnish released Hitch, a very powerful TLS proxy that handles terminates TLS and forwards unencrypted HTTP traffic to Varnish.
Installing Hitch
You can download the source from the Hitch website and compile it on our server. If you want to use packages to install Hitch, you can run the following command on Debian or Ubuntu:
apt-get install -y hitch
Configuring Hitch
Once you've installed Hitch, open up /etc/hitch/hitch.conf
and make sure you use the following configuration:
frontend = {
host = "*"
port = "443"
}
backend = "[localhost]:8443"
write-proxy-protocol-v2 = on
pem-file = "/etc/hitch/certs/example.com"
ciphersuites = "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256"
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:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384"
tls-protos = TLSv1.2 TLSv1.3
ecdh-curve = "X25519:prime256v1:secp384r1"
prefer-server-ciphers = false
Please put your certificate in
/etc/hitch/certs
and adjust thepem-file
directive inhitch.conf
.
Reconfiguring Varnish
Your Varnish runtime configuration probably contains the following listening information:
varnish -a :80
This means Varnish is listening for connections on port 80. To make sure HTTPS works, we'll add another listening port, but with a specific configuration:
varnish -a :80 -a :8443,PROXY
Make sure you reload Varnish after changing the runtime settings.
You'll notice that port 8843
is now also assigned to Varnish. It is not used for standard HTTP, but for HTTP using the PROXY protocol. PROXY protocol support was also enabled in Hitch.
This ensures the original client IP address is passed along to Varnish, regardless of the number of extra hops it has to go through. The original client IP address will be automatically stored in the X-Forwarded-For
header by Varnish.
The type of traffic that is processed by port 8443
is HTTP traffic that originated from Hitch and that is in fact terminated HTTPS traffic.
Identifying HTTPS requests
Hitch is a TLS proxy, it doesn't understand HTTP. This means it cannot set the conventional X-Forwarded-Proto
header to indicate what kind of traffic was terminated. Luckily we can track which port was used.
The VCL code below detects HTTP/HTTPS requests and assigns the proper X-Forwarded-Proto
header.
vcl 4.0;
import std;
sub vcl_recv {
if (std.port(local.ip) == 8443) {
set req.http.X-Forwarded-Proto = "https";
} else {
set req.http.X-Forwarded-Proto = "http";
}
}
A lot of frameworks and CMS systems leverage the
X-Forwarded-Proto
header to automatically build the right URL schemes. The fact that we're setting this header in VCL is very helpful.
Protocol-base cache variations
A cache stores HTTP responses, not HTTP requests. So when an object gets stored in cache, Varnish doesn't know if it came from an HTTP or an HTTPS URL. If we start caching them without extra measures, you can end up getting stuck in an infinite redirection loop if Varnish starts caching the HTTP version of an object.
To avoid this, Varnish needs to have a cache variation per protocol. You can easily instruct Varnish to do so by returning the following HTTP response header in your backend application:
Vary: X-Forwarded-Proto
If for some reason this is not possible, you can also extend the vcl_hash
logic in your VCL code. In that case, please add the following snippet:
sub vcl_hash {
hash_data(req.http.X-Forwarded-Proto);
}