add back first_byte_timeout and between_bytes_timeout to match openshift haproxy.

vcl 4.0;

import std;
import dynamic;

# set backend default
backend default {
  .host = "${VARNISH_BACKEND_HOST:-nginx}";
  .port = "${VARNISH_BACKEND_PORT:-8080}";
  .first_byte_timeout = 35m;
  .between_bytes_timeout = 10m;
}

# Allow purging from localhost
# @TODO allow from openshift network
acl purge {
      "127.0.0.1";
      "10.0.0.0"/8;
      "172.16.0.0"/12;
      "192.168.0.0"/16;
}

sub vcl_init {
    new www_dir = dynamic.director(
      port = "${VARNISH_BACKEND_PORT:-8080}",
      first_byte_timeout = 90s,
      between_bytes_timeout = 90s,
      whitelist = purge,
      ttl = 60s);
   }

# This configuration is optimized for Drupal hosting:
# Respond to incoming requests.
sub vcl_recv {

  if (req.url ~ "^/varnish_status$")  {
    return (synth(200,"OK"));
  }
  # set the backend, which should be used:
  set req.backend_hint = www_dir.backend("${VARNISH_BACKEND_HOST:-nginx}");

  # Always set the forward ip.
   if (req.restarts == 0) {
     if (req.http.x-forwarded-for) {
         set req.http.X-Forwarded-For =  req.http.X-Forwarded-For + ", " + client.ip;
     } else {
         set req.http.X-Forwarded-For = client.ip;
     }
   }

  if (req.http.X-LAGOON-VARNISH ) {
    ## Pass all Requests which are handled via an upstream Varnish
    set req.http.X-LAGOON-VARNISH = "${HOSTNAME}-${LAGOON_GIT_BRANCH:-undef}-${LAGOON_PROJECT}, " + req.http.X-LAGOON-VARNISH;
    set req.http.X-LAGOON-VARNISH-BYPASS = "true";
  } else if (req.http.Fastly-FF) {
    ## Pass all Requests which are handled via Fastly
    set req.http.X-LAGOON-VARNISH = "${HOSTNAME}-${LAGOON_GIT_BRANCH:-undef}-${LAGOON_PROJECT}, fastly";
    set req.http.X-LAGOON-VARNISH-BYPASS = "true";
    set req.http.X-Forwarded-For = req.http.Fastly-Client-IP;
  } else if (req.http.CF-RAY) {
    ## Pass all Requests which are handled via CloudFlare
    set req.http.X-LAGOON-VARNISH = "${HOSTNAME}-${LAGOON_GIT_BRANCH:-undef}-${LAGOON_PROJECT}, cloudflare";
    set req.http.X-LAGOON-VARNISH-BYPASS = "true";
    set req.http.X-Forwarded-For = req.http.CF-Connecting-IP;
  } else if (req.http.X-Pull) {
    ## Pass all Requests which are handled via KeyCDN
    set req.http.X-LAGOON-VARNISH = "${HOSTNAME}-${LAGOON_GIT_BRANCH:-undef}-${LAGOON_PROJECT}, keycdn";
    set req.http.X-LAGOON-VARNISH-BYPASS = "true";
  } else {
    ## We set a header to let a Varnish Chain know that it already has been varnishcached
    set req.http.X-LAGOON-VARNISH = "${HOSTNAME}-${LAGOON_GIT_BRANCH:-undef}-${LAGOON_PROJECT}";

    ## Allow to bypass based on env variable `VARNISH_BYPASS`
    set req.http.X-LAGOON-VARNISH-BYPASS = "${VARNISH_BYPASS:-false}";
  }

  # Websockets are piped
  if (req.http.Upgrade ~ "(?i)websocket") {
      return (pipe);
  }

  if (req.http.X-LAGOON-VARNISH-BYPASS == "true" || req.http.X-LAGOON-VARNISH-BYPASS == "TRUE") {
    return (pass);
  }

  # SA-CORE-2014-004 preventing access to /xmlrpc.php
  if (req.url ~ "^/xmlrpc.php$") {
    return (synth(701, "Unauthorized"));
  }

  # Strip out Google Analytics campaign variables. They are only needed
  # by the javascript running on the page
  # utm_source, utm_medium, utm_campaign, gclid
   if(req.url ~ "(\?|&)(gclid|utm_[a-z]+)=") {
     set req.url = regsuball(req.url, "(gclid|utm_[a-z]+)=[^\&]+&?", "");
     set req.url = regsub(req.url, "(\?|&)$", "");
   }

  # Bypass a cache hit (the request is still sent to the backend)
  if (req.method == "REFRESH") {
      if (!client.ip ~ purge) { return (synth(405, "Not allowed")); }
      set req.method = "GET";
      set req.hash_always_miss = true;
  }

  # Only allow BAN requests from IP addresses in the 'purge' ACL.
  if (req.method == "BAN" || req.method == "URIBAN" || req.method == "PURGE") {
      # Only allow BAN from defined ACL
      if (!client.ip ~ purge) {
          return (synth(403, "Your IP is not allowed."));
      }

      # Only allows BAN if the Host Header has the style of with "${SERVICE_NAME:-varnish}:8080" or "${SERVICE_NAME:-varnish}".
      # Such a request is only possible from within the Docker network, as a request from external goes trough the Kubernetes Router and for that needs a proper Host Header
      if (!req.http.host ~ "^${SERVICE_NAME:-varnish}(:\d+)?$") {
          return (synth(403, "Only allowed from within own network."));
      }

      if (req.method == "BAN") {
        # Logic for the ban, using the Cache-Tags header.
        if (req.http.Cache-Tags) {
            ban("obj.http.Cache-Tags ~ " + req.http.Cache-Tags);
            # Throw a synthetic page so the request won't go to the backend.
            return (synth(200, "Ban added."));
        }
        else {
            return (synth(403, "Cache-Tags header missing."));
        }
      }

      if (req.method == "URIBAN" || req.method == "PURGE") {
        ban("req.url ~ " + req.url);
        # Throw a synthetic page so the request won't go to the backend.
        return (synth(200, "Ban added."));
      }

  }

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

  # We only try to cache  GET and HEAD, other things are passed
  if (req.method != "GET" && req.method != "HEAD") {
    return (pass);
  }

  # Any requests with Basic Auth are passed
  if (req.http.Authorization || req.http.Authenticate)
  {
    return (pass);
  }

  ## Pass requests which are from blackfire
  if (req.http.X-Blackfire-Query) {
    return (pass);
  }

  # Some URLs should never be cached
  if (req.url ~ "^/status\.php$" ||
      req.url ~ "^/update\.php$" ||
      req.url ~ "^/admin([/?]|$).*$" ||
      req.url ~ "^/info([/?]|$).*$" ||
      req.url ~ "^/flag([/?]|$).*$" ||
      req.url ~ "^.*/system/files([/?]|$).*$" ||
      req.url ~ "^/cgi" ||
      req.url ~ "^/cgi-bin"
  ) {
    return (pass);
  }

  # Plupload likes to get piped
  if (req.url ~ "^.*/plupload-handle-uploads.*$"
  ) {
    return (pipe);
  }

  # Handle compression correctly. Different browsers send different
  # "Accept-Encoding" headers, even though they mostly all support the same
  # compression mechanisms. By consolidating these compression headers into
  # a consistent format, we can reduce the size of the cache and get more hits.=
  # @see: http:// varnish.projects.linpro.no/wiki/FAQ/Compression
  if (req.http.Accept-Encoding) {
    if (req.http.Accept-Encoding ~ "gzip") {
      # If the browser supports it, we'll use gzip.
      set req.http.Accept-Encoding = "gzip";
    }
    else if (req.http.Accept-Encoding ~ "deflate") {
      # Next, try deflate if it is supported.
      set req.http.Accept-Encoding = "deflate";
    }
    else {
      # Unknown algorithm. Remove it and send unencoded.
      unset req.http.Accept-Encoding;
    }
  }

  # Always cache the following file types for all users.
  if (req.url ~ "(?i)\.(css|js|jpg|jpeg|gif|ico|png|tiff|tif|img|tga|wmf|swf|html|htm|woff|woff2|mp4|ttf|eot|svg)(\?.*)?$") {
    unset req.http.Cookie;
  }

  # Remove all cookies that Drupal doesn't need to know about. ANY remaining
  # cookie will cause the request to pass-through to a backend. For the most part
  # we always set the NO_CACHE cookie after any POST request, disabling the
  # Varnish cache temporarily. The session cookie allows all authenticated users
  # to pass through as long as they're logged in.
  #
  # 1. Append a semi-colon to the front of the cookie string.
  # 2. Remove all spaces that appear after semi-colons.
  # 3. Match the cookies we want to keep, adding the space we removed
  #    previously, back. (\1) is first matching group in the regsuball.
  # 4. Remove all other cookies, identifying them by the fact that they have
  #    no space after the preceding semi-colon.
  # 5. Remove all spaces and semi-colons from the beginning and end of the
  #    cookie string.
  if (req.http.Cookie) {
    set req.http.CookieCheck = ";" + req.http.Cookie;
    set req.http.CookieCheck = regsuball(req.http.CookieCheck, "; +", ";");
    set req.http.CookieCheck = regsuball(req.http.CookieCheck, ";(${VARNISH_COOKIECHECK:-SESS[a-z0-9]+|SSESS[a-z0-9]+|NO_CACHE})=", "; \1=");
    set req.http.CookieCheck = regsuball(req.http.CookieCheck, ";[^ ][^;]*", "");
    set req.http.CookieCheck = regsuball(req.http.CookieCheck, "^[; ]+|[; ]+$", "");

    set req.http.Cookie = req.http.Cookie + ";";

    if (req.http.CookieCheck == "") {
      # If there are no remaining cookies, remove the cookie header. If there
      # aren't any cookie headers, Varnish's default behavior will be to cache
      # the page.

      unset req.http.CookieCheck;
      unset req.http.Cookie;
    }
    else {
      # If there is any cookies left (a session or NO_CACHE cookie), do not
      # cache the page. Pass it on to Apache directly.
      unset req.http.CookieCheck;
      return (pass);
    }
  }

  # Cacheable, lookup in cache.
  return (hash);
}

sub vcl_pipe {
  # Support for Websockets
  if (req.http.upgrade) {
      set bereq.http.upgrade = req.http.upgrade;
      set bereq.http.connection = req.http.connection;
  }
}

sub vcl_hit {
    if (obj.ttl >= 0s) {
        # normal hit
        return (deliver);
    }
    # We have no fresh fish. Lets look at the stale ones.
    if (std.healthy(req.backend_hint)) {
        # Backend is healthy. If the object is not older then 30secs, deliver it to the client
        # and automatically create a separate backend request to warm the cache for this request.
        if (obj.ttl + 30s > 0s) {
            set req.http.grace = "normal(limited)";
            return (deliver);
        } else {
            # No candidate for grace. Fetch a fresh object.
            return(miss);
        }
    } else {
        # backend is sick - use full grace
        if (obj.ttl + obj.grace > 0s) {
            set req.http.grace = "full";
            return (deliver);
        } else {
            # no graced object.
            return (miss);
        }
    }
}

sub vcl_backend_response {
  # Allow items to be stale if needed.
  set beresp.grace = 6h;

  # Set ban-lurker friendly custom headers.
  set beresp.http.X-Url = bereq.url;
  set beresp.http.X-Host = bereq.http.host;

  # If the backend sends a X-LAGOON-VARNISH-BACKEND-BYPASS header we directly deliver
  if(beresp.http.X-LAGOON-VARNISH-BACKEND-BYPASS == "TRUE") {
    return (deliver);
  }

  # Cache 404 and 403 for 10 seconds
  if(beresp.status == 404 || beresp.status == 403) {
    set beresp.ttl = 10s;
    return (deliver);
  }

  # Don't allow static files to set cookies.
  if (bereq.url ~ "(?i)\.(css|js|jpg|jpeg|gif|ico|png|tiff|tif|img|tga|wmf|swf|html|htm|woff|woff2|mp4|ttf|eot|svg)(\?.*)?$") {
    # beresp == Back-end response from the web server.
    unset beresp.http.set-cookie;
    unset beresp.http.Cache-Control;

    # If an asset would come back with statuscode 500 we only cache it for 10 seconds instead of the usual static file cache
    if (beresp.status == 500) {
      set beresp.ttl = 10s;
      return (deliver);
    }

    set beresp.ttl = ${VARNISH_ASSETS_TTL:-2628001}s;
    set beresp.http.Cache-Control = "public, max-age=${VARNISH_ASSETS_TTL:-2628001}";
    set beresp.http.Expires = "" + (now + beresp.ttl);
  }

  return (deliver);
}

# Set a header to track a cache HIT/MISS.
sub vcl_deliver {
  if (obj.hits > 0) {
    set resp.http.X-Varnish-Cache = "HIT";
  }
  else {
    set resp.http.X-Varnish-Cache = "MISS";
  }

  # Remove ban-lurker friendly custom headers when delivering to client.
  unset resp.http.X-Url;
  unset resp.http.X-Host;

  # unset Cache-Tags Header by default, can be disabled with VARNISH_UNSET_HEADER_CACHE_TAGS=true
  if (!${VARNISH_UNSET_HEADER_CACHE_TAGS:-false}) {
    unset resp.http.Cache-Tags;
  }

  unset resp.http.X-Generator;
  unset resp.http.Server;
  # Inject information about grace
  if (req.http.grace) {
    set resp.http.X-Varnish-Grace = req.http.grace;
  }
  set resp.http.X-LAGOON = "${HOSTNAME}-${LAGOON_GIT_BRANCH:-undef}-${LAGOON_PROJECT}>" + resp.http.X-LAGOON;
  return (deliver);
}

sub vcl_hash {
     hash_data(req.url);
     if (req.http.host) {
         hash_data(req.http.host);
     } else {
         hash_data(server.ip);
     }
     if (req.http.X-Forwarded-Proto) {
        hash_data(req.http.X-Forwarded-Proto);
     }
     if (req.http.HTTPS) {
        hash_data(req.http.HTTPS);
     }
  return (lookup);
}

sub vcl_synth {
  if (resp.status == 701) {
    set resp.status = 401;
    set resp.http.Content-Type = "text/plain; charset=utf-8";
    synthetic({"XMLRPC Interface is blocked due to SA-CORE-2014-004 - mail support@amazee.io if you need it."});
    return (deliver);
  }
  if (resp.status == 700) {
    # Set a status the client will understand
    set resp.status = 200;
    # Create our synthetic response
    synthetic("");
    return(deliver);
}
  return (deliver);
}

sub vcl_backend_error {
    # Restart the request, when we have a backend server error, to try another backend.
    # Restart max twice.
    if (bereq.retries < 2) {
      return(retry);
    }

    set beresp.http.Content-Type = "text/html; charset=utf-8";
    set beresp.http.Retry-After = "5";
    synthetic( {"
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

    <title>Server Error</title>

We are sorry...

We encountered a server-side error. This means that the problem is not with your computer or Internet connection, but rather with the website's server.

We are currently working on solving this problem and apologise for the inconvenience.

            Technical Information

            Error "} + beresp.status + " " + beresp.reason + {"

            XID: "} + bereq.xid + {"

"} );
    return (deliver);
}

vcl 4.0;

import std;
import dynamic;

# set backend default
backend default {
  .host = "${VARNISH_BACKEND_HOST:-nginx}";
  .port = "${VARNISH_BACKEND_PORT:-8080}";
  .first_byte_timeout = 35m;
  .between_bytes_timeout = 10m;
}

# Allow purging from localhost
# @TODO allow from openshift network
acl purge {
      "127.0.0.1";
      "10.0.0.0"/8;
      "172.16.0.0"/12;
      "192.168.0.0"/16;
}

sub vcl_init {
    new www_dir = dynamic.director(
      port = "${VARNISH_BACKEND_PORT:-8080}",
      whitelist = purge,
      ttl = 60s);
   }

# This configuration is optimized for Drupal hosting:
# Respond to incoming requests.
sub vcl_recv {

  if (req.url ~ "^/varnish_status$")  {
    return (synth(200,"OK"));
  }
  # set the backend, which should be used:
  set req.backend_hint = www_dir.backend("${VARNISH_BACKEND_HOST:-nginx}");

  # Always set the forward ip.
   if (req.restarts == 0) {
     if (req.http.x-forwarded-for) {
         set req.http.X-Forwarded-For =  req.http.X-Forwarded-For + ", " + client.ip;
     } else {
         set req.http.X-Forwarded-For = client.ip;
     }
   }

  if (req.http.X-LAGOON-VARNISH ) {
    ## Pass all Requests which are handled via an upstream Varnish
    set req.http.X-LAGOON-VARNISH = "${HOSTNAME}-${LAGOON_GIT_BRANCH:-undef}-${LAGOON_PROJECT}, " + req.http.X-LAGOON-VARNISH;
    set req.http.X-LAGOON-VARNISH-BYPASS = "true";
  } else if (req.http.Fastly-FF) {
    ## Pass all Requests which are handled via Fastly
    set req.http.X-LAGOON-VARNISH = "${HOSTNAME}-${LAGOON_GIT_BRANCH:-undef}-${LAGOON_PROJECT}, fastly";
    set req.http.X-LAGOON-VARNISH-BYPASS = "true";
    set req.http.X-Forwarded-For = req.http.Fastly-Client-IP;
  } else if (req.http.CF-RAY) {
    ## Pass all Requests which are handled via CloudFlare
    set req.http.X-LAGOON-VARNISH = "${HOSTNAME}-${LAGOON_GIT_BRANCH:-undef}-${LAGOON_PROJECT}, cloudflare";
    set req.http.X-LAGOON-VARNISH-BYPASS = "true";
    set req.http.X-Forwarded-For = req.http.CF-Connecting-IP;
  } else if (req.http.X-Pull) {
    ## Pass all Requests which are handled via KeyCDN
    set req.http.X-LAGOON-VARNISH = "${HOSTNAME}-${LAGOON_GIT_BRANCH:-undef}-${LAGOON_PROJECT}, keycdn";
    set req.http.X-LAGOON-VARNISH-BYPASS = "true";
  } else {
    ## We set a header to let a Varnish Chain know that it already has been varnishcached
    set req.http.X-LAGOON-VARNISH = "${HOSTNAME}-${LAGOON_GIT_BRANCH:-undef}-${LAGOON_PROJECT}";

    ## Allow to bypass based on env variable `VARNISH_BYPASS`
    set req.http.X-LAGOON-VARNISH-BYPASS = "${VARNISH_BYPASS:-false}";
  }

  # Websockets are piped
  if (req.http.Upgrade ~ "(?i)websocket") {
      return (pipe);
  }

  if (req.http.X-LAGOON-VARNISH-BYPASS == "true" || req.http.X-LAGOON-VARNISH-BYPASS == "TRUE") {
    return (pass);
  }

  # SA-CORE-2014-004 preventing access to /xmlrpc.php
  if (req.url ~ "^/xmlrpc.php$") {
    return (synth(701, "Unauthorized"));
  }

  # Strip out Google Analytics campaign variables. They are only needed
  # by the javascript running on the page
  # utm_source, utm_medium, utm_campaign, gclid
   if(req.url ~ "(\?|&)(gclid|utm_[a-z]+)=") {
     set req.url = regsuball(req.url, "(gclid|utm_[a-z]+)=[^\&]+&?", "");
     set req.url = regsub(req.url, "(\?|&)$", "");
   }

  # Bypass a cache hit (the request is still sent to the backend)
  if (req.method == "REFRESH") {
      if (!client.ip ~ purge) { return (synth(405, "Not allowed")); }
      set req.method = "GET";
      set req.hash_always_miss = true;
  }

  # Only allow BAN requests from IP addresses in the 'purge' ACL.
  if (req.method == "BAN" || req.method == "URIBAN" || req.method == "PURGE") {
      # Only allow BAN from defined ACL
      if (!client.ip ~ purge) {
          return (synth(403, "Your IP is not allowed."));
      }

      # Only allows BAN if the Host Header has the style of with "${SERVICE_NAME:-varnish}:8080" or "${SERVICE_NAME:-varnish}".
      # Such a request is only possible from within the Docker network, as a request from external goes trough the Kubernetes Router and for that needs a proper Host Header
      if (!req.http.host ~ "^${SERVICE_NAME:-varnish}(:\d+)?$") {
          return (synth(403, "Only allowed from within own network."));
      }

      if (req.method == "BAN") {
        # Logic for the ban, using the Cache-Tags header.
        if (req.http.Cache-Tags) {
            ban("obj.http.Cache-Tags ~ " + req.http.Cache-Tags);
            # Throw a synthetic page so the request won't go to the backend.
            return (synth(200, "Ban added."));
        }
        else {
            return (synth(403, "Cache-Tags header missing."));
        }
      }

      if (req.method == "URIBAN" || req.method == "PURGE") {
        ban("req.url ~ " + req.url);
        # Throw a synthetic page so the request won't go to the backend.
        return (synth(200, "Ban added."));
      }

  }

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

  # We only try to cache  GET and HEAD, other things are passed
  if (req.method != "GET" && req.method != "HEAD") {
    return (pass);
  }

  # Any requests with Basic Auth are passed
  if (req.http.Authorization || req.http.Authenticate)
  {
    return (pass);
  }

  ## Pass requests which are from blackfire
  if (req.http.X-Blackfire-Query) {
    return (pass);
  }

  # Some URLs should never be cached
  if (req.url ~ "^/status\.php$" ||
      req.url ~ "^/update\.php$" ||
      req.url ~ "^/admin([/?]|$).*$" ||
      req.url ~ "^/info([/?]|$).*$" ||
      req.url ~ "^/flag([/?]|$).*$" ||
      req.url ~ "^.*/system/files([/?]|$).*$" ||
      req.url ~ "^/cgi" ||
      req.url ~ "^/cgi-bin"
  ) {
    return (pass);
  }

  # Plupload likes to get piped
  if (req.url ~ "^.*/plupload-handle-uploads.*$"
  ) {
    return (pipe);
  }

  # Handle compression correctly. Different browsers send different
  # "Accept-Encoding" headers, even though they mostly all support the same
  # compression mechanisms. By consolidating these compression headers into
  # a consistent format, we can reduce the size of the cache and get more hits.=
  # @see: http:// varnish.projects.linpro.no/wiki/FAQ/Compression
  if (req.http.Accept-Encoding) {
    if (req.http.Accept-Encoding ~ "gzip") {
      # If the browser supports it, we'll use gzip.
      set req.http.Accept-Encoding = "gzip";
    }
    else if (req.http.Accept-Encoding ~ "deflate") {
      # Next, try deflate if it is supported.
      set req.http.Accept-Encoding = "deflate";
    }
    else {
      # Unknown algorithm. Remove it and send unencoded.
      unset req.http.Accept-Encoding;
    }
  }

  # Always cache the following file types for all users.
  if (req.url ~ "(?i)\.(css|js|jpg|jpeg|gif|ico|png|tiff|tif|img|tga|wmf|swf|html|htm|woff|woff2|mp4|ttf|eot|svg)(\?.*)?$") {
    unset req.http.Cookie;
  }

  # Remove all cookies that Drupal doesn't need to know about. ANY remaining
  # cookie will cause the request to pass-through to a backend. For the most part
  # we always set the NO_CACHE cookie after any POST request, disabling the
  # Varnish cache temporarily. The session cookie allows all authenticated users
  # to pass through as long as they're logged in.
  #
  # 1. Append a semi-colon to the front of the cookie string.
  # 2. Remove all spaces that appear after semi-colons.
  # 3. Match the cookies we want to keep, adding the space we removed
  #    previously, back. (\1) is first matching group in the regsuball.
  # 4. Remove all other cookies, identifying them by the fact that they have
  #    no space after the preceding semi-colon.
  # 5. Remove all spaces and semi-colons from the beginning and end of the
  #    cookie string.
  if (req.http.Cookie) {
    set req.http.CookieCheck = ";" + req.http.Cookie;
    set req.http.CookieCheck = regsuball(req.http.CookieCheck, "; +", ";");
    set req.http.CookieCheck = regsuball(req.http.CookieCheck, ";(${VARNISH_COOKIECHECK:-SESS[a-z0-9]+|SSESS[a-z0-9]+|NO_CACHE})=", "; \1=");
    set req.http.CookieCheck = regsuball(req.http.CookieCheck, ";[^ ][^;]*", "");
    set req.http.CookieCheck = regsuball(req.http.CookieCheck, "^[; ]+|[; ]+$", "");

    set req.http.Cookie = req.http.Cookie + ";";

    if (req.http.CookieCheck == "") {
      # If there are no remaining cookies, remove the cookie header. If there
      # aren't any cookie headers, Varnish's default behavior will be to cache
      # the page.

      unset req.http.CookieCheck;
      unset req.http.Cookie;
    }
    else {
      # If there is any cookies left (a session or NO_CACHE cookie), do not
      # cache the page. Pass it on to Apache directly.
      unset req.http.CookieCheck;
      return (pass);
    }
  }

  # Cacheable, lookup in cache.
  return (hash);
}

sub vcl_pipe {
  # Support for Websockets
  if (req.http.upgrade) {
      set bereq.http.upgrade = req.http.upgrade;
      set bereq.http.connection = req.http.connection;
  }
}

sub vcl_hit {
    if (obj.ttl >= 0s) {
        # normal hit
        return (deliver);
    }
    # We have no fresh fish. Lets look at the stale ones.
    if (std.healthy(req.backend_hint)) {
        # Backend is healthy. If the object is not older then 30secs, deliver it to the client
        # and automatically create a separate backend request to warm the cache for this request.
        if (obj.ttl + 30s > 0s) {
            set req.http.grace = "normal(limited)";
            return (deliver);
        } else {
            # No candidate for grace. Fetch a fresh object.
            return(miss);
        }
    } else {
        # backend is sick - use full grace
        if (obj.ttl + obj.grace > 0s) {
            set req.http.grace = "full";
            return (deliver);
        } else {
            # no graced object.
            return (miss);
        }
    }
}

sub vcl_backend_response {
  # Allow items to be stale if needed.
  set beresp.grace = 6h;

  # Set ban-lurker friendly custom headers.
  set beresp.http.X-Url = bereq.url;
  set beresp.http.X-Host = bereq.http.host;

  # If the backend sends a X-LAGOON-VARNISH-BACKEND-BYPASS header we directly deliver
  if(beresp.http.X-LAGOON-VARNISH-BACKEND-BYPASS == "TRUE") {
    return (deliver);
  }

  # Cache 404 and 403 for 10 seconds
  if(beresp.status == 404 || beresp.status == 403) {
    set beresp.ttl = 10s;
    return (deliver);
  }

  # Don't allow static files to set cookies.
  if (bereq.url ~ "(?i)\.(css|js|jpg|jpeg|gif|ico|png|tiff|tif|img|tga|wmf|swf|html|htm|woff|woff2|mp4|ttf|eot|svg)(\?.*)?$") {
    # beresp == Back-end response from the web server.
    unset beresp.http.set-cookie;
    unset beresp.http.Cache-Control;

    # If an asset would come back with statuscode 500 we only cache it for 10 seconds instead of the usual static file cache
    if (beresp.status == 500) {
      set beresp.ttl = 10s;
      return (deliver);
    }

    set beresp.ttl = ${VARNISH_ASSETS_TTL:-2628001}s;
    set beresp.http.Cache-Control = "public, max-age=${VARNISH_ASSETS_TTL:-2628001}";
    set beresp.http.Expires = "" + (now + beresp.ttl);
  }

  return (deliver);
}

# Set a header to track a cache HIT/MISS.
sub vcl_deliver {
  if (obj.hits > 0) {
    set resp.http.X-Varnish-Cache = "HIT";
  }
  else {
    set resp.http.X-Varnish-Cache = "MISS";
  }

  # Remove ban-lurker friendly custom headers when delivering to client.
  unset resp.http.X-Url;
  unset resp.http.X-Host;

  # unset Cache-Tags Header by default, can be disabled with VARNISH_UNSET_HEADER_CACHE_TAGS=true
  if (!${VARNISH_UNSET_HEADER_CACHE_TAGS:-false}) {
    unset resp.http.Cache-Tags;
  }

  unset resp.http.X-Generator;
  unset resp.http.Server;
  # Inject information about grace
  if (req.http.grace) {
    set resp.http.X-Varnish-Grace = req.http.grace;
  }
  set resp.http.X-LAGOON = "${HOSTNAME}-${LAGOON_GIT_BRANCH:-undef}-${LAGOON_PROJECT}>" + resp.http.X-LAGOON;
  return (deliver);
}

sub vcl_hash {
     hash_data(req.url);
     if (req.http.host) {
         hash_data(req.http.host);
     } else {
         hash_data(server.ip);
     }
     if (req.http.X-Forwarded-Proto) {
        hash_data(req.http.X-Forwarded-Proto);
     }
     if (req.http.HTTPS) {
        hash_data(req.http.HTTPS);
     }
  return (lookup);
}

sub vcl_synth {
  if (resp.status == 701) {
    set resp.status = 401;
    set resp.http.Content-Type = "text/plain; charset=utf-8";
    synthetic({"XMLRPC Interface is blocked due to SA-CORE-2014-004 - mail support@amazee.io if you need it."});
    return (deliver);
  }
  if (resp.status == 700) {
    # Set a status the client will understand
    set resp.status = 200;
    # Create our synthetic response
    synthetic("");
    return(deliver);
}
  return (deliver);
}

sub vcl_backend_error {
    # Restart the request, when we have a backend server error, to try another backend.
    # Restart max twice.
    if (bereq.retries < 2) {
      return(retry);
    }

    set beresp.http.Content-Type = "text/html; charset=utf-8";
    set beresp.http.Retry-After = "5";
    synthetic( {"
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

    <title>Server Error</title>

We are sorry...

We encountered a server-side error. This means that the problem is not with your computer or Internet connection, but rather with the website's server.

We are currently working on solving this problem and apologise for the inconvenience.

            Technical Information

            Error "} + beresp.status + " " + beresp.reason + {"

            XID: "} + bereq.xid + {"

"} );
    return (deliver);
}


update varnish config

update varnish config

# This is a basic VCL configuration file for varnish.  See the vcl(7)
# man page for details on VCL syntax and semantics.
#
# Default backend definition.  Set this to point to your content
# server.
#
backend default {
    .host = "127.0.0.1";
    .port = "8888";
}

acl purge {
     "localhost";
     "172.16.0.0"/24;
}

#
# Below is a commented-out copy of the default VCL logic.  If you
# redefine any of these subroutines, the built-in logic will be
# appended to your code.
#
 sub vcl_recv {
     if (req.http.x-forwarded-for) {
         set req.http.X-Forwarded-For =
             req.http.X-Forwarded-For ", " client.ip;
     } else {
         set req.http.X-Forwarded-For = client.ip;
     }
     if (req.request == "PURGE") {
         if (!client.ip ~ purge) {
             error 405 "This IP is not allowed to send PURGE requests.";
         }

         return (lookup);
     }
     if (req.request != "GET" &&
       req.request != "HEAD" &&
       req.request != "PUT" &&
       req.request != "POST" &&
       req.request != "TRACE" &&
       req.request != "OPTIONS" &&
       req.request != "DELETE") {
         /* Non-RFC2616 or CONNECT which is weird. */
         return (pipe);
     }
     if (req.request != "GET" && req.request != "HEAD") {
         /* We only deal with GET and HEAD by default */
         return (pass);
     }
     if (req.url ~ "^/image.cgi") {
         unset req.http.cookie;
     }
     if (req.url ~ "^/image/") {
         unset req.http.cookie;
     }
     if (req.http.Authorization || req.http.Cookie) {
         /* Not cacheable by default */
         return (pass);
     }
     return (lookup);
 }

 sub vcl_pipe {
     # Note that only the first request to the backend will have
     # X-Forwarded-For set.  If you use X-Forwarded-For and want to
     # have it set for all requests, make sure to have:
     # set req.http.connection = "close";
     # here.  It is not set by default as it might break some broken web
     # applications, like IIS with NTLM authentication.
     set req.http.connection = "close";
     return (pipe);
 }

sub vcl_pass {
    return (pass);
}
#
# sub vcl_hash {
#     set req.hash += req.url;
#     if (req.http.host) {
#         set req.hash += req.http.host;
#     } else {
#         set req.hash += server.ip;
#     }
#     return (hash);
# }

 sub vcl_hit {
     if (req.request == "PURGE") {
         set obj.ttl = 0s;
         error 200 "Purged";
     }
     return (deliver);
 }

 sub vcl_miss {
     if (req.request == "PURGE") {
         error 404 "Not in cache";
     }
     return (fetch);
 }

 sub vcl_fetch {
#     if (!beresp.cacheable) {
#         return (pass);
#     }
     if( (req.request == "GET" || req.request == "HEAD") && (req.url ~ "^/image.cgi" || req.url ~ "^/image/") ) {
         if(beresp.status == 200 || beresp.status == 302) {
             set beresp.ttl = 1h;
         }
         if(beresp.status == 404) {
             set beresp.ttl = 0s;
         }
         if(beresp.status >= 500) {
             set beresp.ttl = 0s;
         }
         return (deliver);
     }
     if (beresp.http.Set-Cookie) {
         return (pass);
     }
 }
#
# sub vcl_deliver {
#     return (deliver);
# }
#
# sub vcl_error {
#     set obj.http.Content-Type = "text/html; charset=utf-8";
#     synthetic {"
# <?xml version="1.0" encoding="utf-8"?>
# <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
#  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
#
#
#     <title>"} obj.status " " obj.response {"</title>
#
#
#
Error "} obj.status " " obj.response {"

#
"} obj.response {"

#
Guru Meditation:

#
XID: "} req.xid {"

#

#
Varnish cache server

#
#
# "};
#     return (deliver);
# }

# This is a basic VCL configuration file for varnish.  See the vcl(7)
# man page for details on VCL syntax and semantics.
#
# Default backend definition.  Set this to point to your content
# server.
#
backend default {
    .host = "127.0.0.1";
    .port = "8888";
}

acl purge {
     "localhost";
     "172.16.0.0"/24;
}

#
# Below is a commented-out copy of the default VCL logic.  If you
# redefine any of these subroutines, the built-in logic will be
# appended to your code.
#
 sub vcl_recv {
     if (req.http.x-forwarded-for) {
         set req.http.X-Forwarded-For =
             req.http.X-Forwarded-For ", " client.ip;
     } else {
         set req.http.X-Forwarded-For = client.ip;
     }
     if (req.request == "PURGE") {
         if (!client.ip ~ purge) {
             error 405 "This IP is not allowed to send PURGE requests.";
         }

         return (lookup);
     }
     if (req.request != "GET" &&
       req.request != "HEAD" &&
       req.request != "PUT" &&
       req.request != "POST" &&
       req.request != "TRACE" &&
       req.request != "OPTIONS" &&
       req.request != "DELETE") {
         /* Non-RFC2616 or CONNECT which is weird. */
         return (pipe);
     }
     if (req.request != "GET" && req.request != "HEAD") {
         /* We only deal with GET and HEAD by default */
         return (pass);
     }
     if (req.url ~ "^/image.cgi") {
         unset req.http.cookie;
     }
     if (req.url ~ "^/image/") {
         unset req.http.cookie;
     }
     if (req.http.Authorization || req.http.Cookie) {
         /* Not cacheable by default */
         return (pass);
     }
     return (lookup);
 }

 sub vcl_pipe {
     # Note that only the first request to the backend will have
     # X-Forwarded-For set.  If you use X-Forwarded-For and want to
     # have it set for all requests, make sure to have:
     # set req.http.connection = "close";
     # here.  It is not set by default as it might break some broken web
     # applications, like IIS with NTLM authentication.
     set req.http.connection = "close";
     return (pipe);
 }

sub vcl_pass {
    return (pass);
}
#
# sub vcl_hash {
#     set req.hash += req.url;
#     if (req.http.host) {
#         set req.hash += req.http.host;
#     } else {
#         set req.hash += server.ip;
#     }
#     return (hash);
# }

 sub vcl_hit {
     if (req.request == "PURGE") {
         set obj.ttl = 0s;
         error 200 "Purged";
     }
     return (deliver);
 }

 sub vcl_miss {
     if (req.request == "PURGE") {
         error 404 "Not in cache";
     }
     return (fetch);
 }

 sub vcl_fetch {
#     if (!beresp.cacheable) {
#         return (pass);
#     }
     if( (req.request == "GET" || req.request == "HEAD") && (req.url ~ "^/image.cgi" || req.url ~ "^/image/") ) {
         set beresp.ttl = 7d;
         if(beresp.status == 404) {
             set beresp.ttl = 0s;
         }
         if(beresp.status >= 500) {
             set beresp.ttl = 0s;
         }
         return (deliver);
     }
     if (beresp.http.Set-Cookie) {
         return (pass);
     }
 }
#
# sub vcl_deliver {
#     return (deliver);
# }
#
# sub vcl_error {
#     set obj.http.Content-Type = "text/html; charset=utf-8";
#     synthetic {"
# <?xml version="1.0" encoding="utf-8"?>
# <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
#  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
#
#
#     <title>"} obj.status " " obj.response {"</title>
#
#
#
Error "} obj.status " " obj.response {"

#
"} obj.response {"

#
Guru Meditation:

#
XID: "} req.xid {"

#

#
Varnish cache server

#
#
# "};
#     return (deliver);
# }


Set the Host header in order to let Heroku's router know which app to route to (#1127)

* Switch host dependant upon location and is QA or PROD

* Add Host header when debugging

import boltsort;

sub vcl_recv {
#FASTLY recv

	# Enable API key authentication for URL purge requests
	if ( req.request == "FASTLYPURGE" ) {
		set req.http.Fastly-Purge-Requires-Auth = "1";
	}

	if (req.request != "HEAD" && req.request != "GET" && req.request != "FASTLYPURGE") {
		return(pass);
	}

	if (req.http.Host ~ "polyfills.io") {
		# Do the canonicalise check before the SSL check to avoid a double redirect
		error 751 "Canonicalise";
	}

	if (!req.http.Fastly-SSL) {
		# 801 is a special error code that Fastly uses to Force SSL on the request
		error 801 "Redirect to prod HTTPS";
	}

	if (req.url ~ "^/v2/(polyfill\.|recordRumData)" && req.url !~ "[\?\&]ua=") {
		set req.http.X-Orig-URL = req.url;
		set req.url = "/v2/normalizeUa?ua=" urlencode(req.http.User-Agent);
	}

	if (req.url ~ "^/v2/recordRumData" && req.http.Normalized-User-Agent) {
		set req.http.Log = regsub(req.url, "^.*?\?(.*)$", "\1") "&ip=" client.ip "&refer_domain=" regsub(req.http.Referer, "^(https?\:\/\/)?(www\.)?(.+?)(\:\d+)?([\/\?].*)?$", "\3") "&country=" geoip.country_code "&data_center=" if(req.http.Cookie:FastlyDC, req.http.Cookie:FastlyDC, server.datacenter);
		error 204 "No Content";
	}

	set req.url = boltsort.sort(req.url);

	declare local var.isQA BOOL;

	set var.isQA = (req.http.host == "qa.polyfill.io");

	if (req.http.X-Geoip-Continent ~ "(NA|SA|OC|AS)") {
		set req.backend = origami_polyfill_service_us;
		set req.http.Host = "ft-polyfill-service-us.herokuapp.com";

		if (!req.backend.healthy) {
			set req.backend = origami_polyfill_service_eu;
			set req.http.Host = "ft-polyfill-service.herokuapp.com";
		}

	} else {
		set req.backend = origami_polyfill_service_eu;
		set req.http.Host = "ft-polyfill-service.herokuapp.com";

		if (!req.backend.healthy) {
			set req.backend = origami_polyfill_service_us;
			set req.http.Host = "ft-polyfill-service-us.herokuapp.com";
		}
	}

	if (var.isQA) {
		if (req.backend == origami_polyfill_service_us) {
			set req.http.Host = "ft-polyfill-service-us-qa.herokuapp.com";
		} else {
			set req.http.Host = "ft-polyfill-service-qa.herokuapp.com";
		}
	}

	return(lookup);
}

sub vcl_deliver {
#FASTLY deliver

	# If the response is to a normalise request and there's a parked "original request", use the normalised UA response to modify the original request and restart it
	if (req.url ~ "^/v\d/normalizeUa" && resp.status == 200 && req.http.X-Orig-URL ~ "^/v2/(polyfill\.|recordRumData)") {
		set req.http.Fastly-force-Shield = "1";
		set req.http.Normalized-User-Agent = resp.http.Normalized-User-Agent;
		if (req.http.X-Orig-URL ~ "\?") {
			set req.url = req.http.X-Orig-URL "&ua=" resp.http.Normalized-User-Agent;
		} else {
			set req.url = req.http.X-Orig-URL "?ua=" resp.http.Normalized-User-Agent;
		}
		restart;

	# If the original request didn't specify a UA override, and we added one when the request was sent to the backend, the backend won't have included a Vary:user-agent header (correctly) but we need to add one
	} else if (req.url ~ "^/v\d/polyfill\..*[\?\&]ua=" && req.http.X-Orig-URL && req.http.X-Orig-URL !~ "[\?\&]ua=") {
		add resp.http.Vary = "User-Agent";
	}

	if (req.url ~ "[\&\?]rum=1") {
		add resp.http.Set-Cookie = "FastlyDC=" server.datacenter "; Path=/; HttpOnly; max-age=60";
	}

	if (req.http.Fastly-Debug) {
		set resp.http.Debug-Host = req.http.Host;
	}

	return(deliver);
}

sub vcl_error {
#FASTLY error

	# Redirect to canonical prod/qa origins
	if (obj.status == 751) {
		set obj.status = 301;
		set obj.response = "Moved Permanently";
		set obj.http.Location = "https://polyfill.io" req.url;
		synthetic {""};
		return (deliver);
	}
}

import boltsort;

sub vcl_recv {
#FASTLY recv

	# Enable API key authentication for URL purge requests
	if ( req.request == "FASTLYPURGE" ) {
		set req.http.Fastly-Purge-Requires-Auth = "1";
	}

	if (req.request != "HEAD" && req.request != "GET" && req.request != "FASTLYPURGE") {
		return(pass);
	}

	if (req.http.Host ~ "polyfills.io") {
		# Do the canonicalise check before the SSL check to avoid a double redirect
		error 751 "Canonicalise";
	}

	if (!req.http.Fastly-SSL) {
		# 801 is a special error code that Fastly uses to Force SSL on the request
		error 801 "Redirect to prod HTTPS";
	}

	if (req.url ~ "^/v2/(polyfill\.|recordRumData)" && req.url !~ "[\?\&]ua=") {
		set req.http.X-Orig-URL = req.url;
		set req.url = "/v2/normalizeUa?ua=" urlencode(req.http.User-Agent);
	}

	if (req.url ~ "^/v2/recordRumData" && req.http.Normalized-User-Agent) {
		set req.http.Log = regsub(req.url, "^.*?\?(.*)$", "\1") "&ip=" client.ip "&refer_domain=" regsub(req.http.Referer, "^(https?\:\/\/)?(www\.)?(.+?)(\:\d+)?([\/\?].*)?$", "\3") "&country=" geoip.country_code "&data_center=" if(req.http.Cookie:FastlyDC, req.http.Cookie:FastlyDC, server.datacenter);
		error 204 "No Content";
	}

	set req.url = boltsort.sort(req.url);

	if (req.http.X-Geoip-Continent ~ "(NA|SA|OC|AS)") {
		set req.backend = origami_polyfill_service_us;

		if (!req.backend.healthy) {
			set req.backend = origami_polyfill_service_eu;
		}

	} else {
		set req.backend = origami_polyfill_service_eu;

		if (!req.backend.healthy) {
			set req.backend = origami_polyfill_service_us;
		}
	}

	return(lookup);
}

sub vcl_deliver {
#FASTLY deliver

	# If the response is to a normalise request and there's a parked "original request", use the normalised UA response to modify the original request and restart it
	if (req.url ~ "^/v\d/normalizeUa" && resp.status == 200 && req.http.X-Orig-URL ~ "^/v2/(polyfill\.|recordRumData)") {
		set req.http.Fastly-force-Shield = "1";
		set req.http.Normalized-User-Agent = resp.http.Normalized-User-Agent;
		if (req.http.X-Orig-URL ~ "\?") {
			set req.url = req.http.X-Orig-URL "&ua=" resp.http.Normalized-User-Agent;
		} else {
			set req.url = req.http.X-Orig-URL "?ua=" resp.http.Normalized-User-Agent;
		}
		restart;

	# If the original request didn't specify a UA override, and we added one when the request was sent to the backend, the backend won't have included a Vary:user-agent header (correctly) but we need to add one
	} else if (req.url ~ "^/v\d/polyfill\..*[\?\&]ua=" && req.http.X-Orig-URL && req.http.X-Orig-URL !~ "[\?\&]ua=") {
		add resp.http.Vary = "User-Agent";
	}

	if (req.url ~ "[\&\?]rum=1") {
		add resp.http.Set-Cookie = "FastlyDC=" server.datacenter "; Path=/; HttpOnly; max-age=60";
	}

	return(deliver);
}

sub vcl_error {
#FASTLY error

	# Redirect to canonical prod/qa origins
	if (obj.status == 751) {
		set obj.status = 301;
		set obj.response = "Moved Permanently";
		set obj.http.Location = "https://polyfill.io" req.url;
		synthetic {""};
		return (deliver);
	}
}


Fix inability to purge by URL from the Fastly UI

    # Fixup for Varnish ESI not dealing with https:// absolute URLs well
    if (req.is_esi_subreq && req.url ~ "/https://([^/]+)(/.*)$") {
        set req.http.Host = re.group.1;
        set req.url = re.group.2;
    }

    # Pass any checkout, cart or customer/myaccount urls
    if (req.url.path ~ "/(cart|checkout|customer)") {
        set req.http.x-pass = "1";
    # Pass all admin actions
    } else if (req.url.path ~ "^/(index\.php/)?####ADMIN_PATH####/") {
        set req.http.x-pass = "1";
    # bypass language switcher
    } else if (req.url.qs ~ "(?i)___from_store=.*&___store=.*") {
        set req.http.x-pass = "1";
    }

    # set HTTPS header for offloaded TLS
    if (req.http.Fastly-SSL) {
        set req.http.Https = "on";
    }

    if (req.http.cookie:FASTLY_CDN_ENV) {
        set req.http.Fastly-Cdn-Env = req.http.cookie:FASTLY_CDN_ENV;
    } else {
        unset req.http.Fastly-Cdn-Env;
    }

    ############################################################################################################
    # Following code block controls purge by URL. By default we want to protect all URL purges. In general this
    # is addressed by adding Fastly-Purge-Requires-Auth request header in vcl_recv however this runs the risk of
    # exposing API tokens if user attempts to purge non-https URLs. For this reason inside the Magento module
    # we use X-Purge-Token. Unfortunately this breaks purge from the Fastly UI. Therefore in the next code block
    # we check for presence of X-Purge-Token. If it's not present we force the Fastly-Purge-Requires-Auth
    if (req.request == "FASTLYPURGE") {
        # extract token signature and expiration
        if (req.http.X-Purge-Token && req.http.X-Purge-Token ~ "^([^_]+)_(.*)" ) {

            declare local var.X-Exp STRING;
            declare local var.X-Sig STRING;
            /* extract token expiration and signature */
            set var.X-Exp = re.group.1;
            set var.X-Sig = re.group.2;

            /* validate signature */
            if (var.X-Sig == regsub(digest.hmac_sha1(req.service_id, req.url.path var.X-Exp), "^0x", "")) {
            /* check that expiration time has not elapsed */
            if (time.is_after(now, std.integer2time(std.atoi(var.X-Exp)))) {
                error 410;
            }
            }

        } else {
            set req.http.Fastly-Purge-Requires-Auth = "1";
        }
    }

    # disable ESI processing on Origin Shield
    if (req.http.Fastly-FF) {
        set req.esi = false;
        # Needed for proper handling of stale while revalidated when shielding is involved
        set req.max_stale_while_revalidate = 0s;
    }

    # static files are always cacheable. remove SSL flag and cookie
    if (req.url.path ~ "^/(media|js|skin)/.*\.(png|jpg|jpeg|gif|css|js|swf|ico|webp|svg)$") {
        unset req.http.Https;
        unset req.http.Cookie;
    }

    # formkey lookup
    if (req.url.path ~ "/fastlycdn/getformkey/") {
        # check if we have a formkey cookie
        if (req.http.Cookie:FASTLY_CDN_FORMKEY) {
            set req.http.Formkey = req.http.Cookie:FASTLY_CDN_FORMKEY;
        } else {
            # create formkey
            set req.http.seed = req.http.Cookie client.ip remote.port client.geo.longitude client.geo.latitude client.geo.postal_code;
            set req.http.Formkey = regsub(digest.hash_md5(req.http.seed), "^0x", "");
        }
        error 760 req.http.Formkey;
    }

    # client.geo lookup
    if (req.url.path ~ "fastlycdn/esi/getcountry/") {
        # check if GeoIP has been already processed by client
        if (req.http.Cookie:FASTLY_CDN_GEOIP_PROCESSED) {
            error 200 "";
        } else {
            # modify req.url and restart request processing
            error 750 client.geo.country_code;
        }
    }

    # client.geo get country code
    if (req.url.path ~ "fastlycdn/esi/getcountrycode/") {
        # create and set req.http.X-Country-Code
        error 755 client.geo.country_code;
    }

    # check for ESI calls
    if (req.url.qs ~ "esi_data=") {
        # check for valid cookie data
        if (req.http.Cookie ~ "FASTLY_CDN-([A-Za-z0-9-_]+)=([^;]*)") {
            set req.url = querystring.filter(req.url, "esi_data") + "&esi_data=" + re.group.2;
        }
    }

    # Per suggestions in https://github.com/sdinteractive/SomethingDigital_PageCacheParams
    # we'll strip out query parameters used in Google AdWords, Mailchimp tracking
    set req.http.Magento-Original-URL = req.url;
    set req.url = querystring.regfilter(req.url, "^(####QUERY_PARAMETERS####)");
    # Sort the query arguments to increase cache hit ratio with query arguments that
    # may be out of order however only on URLs that are not being passed.
    if ( !req.http.x-pass ) {
        set req.url = boltsort.sort(req.url);
    }

    # Fixup for Varnish ESI not dealing with https:// absolute URLs well
    if (req.is_esi_subreq && req.url ~ "/https://([^/]+)(/.*)$") {
        set req.http.Host = re.group.1;
        set req.url = re.group.2;
    }

    # Pass any checkout, cart or customer/myaccount urls
    if (req.url.path ~ "/(cart|checkout|customer)") {
        set req.http.x-pass = "1";
    # Pass all admin actions
    } else if (req.url.path ~ "^/(index\.php/)?####ADMIN_PATH####(_.*)?/") {
        set req.http.x-pass = "1";
    # bypass language switcher
    } else if (req.url.qs ~ "(?i)___from_store=.*&___store=.*") {
        set req.http.x-pass = "1";
    }

    # set HTTPS header for offloaded TLS
    if (req.http.Fastly-SSL) {
        set req.http.Https = "on";
    }

    if (req.http.cookie:FASTLY_CDN_ENV) {
        set req.http.Fastly-Cdn-Env = req.http.cookie:FASTLY_CDN_ENV;
    } else {
        unset req.http.Fastly-Cdn-Env;
    }

    # auth for purging
    if (req.request == "FASTLYPURGE") {

        if (!req.http.X-Purge-Token ~ "^([^_]+)_(.*)" ) {
            error 403;
        }

        declare local var.X-Exp STRING;
        declare local var.X-Sig STRING;
        /* extract token expiration and signature */
        set var.X-Exp = re.group.1;
        set var.X-Sig = re.group.2;

        /* validate signature */
        if (var.X-Sig == regsub(digest.hmac_sha1(req.service_id, req.url.path var.X-Exp), "^0x", "")) {
            /* check that expiration time has not elapsed */
            if (time.is_after(now, std.integer2time(std.atoi(var.X-Exp)))) {
                error 410;
            }

        } else {
            error 403;
        }

    }

    # disable ESI processing on Origin Shield
    if (req.http.Fastly-FF) {
        set req.esi = false;
        # Needed for proper handling of stale while revalidated when shielding is involved
        set req.max_stale_while_revalidate = 0s;
    }

    # static files are always cacheable. remove SSL flag and cookie
    if (req.url.path ~ "^/(media|js|skin)/.*\.(png|jpg|jpeg|gif|css|js|swf|ico|webp|svg)$") {
        unset req.http.Https;
        unset req.http.Cookie;
    }

    # formkey lookup
    if (req.url.path ~ "/fastlycdn/getformkey/") {
        # check if we have a formkey cookie
        if (req.http.Cookie:FASTLY_CDN_FORMKEY) {
            set req.http.Formkey = req.http.Cookie:FASTLY_CDN_FORMKEY;
        } else {
            # create formkey
            set req.http.seed = req.http.Cookie client.ip remote.port client.geo.longitude client.geo.latitude client.geo.postal_code;
            set req.http.Formkey = regsub(digest.hash_md5(req.http.seed), "^0x", "");
        }
        error 760 req.http.Formkey;
    }

    # client.geo lookup
    if (req.url.path ~ "fastlycdn/esi/getcountry/") {
        # check if GeoIP has been already processed by client
        if (req.http.Cookie:FASTLY_CDN_GEOIP_PROCESSED) {
            error 200 "";
        } else {
            # modify req.url and restart request processing
            error 750 client.geo.country_code;
        }
    }

    # client.geo get country code
    if (req.url.path ~ "fastlycdn/esi/getcountrycode/") {
        # create and set req.http.X-Country-Code
        error 755 client.geo.country_code;
    }

    # check for ESI calls
    if (req.url.qs ~ "esi_data=") {
        # check for valid cookie data
        if (req.http.Cookie ~ "FASTLY_CDN-([A-Za-z0-9-_]+)=([^;]*)") {
            set req.url = querystring.filter(req.url, "esi_data") + "&esi_data=" + re.group.2;
        }
    }

    # Per suggestions in https://github.com/sdinteractive/SomethingDigital_PageCacheParams
    # we'll strip out query parameters used in Google AdWords, Mailchimp tracking
    set req.http.Magento-Original-URL = req.url;
    set req.url = querystring.regfilter(req.url, "^(####QUERY_PARAMETERS####)");
    # Sort the query arguments to increase cache hit ratio with query arguments that
    # may be out of order however only on URLs that are not being passed.
    if ( !req.http.x-pass ) {
        set req.url = boltsort.sort(req.url);
    }


Don't reference the old TRAC wiki in example.vcl

Refs #2948

#
# This is an example VCL file for Varnish.
#
# It does not do anything by default, delegating control to the
# builtin VCL. The builtin VCL is called when there is no explicit
# return statement.
#
# See the VCL chapters in the Users Guide for a comprehensive documentation
# at https://www.varnish-cache.org/docs/.

# Marker to tell the VCL compiler that this VCL has been written with the
# 4.0 or 4.1 syntax.
vcl 4.1;

# Default backend definition. Set this to point to your content server.
backend default {
    .host = "127.0.0.1";
    .port = "8080";
}

sub vcl_recv {
    # Happens before we check if we have this in cache already.
    #
    # Typically you clean up the request here, removing cookies you don't need,
    # rewriting the request, etc.
}

sub vcl_backend_response {
    # Happens after we have read the response headers from the backend.
    #
    # Here you clean the response headers, removing silly Set-Cookie headers
    # and other mistakes your backend does.
}

sub vcl_deliver {
    # Happens when we have all the pieces we need, and are about to send the
    # response to the client.
    #
    # You can do accounting or modifying the final object here.
}

#
# This is an example VCL file for Varnish.
#
# It does not do anything by default, delegating control to the
# builtin VCL. The builtin VCL is called when there is no explicit
# return statement.
#
# See the VCL chapters in the Users Guide at https://www.varnish-cache.org/docs/
# and https://www.varnish-cache.org/trac/wiki/VCLExamples for more examples.

# Marker to tell the VCL compiler that this VCL has been adapted to the
# 4.0 or 4.1 syntax.
vcl 4.1;

# Default backend definition. Set this to point to your content server.
backend default {
    .host = "127.0.0.1";
    .port = "8080";
}

sub vcl_recv {
    # Happens before we check if we have this in cache already.
    #
    # Typically you clean up the request here, removing cookies you don't need,
    # rewriting the request, etc.
}

sub vcl_backend_response {
    # Happens after we have read the response headers from the backend.
    #
    # Here you clean the response headers, removing silly Set-Cookie headers
    # and other mistakes your backend does.
}

sub vcl_deliver {
    # Happens when we have all the pieces we need, and are about to send the
    # response to the client.
    #
    # You can do accounting or modifying the final object here.
}


Formatting change.

Formatting change.

vcl 4.0;

# {{ ansible_managed }}

import directors;
import std;

###
# Varnish Documentation
#   https://www.varnish-cache.org/docs/trunk/users-guide/vcl-built-in-subs.html
#   https://www.varnish-cache.org/docs/trunk/reference/vcl.html
##

{% for backend in varnish_backend_hosts %}
backend application_{{ loop.index }} {
  .host = "{{ backend }}";
  .port = "{{ varnish_backend_port }}";
{% if varnish_health_checks_enabled %}
  .probe = {
    .url = "{{ varnish_health_check_url }}";
    .interval = {{ varnish_health_check_interval }};
    .timeout  = {{ varnish_health_check_timeout }};
    .window = {{ varnish_health_check_window }};
    .threshold = {{ varnish_health_check_threshold }};
  }
{% endif %}
}
{% endfor %}

###
# vcl_init
#   Called when VCL is loaded, before any requests pass through it. Typically used to initialize VMODs.
###
# The vcl_init subroutine may terminate with calling return() with one of the following keywords:
#
# ok
#   Normal return, VCL continues loading.
##

sub vcl_init {
  new application = directors.round_robin();
{% for backend in varnish_backend_hosts %}
  application.add_backend(application_{{ loop.index }});
{% endfor %}
}

###
# vcl_recv
#   Called at the beginning of a request, after the complete request has been received and parsed.
#   Its purpose is to decide whether or not to serve the request, how to do it, and, if applicable, which backend to use.
#   It is also used to modify the request
###
# The vcl_recv subroutine may terminate with calling return() on one of the following keywords:
#
# synth(status code, reason)
#   Return a synthetic object with the specified status code to the client and abandon the request.
# pass
#   Switch to pass mode. Control will eventually pass to vcl_pass.
# pipe
#   Switch to pipe mode. Control will eventually pass to vcl_pipe.
# hash
#   Continue processing the object as a potential candidate for caching. Passes the control over to vcl_hash.
# purge
#   Purge the object and it's variants. Control passes through vcl_hash to vcl_purge.
#

sub vcl_recv {
  set req.backend_hint = application.backend();

  ###
  # Avoid processing uncacheable requests
  ###

  if (req.http.Authorization) {
    set req.http.X-Passthrough-Reason = "No Caching Authenticated Content";
    return (pass);
  }

{% if varnish_uri_blacklist_enabled %}
  if ((req.url ~ "{{ varnish_uri_blacklist_regexp }}")) {
    set req.http.X-Passthrough-Reason = "Path in Blacklist";
    return(pass);
  }
{% endif %}

{% if varnish_diagnostic_headers_forwarded_for %}
  ###
  # Inject X-Forwarded-For headers, but only once.
  ###

  if (req.restarts == 0) {
    if (req.http.X-Forwarded-For) {
      set req.http.X-Forwarded-For = req.http.X-Forwarded-For + ", " + client.ip;
    } else {
      set req.http.X-Forwarded-For = client.ip;
    }
  }
{% endif %}

{% if varnish_cookie_sanitization_enabled %}
  ###
  # Cookie Sanitization
  ###
  if (req.http.Cookie) {
    set req.http.X-Cookie-Unmodified = req.http.Cookie;

  {% for cookie in varnish_cookie_sanitization_blacklist %}
#    set req.http.Cookie = regsuball(req.http.Cookie, "{{ cookie }}=[^;]+(; )?", "");
  {% endfor %}

    ### General Cleanup
    set req.http.Cookie = regsuball(req.http.Cookie, "^;\s*", "");
    if (req.http.Cookie ~ "^\s*$") {
        set req.http.X-Cookie-Discarded = "YES:Empty";
        unset req.http.Cookie;
    }
    if (req.http.Cookie) {
      set req.http.X-Cookie-Sanitized = req.http.Cookie;
    }
  }
{% endif %}

{% if varnish_header_sanitization_enabled %}
  ###
  # Header Sanitization
  ###

{% if varnish_header_sanitization_discard_host_port %}
  set req.http.Host = regsub(req.http.Host, ":[0-9]+", "");
{% endif %}

{% if varnish_header_sanitization_normalize_accept_encoding %}
  if (req.http.Accept-Encoding) {
    if (req.url ~ "\.(jpg|png|gif|gz|tgz|bz2|tbz|mp3|ogg)$") {
      unset req.http.Accept-Encoding;
    } elsif (req.http.Accept-Encoding ~ "gzip") {
      set req.http.Accept-Encoding = "gzip";
    } elsif (req.http.Accept-Encoding ~ "deflate") {
      set req.http.Accept-Encoding = "deflate";
    } else {
      unset req.http.Accept-Encoding;
    }
  }
{% endif %}

{% endif %}

{% if varnish_uri_sanitization_enabled %}
  ###
  # URI Sanitization
  ###

  if (req.url ~ "(\?|&){{ varnish_uri_sanitization_regexp }}=") {
    set req.url = regsuball(req.url, "&{{ varnish_uri_sanitization_regexp }}=([A-z0-9_\-\.%25]+)", "");
    set req.url = regsuball(req.url, "\?{{ varnish_uri_sanitization_regexp }}=([A-z0-9_\-\.%25]+)", "?");
    set req.url = regsub(req.url, "\?&", "?");
    set req.url = regsub(req.url, "\?$", "");
  }
  if (req.url ~ "\#") {
    set req.url = regsub(req.url, "\#.*$", "");
  }
{% endif %}

{% if varnish_workaround_telusdotcom_browser_profile %}
  ###
  # Workaround and Diagnostics for BrowserProfile and Language/Region Detection on TELUS.com
  ###

  if (req.http.Cookie ~ "BrowserProfile") {
    set req.http.X-BrowserProfile = regsuball(req.http.Cookie, "(.*)BrowserProfile=([^;]*)(.*)", "\2");
    if (req.http.X-BrowserProfile ~ "language") {
      set req.http.X-Language = std.tolower(regsuball(req.http.X-BrowserProfile, "(.*)language\\.:\\.([^\\]*)(.*)", "\2"));
      set req.http.X-Language-Found = "YES:BrowserProfile";
    }
    if (req.http.X-BrowserProfile ~ "region") {
      set req.http.X-Region = std.tolower(regsuball(regsuball(req.http.X-BrowserProfile, "geo\\.\:{(.*)\}", ""), "(.*)region\\.:\\.([^\\]*)(.*)", "\2"));
      set req.http.X-Region-Found = "YES:BrowserProfile";
    }
    if (req.http.X-BrowserProfile ~ "geo") {
      set req.http.X-Geolocation = regsuball(req.http.X-BrowserProfile, "(.*)geo..:\\.([^\\]*)(.*)", "\2");
      set req.http.X-Geolocation-Found = "YES:BrowserProfile";

      if (req.http.X-Geolocation ~ "country") {
        set req.http.X-Geolocation-Country = regsuball(req.http.X-Geolocation, "(.*)country\\.:\\.([^\\]*)(.*)", "\2");
      }
      if (req.http.X-Geolocation ~ "region") {
        set req.http.X-Geolocation-Region = regsuball(req.http.X-Geolocation, "(.*)region\\.:\\.([^\\]*)(.*)", "\2");
      }
      if (req.http.X-Geolocation ~ "city") {
        set req.http.X-Geolocation-City = regsuball(req.http.X-Geolocation, "(.*)city\\.:\\.([^\\]*)(.*)", "\2");
      }
      if (req.http.X-Geolocation ~ "isp") {
        set req.http.X-Geolocation-ISP = regsuball(req.http.X-Geolocation, "(.*)isp\\.:\\.([^\\]*)(.*)", "\2");
      }
    } else {
      set req.http.X-Geolocation-Found = "NO";
    }
  }

  if (!req.http.X-Language) {
    if (req.http.Cookie ~ "lang=") {
      set req.http.X-Language = std.tolower(regsuball(req.http.Cookie, "(.*)lang=([^;]*)(.*)", "\2"));
      set req.http.X-Language-Found = "YES:lang";
    } else {
      set req.http.X-Language = "en";
      set req.http.X-Language-Found = "NO";
    }
  }

  if (!req.http.X-Region) {
    if (req.http.Cookie ~ "prov=") {
      set req.http.X-Region = std.tolower(regsuball(req.http.Cookie, "(.*)prov=([^;]*)(.*)", "\2"));
      set req.http.X-Region-Found = "YES:prov";
    } else {
      set req.http.X-Region = "bc";
      set req.http.X-Region-Found = "NO";
    }
  }

  if (req.url ~ "/(en|fr)/") {
    if (req.url ~ "/en/" && req.http.X-Language != "en") {
      set req.http.X-Passthrough-Reason = "Unaligned Language (Path: /en/) vs (Cookie: " + req.http.X-Language + ")";
      return(pass);
    }
    if (req.url ~ "/fr/" && req.http.X-Language != "fr") {
      set req.http.X-Passthrough-Reason = "Unaligned Language (Path: /fr/) vs (Cookie: " + req.http.X-Language + ")";
      return(pass);
    }
  } else {
      set req.http.X-Passthrough-Reason = "Not Set in Path (Language)";
    return(pass);
  }

  if (req.url ~ "/(ab|bc|mb|nb|nl|ns|nt|nu|on|pe|qc|sk|yt)/") {
    {% for province in ['ab', 'bc', 'mb', 'nb', 'nl', 'ns', 'nt', 'nu', 'on', 'pe', 'qc', 'sk', 'yt'] %}
    if (req.url ~ "/{{ province }}/" && req.http.X-Region != "{{ province }}") {
      set req.http.X-Passthrough-Reason = "Unaligned Region (Path: /{{ province }}/) vs (Cookie: " + req.http.X-Region + ")";
      return(pass);
    }
    {% endfor %}
  } else {
      set req.http.X-Passthrough-Reason = "Not Set in Path (Province)";
    return(pass);
  }

  if (req.http.Cookie) {
    if (req.http.Cookie ~ "resolution") {
      set req.http.X-Screen-Resolution = regsuball(req.http.Cookie, "(.*)resolution=([^;]*)(.*)", "\2");
    }

    if (req.http.Cookie ~ "CustomerType") {
      set req.http.X-Customer-Type = regsuball(req.http.Cookie, "(.*)CustomerType=([^;]*)(.*)", "\2");
    }
    if (req.http.Cookie ~ "outdated-browser-notification") {
      set req.http.X-Outdated-Browser = regsuball(req.http.Cookie, "(.*)outdated-browser-notification=([^;]*)(.*)", "\2");
    }
  }
{% endif %}

{% if varnish_cookie_sanitization_discard_from_client %}
    set req.http.X-Cookie-Discarded = "YES:No Reason Not To";
    unset req.http.Cookie;
{% endif %}
}

###
# vcl_hit
#   Called when a cache lookup is successful.
###
# The vcl_hit subroutine may terminate with calling return() with one of the following keywords:
#
# restart
#   Restart the transaction. Increases the restart counter. If the number of restarts is higher than max_restarts Varnish emits a guru meditation error.
# deliver
#   Deliver the object. Control passes to vcl_deliver.
# synth(status code, reason)
#   Return the specified status code to the client and abandon the request.
###

sub vcl_hit {
  if (obj.ttl > 0s) {
    return (deliver);
  }
{% if varnish_health_checks_enabled %}
  if (!std.healthy(req.backend_hint) && (obj.ttl + obj.grace > 0s)) {
    return (deliver);
  } else {
    return (fetch);
  }
{% else %}
  if (obj.ttl + obj.grace > 0s) {
     return (deliver);
  }

  return (fetch);
{% endif %}
}

###
# vcl_backend_response
#   Called after the response headers has been successfully retrieved from the backend.
###
# The vcl_backend_response subroutine may terminate with calling return() with one of the following keywords:
#
# deliver
#   Possibly insert the object into the cache, then deliver it to the Control will eventually pass to vcl_deliver.
# abandon
#   Abandon the backend request and generates an error.
# retry
#   Retry the backend transaction. Increases the retries counter. If the number of retries is higher than max_retries Varnish emits a guru meditation error.
###

sub vcl_backend_response {
{% if varnish_workaround_telusdotcom_browser_profile %}
  if (bereq.http.X-Language) {
    set beresp.http.X-Language = bereq.http.X-Language;
#    if (bereq.http.X-Language-Found) {
#      set beresp.http.X-Language-Found = bereq.http.X-Language-Found;
#    }
  }
  if (bereq.http.X-Region) {
    set beresp.http.X-Region = bereq.http.X-Region;
#    if (bereq.http.X-Region-Found) {
#      set beresp.http.X-Region-Found = bereq.http.X-Region-Found;
#    }
  }
{% endif %}

{% if varnish_uri_blacklist_enabled %}
  if (bereq.url ~ "{{ varnish_uri_blacklist_regexp }}") {
    set beresp.http.X-Cacheable = "NO:Path in Blacklist";
    set beresp.uncacheable = true;
    set beresp.ttl = {{ varnish_backend_response_ttl }}s;
    return(deliver);
  } else {
    if (bereq.http.X-Passthrough-Reason) {
{% if varnish_diagnostic_headers_passthrough_reason %}
      set beresp.http.X-Passthrough-Reason = bereq.http.X-Passthrough-Reason;
{% endif %}
      return(deliver);
    } else {
{% if varnish_cookie_sanitization_discard_from_server %}
      unset beresp.http.Set-Cookie;
{% endif %}
    }
  }
{% else %}
{% if varnish_cookie_sanitization_discard_from_server %}
  unset beresp.http.Set-Cookie;
{% endif %}
{% endif %}

{% if varnish_diagnostic_headers_enabled %}
  if (bereq.http.Cookie) {
    set beresp.http.X-Cacheable = "NO:Cookie in Request";
    set beresp.uncacheable = true;
    set beresp.ttl = {{ varnish_backend_response_ttl }}s;
    return(deliver);
  } elsif (beresp.http.Set-Cookie) {
    set beresp.http.X-Cacheable = "NO:Set-Cookie in Response";
    set beresp.uncacheable = true;
    set beresp.ttl = {{ varnish_backend_response_ttl }}s;
    return(deliver);
  } elsif (beresp.http.Cache-Control ~ "private") {
    set beresp.http.X-Cacheable = "NO:Cache-Control=private";
    set beresp.uncacheable = true;
    set beresp.ttl = {{ varnish_backend_response_ttl }}s;
    return(deliver);
  } elsif (beresp.http.X-No-Cache) {
    set beresp.http.X-Cacheable = "NO:X-No-Cache";
    set beresp.uncacheable = true;
    set beresp.ttl = {{ varnish_backend_response_ttl }}s;
    return(deliver);
  } elsif (beresp.ttl <= 0s) {
    set beresp.http.X-Cacheable = "NO:Not Cacheable";
    set beresp.uncacheable = true;
    set beresp.ttl = {{ varnish_backend_response_ttl }}s;
    return(deliver);
  } else {
    set beresp.http.X-Cacheable = "YES";
  }
{% endif %}

  set beresp.grace = {{ varnish_grace_period }};
}

###
# vcl_deliver
#   Called before a cached object is delivered to the client.
###
# The vcl_deliver subroutine may terminate with calling return() with one of the following keywords:
#
# deliver
#   Deliver the object to the client.
# restart
#   Restart the transaction. Increases the restart counter. If the number of restarts is higher than max_restarts Varnish emits a guru meditation error.
###

sub vcl_deliver {
  if (obj.hits > 0) {
    set resp.http.X-Cache = "HIT";
  } else {
    set resp.http.X-Cache = "MISS";
  }
}


re #120 - Update VCL description

re #120 - Update VCL description

vcl 4.0;

# This Varnish configuration is a very basic template to get started with caching Joomla sites.
# In no way is this configuration complete: every site is unique and needs customisation!
#
# Joomla creates a session cookie by default, even if you not logged in. To deal with this,
# we need to let Varnish know when a user is logged in or not. If the user is not logged in,
# we will prevent the Joomla response from setting any cookies so Varnish can cache the page.
#
# To set the X-Logged-In header, append the following line in to the onAfterInitialise() method
# in /plugins/system/cache/cache.php, right after "$user = JFactory::getUser();" (line #60):
#
# JFactory::getApplication()->setHeader('X-Logged-In', $user->guest ? 'false' : 'true', true);
#
# Now enable the Cache plugin and Varnish cache. Clear your existing cookies.
# Your front-end pages will be cached as long as you browse the site as a guest.
#
# This VCL is based on https://snipt.net/fevangelou/the-perfect-varnish-configuration-for-joomla-websites/

import std;

backend default {
    .host = "127.0.0.1";
    .port = "8080";
    .probe = {
      .url = "/varnish-enabled";
      .interval = 1s;
      .timeout = 1s;
    }
}

backend alternative {
    .host = "127.0.0.1";
    .port = "8080";
}

sub vcl_recv {
        # Forward client's IP to backend
        unset req.http.X-Forwarded-For;
        set req.http.X-Forwarded-For = client.ip;

        set req.http.X-Forwarded-By = server.ip;
        set req.http.X-Forwarded-Port = 80;

        # Check if we've still enabled Varnish, if not, passthrough every request
        if (! std.healthy(req.backend_hint))
        {
            set req.backend_hint = alternative;
            return (pass);
        }

        # Do not cache phpmyadmin
        if (req.http.host == "phpmyadmin.joomla.box") {
            return (pass);
        }

        # Do not cache system tools on joomla.box:
        if (req.http.host == "joomla.box")
        {
            if (req.url == "/apc" || req.url == "/phpinfo" || req.url == "/pimpmylog") {
                return (pass);
            }
        }

        # Proxy (pass) any request that goes to the backend admin,
        # the banner component links or any post requests
        if(req.url ~ "/administrator" || req.url ~ "/component/banners" || req.url ~ "/component/users" || req.method == "POST") {
            return (pass);
        }

        # Do not cache if user is logged in
        if (req.http.Authorization || req.http.Authenticate || req.http.Cookie) {
            return (pass);
        }

        # Don't cache ajax requests
        if(req.http.X-Requested-With == "XMLHttpRequest" || req.url ~ "nocache") {
            return (pass);
        }

        # Properly handle different encoding types
        if (req.http.Accept-Encoding) {
          if (req.url ~ "\.(jpg|jpeg|png|gif|gz|tgz|bz2|tbz|mp3|ogg|swf)$") {
            # 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") {
            set req.http.Accept-Encoding = "deflate";
          } else {
            # unknown algorithm (aka crappy browser)
            unset req.http.Accept-Encoding;
          }
        }

        return (hash);
}

sub vcl_backend_response {
        # Unset the "etag" header (suggested)
        unset beresp.http.etag;

        # This is Joomla! specific: fix stupid "no-cache" header sent by Joomla! even
        # when caching is on - make sure to replace 300 with the number of seconds that
        # you want the browser to cache content
        if(beresp.http.Cache-Control ~ "no-cache" || beresp.http.Cache-Control == ""){
            set beresp.http.Cache-Control = "max-age=300, public, must-revalidate";
        }

        # Check for the custom "x-Logged-In" header to identify if the visitor is a guest,
        # then unset any cookie (including session cookies) provided it's not a POST request.
        if(bereq.method != "POST" && beresp.http.X-Logged-In == "false") {
            unset beresp.http.Set-Cookie;
        }

        # This is how long Varnish will cache content
        set beresp.ttl = 1w;

        return (deliver);
}

sub vcl_deliver {
        if (resp.http.x-varnish ~ " ") {
            set resp.http.X-Varnish-Status = "HIT";
        } else {
            set resp.http.X-Varnish-Status = "MISS";
        }

        # Please note that obj.hits behaviour changed in 4.0, now it counts per objecthead, not per object
        # and obj.hits may not be reset in some cases where bans are in use. See bug 1492 for details.
        # So take hits with a grain of salt
        set resp.http.X-Varnish-Hits = obj.hits;
}

vcl 4.0;

# This Varnish configuration is a very basic template to get started with caching Joomla sites.
# In no way is this configuration complete: every site is unique and needs customisation!
#
# Joomla creates a session cookie by default, even if you not logged in. To deal with this,
# we need to let Varnish know when a user is logged in or not. If the user is not logged in,
# we will prevent the Joomla response from setting any cookies so Varnish can cache the page.
#
# To set the X-Logged-In header, append the following line in to the onAfterInitialise() method
# in /plugins/system/cache/cache.php, right after "$user = JFactory::getUser();" (line #60):
#
# JFactory::getApplication()->setHeader('X-Logged-In', $user->guest ? 'false' : 'true', true);
#
# Now enable the Cache plugin and Varnish cache. Your front-end pages will be cached
# as long as you browse the site as a guest.
#
# This VCL is based on https://snipt.net/fevangelou/the-perfect-varnish-configuration-for-joomla-websites/

import std;

backend default {
    .host = "127.0.0.1";
    .port = "8080";
    .probe = {
      .url = "/varnish-enabled";
      .interval = 1s;
      .timeout = 1s;
    }
}

backend alternative {
    .host = "127.0.0.1";
    .port = "8080";
}

sub vcl_recv {
        # Forward client's IP to backend
        unset req.http.X-Forwarded-For;
        set req.http.X-Forwarded-For = client.ip;

        set req.http.X-Forwarded-By = server.ip;
        set req.http.X-Forwarded-Port = 80;

        # Check if we've still enabled Varnish, if not, passthrough every request
        if (! std.healthy(req.backend_hint))
        {
            set req.backend_hint = alternative;
            return (pass);
        }

        # Do not cache phpmyadmin
        if (req.http.host == "phpmyadmin.joomla.box") {
            return (pass);
        }

        # Do not cache system tools on joomla.box:
        if (req.http.host == "joomla.box")
        {
            if (req.url == "/apc" || req.url == "/phpinfo" || req.url == "/pimpmylog") {
                return (pass);
            }
        }

        # Proxy (pass) any request that goes to the backend admin,
        # the banner component links or any post requests
        if(req.url ~ "/administrator" || req.url ~ "/component/banners" || req.url ~ "/component/users" || req.method == "POST") {
            return (pass);
        }

        # Do not cache if user is logged in
        if (req.http.Authorization || req.http.Authenticate || req.http.Cookie) {
            return (pass);
        }

        # Don't cache ajax requests
        if(req.http.X-Requested-With == "XMLHttpRequest" || req.url ~ "nocache") {
            return (pass);
        }

        # Properly handle different encoding types
        if (req.http.Accept-Encoding) {
          if (req.url ~ "\.(jpg|jpeg|png|gif|gz|tgz|bz2|tbz|mp3|ogg|swf)$") {
            # 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") {
            set req.http.Accept-Encoding = "deflate";
          } else {
            # unknown algorithm (aka crappy browser)
            unset req.http.Accept-Encoding;
          }
        }

        return (hash);
}

sub vcl_backend_response {
        # Unset the "etag" header (suggested)
        unset beresp.http.etag;

        # This is Joomla! specific: fix stupid "no-cache" header sent by Joomla! even
        # when caching is on - make sure to replace 300 with the number of seconds that
        # you want the browser to cache content
        if(beresp.http.Cache-Control ~ "no-cache" || beresp.http.Cache-Control == ""){
            set beresp.http.Cache-Control = "max-age=300, public, must-revalidate";
        }

        # Check for the custom "x-Logged-In" header to identify if the visitor is a guest,
        # then unset any cookie (including session cookies) provided it's not a POST request.
        if(bereq.method != "POST" && beresp.http.X-Logged-In == "false") {
            unset beresp.http.Set-Cookie;
        }

        # This is how long Varnish will cache content
        set beresp.ttl = 1w;

        return (deliver);
}

sub vcl_deliver {
        if (resp.http.x-varnish ~ " ") {
            set resp.http.X-Varnish-Status = "HIT";
        } else {
            set resp.http.X-Varnish-Status = "MISS";
        }

        # Please note that obj.hits behaviour changed in 4.0, now it counts per objecthead, not per object
        # and obj.hits may not be reset in some cases where bans are in use. See bug 1492 for details.
        # So take hits with a grain of salt
        set resp.http.X-Varnish-Hits = obj.hits;
}


Add new variations on parenthesist, for insert after parenthesis

# Voice commands for atom

include folders.vch;
include letters.vch;

Go Strawberry = {Ctrl+Alt+F8};
Go Blueberry ={Ctrl+Alt+o};
Go Banana = {Ctrl+Alt+F7};

# Commands for developing Atom: init.config, packages; also for navigating existing packages/commands
Reload Atom = {Ctrl+s} Wait(100)  {Shift+Ctrl+Alt+F5};
Command Palette = {Ctrl+Shift+p};
Keyboard Shortcuts = {Ctrl+.};
Run Specs = {Ctrl+Alt+p};
Show Console = {Ctrl+Alt+i};

# Commands for developing D3: D3 visualizations, D3 Sandwiches
Save and Reload =  {Ctrl+s}  Wait(100) SendSystemKeys("{Alt+Tab}")  {Ctrl+r};
Save and Really Reload =  {Ctrl+s}  Wait(100) SendSystemKeys("{Alt+Tab}") {Ctrl+Shift+r};

# File commands
Find (File | Document) = {Ctrl+t};
Save As = {Ctrl+Shift+s};

# --- Navigation commands ------------------------------------
<delimiters> := (Quote = '"' | 'Single Quote' = "'" | Comma = ',' | 'Equal Sign' = '=' | Equals = '='
	| Period = '.' | Colon = ':' | 'Semi-Colon' = ';' | Hyphen = '-' |  Underscore = '_' | Slash = '/'
	| Paren = "(" | "Close Paren" = ")" | Parentheses = "(" | "Close Parentheses" = ")"
	| Bracket = '[' | "Close Bracket" = ']' | Brace = '{' | 'Close Brace' = '}'	);

Line 1..200 = {Ctrl+g} $1 {Enter};
(Left = 'b' | Right = 'f') Word = {Alt+$1};
Top = {Ctrl+Home};
Bottom = {Ctrl+End};
(Find = 'Right' | 'Insert After' = 'Right' | 'Insert Before' = 'Left') (Next = 'Enter' | Last = 'Shift+F3') <delimiters>
		= {Ctrl+f} $3 {$2} {Esc}   Wait(100) {$1};
Insert (Before = 'Left' | After = 'Right') <delimiters> =  {Ctrl+f} $2 {Enter} {Esc}   Wait(100) {$1};
Insert (Before = 'Left' | After = 'Right') 1..20 <delimiters> =  {Ctrl+f} $3 {Enter} {Esc}   Wait(100) {F3_$2} {Shift+F3} Wait(100) {$1};
Insert (Before = 'Left' | After = 'Right') Enda <delimiters> = {End} {Ctrl+f} $2 {Enter} {Esc} {Shift+F3}  Wait(100) {$1};

Fine Alpha <letters> = {Ctrl+f} $1 {Enter} {Esc}   Wait(100) {Left};
Insert (Before = 'Left' | After = 'Right') Alpha <letters> =  {Ctrl+f} $2 {Enter} {Esc}   Wait(100) {$1};
Fine Number 0..9 = {Ctrl+f} $1 {Enter} {Esc} Wait(100) {Right};
Find Again = {F3} {Right};
Find Again 1..20 Times = {F3_$1} {Right};

Find Symbol = {Ctrl+r};

(Set | Add | Clear | Hide | Delete) Bookmark = {Ctrl+Alt+F2};
(Next = 'F2' | Last = 'Shift+F2') Bookmark = {$1};

Next Window = {Ctrl+Tab};
Last Window = {Ctrl+Shift+Tab};

# --- Editing commands ----------------------------------------
Select Line = {Ctrl+l};
Select (Next = 'Down' | Last = 'Up') 1..20 Lines = {Home} {Shift+$1_$2};
(Join = 'j' | Indent = ']' | Dedent = '[')  That = {Ctrl+$1};
Move Line (Up | Down) 1..20  = {Ctrl+$1_$2};

Delete Word = {Ctrl+Del};
Delete 1..20 Words = {Ctrl+Del_$1};
Delete (Back | Last) Word = {Ctrl+Backspace};
Delete (Back | Last) 1..20 Words = {Ctrl+Backspace_$2};
Delete Enda Word = {End} {Ctrl+Backspace};

(Delete Line | Dede) = {Ctrl+Shift+k};
Delete 1..20 Lines = {Ctrl+Shift+k_$1};
(Cut = 'x' | Paste = 'v' | Copy = 'c') That = {Ctrl+$1};

Comment (That | Line) = {Ctrl+/};
Comment 1..20 Lines = {Home} {Shift+Down_$1} {Ctrl+/};

Select between (Delimiters | Brackets) ={Ctrl+m} {Ctrl+Alt+m};

Delete Tags = {Ctrl+Alt+F4};

Replace = {Ctrl+f};
Replace All = {Ctrl+Enter};

# --- Code commands -------------------------------------------
Start (Item = 'li' | List = 'ul' | HTML = 'html' | pre = "pre"
	| 'Bold ID' = 'bold-id' | 'Input' = 'ds-input-text'
	| 'Fix Line' = 'fix-current-line' | 'Fix Current Line' = 'fix-current-line'
	| Snippet = 'my-snippet' | 'Recipe Row' = 'recipe-row' | 'Recipe Play' = 'recipe-play') = $1 {Tab};
Start (para = '<p>' ) = $1;
Add (para = 'p') =  '<' $1 '>' {End} '</' $1 '>';
Replace With (para = 'p') = {Ctrl+Alt+F4} '<' $1 '>' {End} '</' $1 '>';

Start (Header | Heading) 1..7 = h $2 {Tab};
Add (Header | Heading) 1..7 =  '<h' $2 '>' {End} '</h' $2 '>';
Replace With (Header | Heading) 1..7 = {Ctrl+Alt+F4}  '<h' $2 '>' {End} '</h' $2 '>' {Home};
Mid HREF = '">';

Paste Function = 'function ' {Ctrl+v} ' {' {Enter} {Enter} '};' {Up};

Pre-Format Code =  {Ctrl+Alt+F5};

# TO ADD:
# adding HTML comments, using a snippet

# Voice commands for atom

include folders.vch;
include letters.vch;

Go Strawberry = {Ctrl+Alt+F8};
Go Blueberry ={Ctrl+Alt+o};
Go Banana = {Ctrl+Alt+F7};

# Commands for developing Atom: init.config, packages; also for navigating existing packages/commands
Reload Atom = {Ctrl+s} Wait(100)  {Shift+Ctrl+Alt+F5};
Command Palette = {Ctrl+Shift+p};
Keyboard Shortcuts = {Ctrl+.};
Run Specs = {Ctrl+Alt+p};
Show Console = {Ctrl+Alt+i};

# Commands for developing D3: D3 visualizations, D3 Sandwiches
Save and Reload =  {Ctrl+s}  Wait(100) SendSystemKeys("{Alt+Tab}")  {Ctrl+r};
Save and Really Reload =  {Ctrl+s}  Wait(100) SendSystemKeys("{Alt+Tab}") {Ctrl+Shift+r};

# File commands
Find (File | Document) = {Ctrl+t};
Save As = {Ctrl+Shift+s};

# --- Navigation commands ------------------------------------
<delimiters> := (Quote = '"' | 'Single Quote' = "'" | Comma = ',' | 'Equal Sign' = '=' | Equals = '='
	| Period = '.' | Colon = ':' | 'Semi-Colon' = ';' | Hyphen = '-' |  Underscore = '_' | Slash = '/'
	| Paren = "(" | "Close Paren" = ")"
	| Bracket = '[' | "Close Bracket" = ']' | Brace = '{' | 'Close Brace' = '}'	);

Line 1..200 = {Ctrl+g} $1 {Enter};
(Left = 'b' | Right = 'f') Word = {Alt+$1};
Top = {Ctrl+Home};
Bottom = {Ctrl+End};
(Find = 'Right' | 'Insert After' = 'Right' | 'Insert Before' = 'Left') (Next = 'Enter' | Last = 'Shift+F3') <delimiters>
		= {Ctrl+f} $3 {$2} {Esc}   Wait(100) {$1};
Insert (Before = 'Left' | After = 'Right') <delimiters> =  {Ctrl+f} $2 {Enter} {Esc}   Wait(100) {$1};
Insert (Before = 'Left' | After = 'Right') 1..20 <delimiters> =  {Ctrl+f} $3 {Enter} {Esc}   Wait(100) {F3_$2} {Shift+F3} Wait(100) {$1};
Insert (Before = 'Left' | After = 'Right') Enda <delimiters> = {End} {Ctrl+f} $2 {Enter} {Esc} {Shift+F3}  Wait(100) {$1};

Fine Alpha <letters> = {Ctrl+f} $1 {Enter} {Esc}   Wait(100) {Left};
Insert (Before = 'Left' | After = 'Right') Alpha <letters> =  {Ctrl+f} $2 {Enter} {Esc}   Wait(100) {$1};
Fine Number 0..9 = {Ctrl+f} $1 {Enter} {Esc} Wait(100) {Right};
Find Again = {F3} {Right};
Find Again 1..20 Times = {F3_$1} {Right};

Find Symbol = {Ctrl+r};

(Set | Add | Clear | Hide | Delete) Bookmark = {Ctrl+Alt+F2};
(Next = 'F2' | Last = 'Shift+F2') Bookmark = {$1};

Next Window = {Ctrl+Tab};
Last Window = {Ctrl+Shift+Tab};

# --- Editing commands ----------------------------------------
Select Line = {Ctrl+l};
Select (Next = 'Down' | Last = 'Up') 1..20 Lines = {Home} {Shift+$1_$2};
(Join = 'j' | Indent = ']' | Dedent = '[')  That = {Ctrl+$1};
Move Line (Up | Down) 1..20  = {Ctrl+$1_$2};

Delete Word = {Ctrl+Del};
Delete 1..20 Words = {Ctrl+Del_$1};
Delete (Back | Last) Word = {Ctrl+Backspace};
Delete (Back | Last) 1..20 Words = {Ctrl+Backspace_$2};
Delete Enda Word = {End} {Ctrl+Backspace};

(Delete Line | Dede) = {Ctrl+Shift+k};
Delete 1..20 Lines = {Ctrl+Shift+k_$1};
(Cut = 'x' | Paste = 'v' | Copy = 'c') That = {Ctrl+$1};

Comment (That | Line) = {Ctrl+/};
Comment 1..20 Lines = {Home} {Shift+Down_$1} {Ctrl+/};

Select between (Delimiters | Brackets) ={Ctrl+m} {Ctrl+Alt+m};

Delete Tags = {Ctrl+Alt+F4};

Replace = {Ctrl+f};
Replace All = {Ctrl+Enter};

# --- Code commands -------------------------------------------
Start (Item = 'li' | List = 'ul' | HTML = 'html' | pre = "pre"
	| 'Bold ID' = 'bold-id' | 'Input' = 'ds-input-text'
	| 'Fix Line' = 'fix-current-line' | 'Fix Current Line' = 'fix-current-line'
	| Snippet = 'my-snippet' | 'Recipe Row' = 'recipe-row' | 'Recipe Play' = 'recipe-play') = $1 {Tab};
Start (para = '<p>' ) = $1;
Add (para = 'p') =  '<' $1 '>' {End} '</' $1 '>';
Replace With (para = 'p') = {Ctrl+Alt+F4} '<' $1 '>' {End} '</' $1 '>';

Start (Header | Heading) 1..7 = h $2 {Tab};
Add (Header | Heading) 1..7 =  '<h' $2 '>' {End} '</h' $2 '>';
Replace With (Header | Heading) 1..7 = {Ctrl+Alt+F4}  '<h' $2 '>' {End} '</h' $2 '>' {Home};
Mid HREF = '">';

Paste Function = 'function ' {Ctrl+v} ' {' {Enter} {Enter} '};' {Up};

Pre-Format Code =  {Ctrl+Alt+F5};

# TO ADD:
# adding HTML comments, using a snippet