Varnish on Magento 2

in Caching

NOTE: Did you order a trial via (by invitation only)? Please be aware Varnish is not included in your trial.

Customers with Hypernode Professional and Excellence plans can use Varnish to boost their Magento shop. This article explains how you can configure Varnish for your Hypernode. Do you have a Magento 1 shop, please check this article.

Although Varnish is extremely awesome when it get’s to speeding up websites, Varnish is a complex technique that needs some experience to set it up. Don’t implement varnish on production nodes but use a development node or a Hypernode Vagrant.

Configure Varnish for Magento 2.x

As Magento 2 supports Varnish out of the box, there is no need for the turpentine extension anymore in Magento 2.
Simply follow the steps below to configure Varnish for Magento 2

Enable Varnish

To get started using Varnish, you first need to enable it in our Service Panel. This will trigger a server configuration update.
A few minutes after enabling Varnish, you’ll be able to use it on your node.

Configure Magento for Varnish

  • Log in to the Magento Admin as an administrator.
  • Navigate to Stores > Configuration > Advanced > System > Full Page Cache
  • From the Caching Application list, click Varnish Caching
  • Enter a TTL value
  • Expand Varnish Configuration and insert the correct information:
    • Backend Host:
    • Backend Port: 8080
  • Save your VCL by clicking the button ‘Save config‘ in the top right
  • Click Export VCL for varnish 4

Configure your backend servers through the commandline

If you want to flush the Varnish cache from the Magento backend, you need to add the Varnish server in your Magento config to http-cache-hosts.

To do this, run the following command:

cd /data/web/magento2
chmod 750 bin/magento
bin/magento setup:config:set --http-cache-hosts=

Now when you flush your caches in cache management, your varnish full_page cache will be flushed too.

Test and upload your VCL

After downloading your VCL, check (in notepad or something similar) if the following configuration is present:

backend default {
  .host = "";
  .port = "8080";

If your VCL checks out, upload it to your Hypernode (using SCP, FTP or FTPS or whichever client you prefer)

Import your VCL into the Varnish daemon

Import your VCL into Varnish and save as mag2:

varnishadm vcl.load mag2 /data/web/default.vcl

The output should say: your VCL is compiled.

Now tell Varnish to activate the loaded VCL:

varnishadm vcl.use mag2

In the examples we used the name ‘mag2’ for our VCL, but you can use any name you prefer.

Test if the correct VCL is uploaded

List all VCL’s with the following command:

varnishadm vcl.list

The VCL you just imported and activated should have the status ‘active’. If all went well, varnish is now functioning with a working VCL.

Now to make sure this VCL is loaded after a reboot, make sure the “boot” vcl is thrown away:

varnishadm vcl.discard boot

Flush your Varnish cache when using Magento 2

To flush the Varnish cache of your Magento store on the command line you can use /data/web/magento2/bin/magento cache:flush full_page
This will purge the Varnish cache of the local Varnish instance.

Additionally you can flush your cache through the Magento admin backend or use varnishadm directly:

varnishadm "ban req.url ~ ."

Or if you want to flush the cache for a single domain in a multisite setup:

varnishadm "ban =="

Warming your cache

Magento 2 doesn’t need to use turpentine anymore, so we can’t use the cache crawler provided with turpentine. As an alternative you can use an extension to warm your cache or the cache warmers we provided on our Github account.

Enable debug headers

If you are implementing Varnish on Magento 2, you might want to view some caching headers that indicate whether the page is cacheable or not.
To do this, put your Magento install in Developer mode. Now if you request a page through curl, you can see the X-Magento-Cache-Debug header:

curl -I -v --location-trusted '' > /dev/null | grep X-Magento

Which will show the debug headers:

X-Magento-Cache-Control: max-age=86400, public, s-maxage=86400
X-Magento-Cache-Debug: MISS


  • If your Varnish setup is not working over SSL, check this article
  • There is a bug when Varnish is activated on Magento 2.2.0, resulting in a “503 backend fetch” error. Please see Magento Github issue 10165. For now, we advise you to either wait with upgrading to Magento 2.2.0 when using Varnish until this bug is fixed or use an adjusted .vcl as a temporary workaround:
vcl 4.0;

import std;
# The minimal Varnish version is 4.0
# For SSL offloading, pass the following header in your proxy server or load balancer: 'X-Forwarded-Proto: https'

backend default {
    .host = "";
    .port = "8080";

acl purge {

sub vcl_recv {
    if (req.method == "PURGE") {
        if (client.ip !~ purge) {
            return (synth(405, "Method not allowed"));
        if (!req.http.X-Magento-Tags-Pattern) {
            return (synth(400, "X-Magento-Tags-Pattern header required"));
        ban("obj.http.X-Magento-Tags ~ " + req.http.X-Magento-Tags-Pattern);
        return (synth(200, "Purged"));

    if (req.method != "GET" &&
        req.method != "HEAD" &&
        req.method != "PUT" &&
        req.method != "POST" &&
        req.method != "TRACE" &&
        req.method != "OPTIONS" &&
        req.method != "DELETE") {
          /* Non-RFC2616 or CONNECT which is weird. */
          return (pipe);

    # We only deal with GET and HEAD by default
    if (req.method != "GET" && req.method != "HEAD") {
        return (pass);

    # Bypass shopping cart, checkout and search requests
    if (req.url ~ "/checkout" || req.url ~ "/catalogsearch") {
        return (pass);

    # normalize url in case of leading HTTP scheme and domain
    set req.url = regsub(req.url, "^http[s]?://", "");

    # collect all cookies

    # Compression filter. See
    if (req.http.Accept-Encoding) {
        if (req.url ~ "\.(jpg|jpeg|png|gif|gz|tgz|bz2|tbz|mp3|ogg|swf|flv)$") {
            # No point in compressing these
            unset req.http.Accept-Encoding;
        } elsif (req.http.Accept-Encoding ~ "gzip") {
            set req.http.Accept-Encoding = "gzip";
        } elsif (req.http.Accept-Encoding ~ "deflate" && req.http.user-agent !~ "MSIE") {
            set req.http.Accept-Encoding = "deflate";
        } else {
            # unkown algorithm
            unset req.http.Accept-Encoding;

    # Remove Google gclid parameters to minimize the cache objects
    set req.url = regsuball(req.url,"\?gclid=[^&]+$",""); # strips when QS = "?gclid=AAA"
    set req.url = regsuball(req.url,"\?gclid=[^&]+&","?"); # strips when QS = "?gclid=AAA&foo=bar"
    set req.url = regsuball(req.url,"&gclid=[^&]+",""); # strips when QS = "?foo=bar&gclid=AAA" or QS = "?foo=bar&gclid=AAA&bar=baz"

    # static files are always cacheable. remove SSL flag and cookie
        if (req.url ~ "^/(pub/)?(media|static)/.*\.(ico|css|js|jpg|jpeg|png|gif|tiff|bmp|mp3|ogg|svg|swf|woff|woff2|eot|ttf|otf)$") {
        unset req.http.Https;
        unset req.http.X-Forwarded-Proto;
        unset req.http.Cookie;

    return (hash);

sub vcl_hash {
    if (req.http.cookie ~ "X-Magento-Vary=") {
        hash_data(regsub(req.http.cookie, "^.*?X-Magento-Vary=([^;]+);*.*$", "\1"));

    # For multi site configurations to not cache each other's content
    if ( {
    } else {

    # To make sure HTTP users don't see SSL warning
    if (req.http.X-Forwarded-Proto) {
    /* {{ design_exceptions_code }} */

sub vcl_backend_response {
    if (beresp.http.content-type ~ "text") {
        set beresp.do_esi = true;

    if (bereq.url ~ "\.js$" || beresp.http.content-type ~ "text") {
        set beresp.do_gzip = true;

    # cache only successfully responses and 404s
    if (beresp.status != 200 && beresp.status != 404) {
        set beresp.ttl = 0s;
        set beresp.uncacheable = true;
        return (deliver);
    } elsif (beresp.http.Cache-Control ~ "private") {
        set beresp.uncacheable = true;
        set beresp.ttl = 86400s;
        return (deliver);

    if (beresp.http.X-Magento-Debug) {
        set beresp.http.X-Magento-Cache-Control = beresp.http.Cache-Control;

    # validate if we need to cache it and prevent from setting cookie
    # images, css and js are cacheable by default so we have to remove cookie also
    if (beresp.ttl > 0s && (bereq.method == "GET" || bereq.method == "HEAD")) {
        unset beresp.http.set-cookie;
        if (bereq.url !~ "\.(ico|css|js|jpg|jpeg|png|gif|tiff|bmp|gz|tgz|bz2|tbz|mp3|ogg|svg|swf|woff|woff2|eot|ttf|otf)(\?|$)") {
            set beresp.http.Pragma = "no-cache";
            set beresp.http.Expires = "-1";
            set beresp.http.Cache-Control = "no-store, no-cache, must-revalidate, max-age=0";
            set beresp.grace = 1m;

   # If page is not cacheable then bypass Varnish for 2 minutes as Hit-For-Pass
   if (beresp.ttl <= 0s ||
        beresp.http.Surrogate-control ~ "no-store" ||
        (!beresp.http.Surrogate-Control && beresp.http.Vary == "*")) {
        # Mark as Hit-For-Pass for the next 2 minutes
        set beresp.ttl = 120s;
        set beresp.uncacheable = true;
    return (deliver);

sub vcl_deliver {
    if (resp.http.X-Magento-Debug) {
        if (resp.http.x-varnish ~ " ") {
            set resp.http.X-Magento-Cache-Debug = "HIT";
        } else {
            set resp.http.X-Magento-Cache-Debug = "MISS";
    } else {
        unset resp.http.Age;

    unset resp.http.X-Magento-Debug;
    unset resp.http.X-Magento-Tags;
    unset resp.http.X-Powered-By;
    unset resp.http.Server;
    unset resp.http.X-Varnish;
    unset resp.http.Via;
    unset resp.http.Link;