Hello!
For those of you who depend on Varnish to offer robust caching and scaling potential to your web stack, hearing about Google’s prioritization (albeit arguably small, for now) of sites that force SSL may cause pause in how to implement.
Varnish currently doesn’t have the ability to handle SSL certificates and encrypt requests as such. It may never actually have this ability because its focus is to cache content and it does a very good job I might add.
So if Varnish can’t handle the SSL traffic directly, how would you go about implementing this with Nginx?
Well, nginx has the ability to proxy traffic. This is one of the many reasons why some admins choose to pair Varnish with Nginx. Nginx can do reverse proxying and header manipulation out of the box without custom modules or configuration. Combine that with the lightweight production tested scalability of Nginx over Apache and the reasons are simple. We’re not interested in that debate here, just a simple implementation.
Nginx Configuration
With Nginx, you will need to add an SSL listener to handle the ssl traffic. You then assign your certificate. The actual traffic is then proxied to the (already set up) non-https listener (varnish).
server { listen x.x.x.x:443 ssl; server_name yoursite.com www.yoursite.com; ssl_certificate /etc/nginx/ssl/yoursite.com.crt; ssl_certificate_key /etc/nginx/ssl/yoursite.com.key; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers HIGH:!aNULL:!MD5; if ($host !~* ^www.) { rewrite ^(.*)$ https://www.$host$1 permanent; } location / { # Pass the request on to Varnish. proxy_pass http://127.0.0.1:80; # Pass some headers to the downstream server, so it can identify the host. proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # Tell any web apps like Drupal that the session is HTTPS. proxy_set_header X-Forwarded-Proto https; proxy_redirect off; } }
The one thing to note before going further is the second last line of the configuration. That is important because it allows you to avoid an infinite redirect loop of a request proxying to varnish, varnish redirecting non-ssl to ssl and back to nginx for a proxy. You’ll notice that pretty quickly because your site will ultimately go down 🙁
What nginx is doing is defining a custom HTTP header and assigning a value of “https” to it :
proxy_set_header X-Forwarded-Proto https;
So the rest of the nginx configuration can remain the same (the configuration that varnish ultimately forwards requests in order to cache).
Varnish
What you’re going to need in your varnish configuration is a minor adjustment :
if (req.http.X-Forwarded-Proto !~ "(?i)https") { set req.http.x-Redir-Url = "https://www.yoursite.com" + req.url; error 750 req.http.x-Redir-Url; }
What the above snippet is doing is simply checking if the header “X-Forwarded-Proto” (that nginx just set) exists and if the value equals (case insensitive) to “https”. If that is not present or matches , it sets a redirect to force the SSL connection which is handled by the nginx ssl proxy configuration above. Its also important to note that we are not just doing a clean break redirect, we are still appending the originating request URI in order to make it a smooth transition and potentially not break any previously ranked links/urls.
The last thing to note is the error 750 handler that handles the redirect in varnish :
sub vcl_error { if (obj.status == 750) { set obj.http.Location = obj.response; set obj.status = 302; return(deliver); } }
You can see that were using a 302 temporary redirect instead of a permanent 301 redirect. This is your decision though browsers tend to be stubborn in their own internal caching of 301 redirects so 302 is good for testing.
After restarting varnish and nginx you should be able to quickly confirm that no non-SSL traffic is allowed anymore. You can not only enjoy the (marginal) SEO “bump” but you are also contributing to the HTTPS Everywhere movement which is an important cause!