no longer operate with a dynamic whitelist.

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,
      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}",
      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);
}


#85: Don't pass ajax requests by default

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!
# For demonstration purposes this VCL will cache all front-end pages (including pages with cookies).

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
        set req.http.backend = "default";
        if (! std.healthy(req.backend_hint))
        {
            set req.backend_hint = alternative;
            set req.http.backend = "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" || req.url ~ "^/dashboard") {
                return (pass);
            }
        }

        # Do not cache ZendServer endpoint
        if (req.url ~ "^/ZendServer") {
            return (pass);
        }

        # Do not cache POST requests
        if (req.method == "POST") {
            return (pass);
        }

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

        # Do not cache if user is logged in
        if (req.http.Authorization || req.http.Authenticate) {
            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 {
        if(bereq.http.backend == "alternative")
        {
            set beresp.uncacheable = true;

            return(deliver);
        }

        # 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_hash
{
    hash_data(req.url);

    if (req.http.host) {
        hash_data(req.http.host);
    } else {
        hash_data(server.ip);
    }

    # Hash cookies for requests that have them
    if (req.http.Cookie) {
        hash_data(req.http.Cookie);
    }
}

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!
# For demonstration purposes this VCL will cache all front-end pages (including pages with cookies).

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
        set req.http.backend = "default";
        if (! std.healthy(req.backend_hint))
        {
            set req.backend_hint = alternative;
            set req.http.backend = "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" || req.url ~ "^/dashboard") {
                return (pass);
            }
        }

        # Do not cache ZendServer endpoint
        if (req.url ~ "^/ZendServer") {
            return (pass);
        }

        # Do not cache POST requests
        if (req.method == "POST") {
            return (pass);
        }

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

        # Do not cache if user is logged in
        if (req.http.Authorization || req.http.Authenticate) {
            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 {
        if(bereq.http.backend == "alternative")
        {
            set beresp.uncacheable = true;

            return(deliver);
        }

        # 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_hash
{
    hash_data(req.url);

    if (req.http.host) {
        hash_data(req.http.host);
    } else {
        hash_data(server.ip);
    }

    # Hash cookies for requests that have them
    if (req.http.Cookie) {
        hash_data(req.http.Cookie);
    }
}

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;
}


Updated instance name

Updated instance name

//
// Edge Architecture rules for Varnish 4
// http://www.w3.org/TR/2001/NOTE-edge-arch-20010804
//
// Author Karto Martin <source@karto.net>
// Copyright (c) 2015 Karto Martin. All Right Reserved.
// License The MIT License
//

#######################################################################
# Client side

#sub edge_arch_recv {
#}
#sub edge_arch_pipe {
#}
#sub edge_arch_pass {
#}
#sub edge_arch_hash {
#}
#sub edge_arch_purge {
#}
#sub edge_arch_hit {
#}
#sub edge_arch_miss {
#}
#sub edge_arch_deliver {
#}
#sub edge_arch_synth {
#}

#######################################################################
# Backend Fetch

sub edge_arch_backend_fetch {
    # Send Surrogate-Capability headers to announce ESI support to backend
    if (bereq.http.Surrogate-Capability) {
        set bereq.http.Surrogate-Capability = bereq.http.Surrogate-Capability + ", " + server.identity + {"="Surrogate/1.0 ESI/1.0""};
    }
    else {
        set bereq.http.Surrogate-Capability = server.identity + {"="Surrogate/1.0 ESI/1.0""};
    }
}
sub edge_arch_backend_response {

    ###
    # Parse Surrogate-Control for targeted or apply to all content=""
    #
    call edge_arch_set_surrogate_control_content;

    ###
    # There are content to process
    #
    if (beresp.http.Surrogate-Control-Content) {

        ###
        # Just remove Surrogate/1.0
        #
        if (beresp.http.Surrogate-Control-Content ~ "(?i)(^|\s)Surrogate/1\.0(\s|$)") {
            set beresp.http.Surrogate-Control-Content = regsub(regsub(beresp.http.Surrogate-Control-Content, "(^|\s)Surrogate/1\.0(?=\s|$)", ""), "^\s+", "");
        }

        ###
        # Set do_esi and remove ESI/1.0
        #
        if (beresp.http.Surrogate-Control-Content ~ "(?i)(^|\s)ESI/1\.0(\s|$)") {
            set beresp.http.Surrogate-Control-Content = regsub(regsub(beresp.http.Surrogate-Control-Content, "(^|\s)ESI/1\.0(?=\s|$)", ""), "^\s+", "");
            set beresp.do_esi = true;
        }

        ###
        # Update Surrogate-Control with changes to targeted or apply to all content=""
        #
        call edge_arch_set_surrogate_control;

        ###
        # Remove temp variable
        #
        unset beresp.http.Surrogate-Control-Content;

    }

}
sub edge_arch_backend_response_cacheable {

    ###
    # Only if caching have not already been determined
    #
    if ( ! beresp.http.X-Cacheable) {

        ###
        # Process Surrogate-Control and set X-Cacheable
        #
        call edge_arch_set_x_cacheable;

        ###
        # Surrogate-Control has a no-store directive
        #
        if (beresp.http.X-Cacheable ~ "^sc:no-store") {
            set beresp.ttl = 121s;
            set beresp.uncacheable = true;
        }

        ###
        # Surrogate-Control has a max-age directive
        #
        elsif (beresp.http.X-Cacheable ~ "^sc:max-age") {
            set beresp.ttl = std.duration(regsub(beresp.http.X-Cacheable, "^.*=(\d*)(\+\d*)?(;.*)?$", "\1")+"s", 0s);
            # Grace period
            if (beresp.http.Surrogate-Control ~ "=\d*\+\d*") {
                set beresp.grace = std.duration(regsub(beresp.http.X-Cacheable, "^.*=\d*\+(\d*)(;.*)?$", "\1")+"s", 0s);
            }
            // https://tools.ietf.org/html/rfc7234#section-4.2
            // half Last-Modified: Sat, 23 May 2015 22:09:02 +0000
            # Calculating Heuristic Grace
            elsif (beresp.http.Last-Modified && std.time(beresp.http.Last-Modified, now) < now) {
                if ((now - std.time(beresp.http.Last-Modified, now)) * 0.5 > beresp.grace &&
                        (now - std.time(beresp.http.Last-Modified, now)) * 0.5 < beresp.keep) {
                    if ("edge_arch_vtc" == server.identity) {
                        set beresp.grace = std.duration(regsub(""+((now - std.time(beresp.http.Last-Modified, now)) * 0.5), "\.\d+$", "s"), 0s);
                    }
                    else {
                        set beresp.grace = (now - std.time(beresp.http.Last-Modified, now)) * 0.5;
                    }
                }
                elsif ((now - std.time(beresp.http.Last-Modified, now)) * 0.5 >= beresp.keep) {
                    set beresp.grace = beresp.keep;
                }
            }
            set beresp.uncacheable = false;
        }

    }
}
#sub edge_arch_backend_error {
#}

#######################################################################
# Housekeeping

#sub edge_arch_init {
#}
#sub edge_arch_fini {
#}

#######################################################################
# Each Surrogate Control content

sub edge_arch_set_surrogate_control_content {
    if ("edge_arch_vtc" != server.identity && "www" != server.identity && "varnishdrupalol" != server.identity) {
        std.log("edge_arch_backend_response: Need configuration to do Surrogate-Control Targeting for "+server.identity+"");
        std.syslog(156, "edge_arch_backend_response: Need configuration to do Surrogate-Control Targeting for "+server.identity+"");
        header.append(beresp.http.Warning, "699 "+server.identity+{" "Need configuration to do Surrogate-Control Targeting for "}+server.identity+{"" ""}+now+{"""});
    }
    if ("edge_arch_vtc" == server.identity && beresp.http.Surrogate-Control ~ {"(?i)(^|,)\s*content\s*=\s*"[^"]*"\s*;\s*edge_arch_vtc\s*(,|$)"}) {
        set beresp.http.Surrogate-Control-Content = regsub(beresp.http.Surrogate-Control, {"(?i)^(?:.*,)?\s*content\s*=\s*"([^"]*)"\s*;\s*edge_arch_vtc\s*(?:,.*)?$"}, "\1");
    }
    elsif ("www" == server.identity && beresp.http.Surrogate-Control ~ {"(?i)(^|,)\s*content\s*=\s*"[^"]*"\s*;\s*www\s*(,|$)"}) {
        set beresp.http.Surrogate-Control-Content = regsub(beresp.http.Surrogate-Control, {"(?i)^(?:.*,)?\s*content\s*=\s*"([^"]*)"\s*;\s*www\s*(?:,.*)?$"}, "\1");
    }
    elsif ("varnishdrupalol" == server.identity && beresp.http.Surrogate-Control ~ {"(?i)(^|,)\s*content\s*=\s*"[^"]*"\s*;\s*varnishdrupalol\s*(,|$)"}) {
        set beresp.http.Surrogate-Control-Content = regsub(beresp.http.Surrogate-Control, {"(?i)^(?:.*,)?\s*content\s*=\s*"([^"]*)"\s*;\s*varnishdrupalol\s*(?:,.*)?$"}, "\1");
    }
    elsif (beresp.http.Surrogate-Control ~ {"(?i)(^|,)\s*content\s*=\s*"[^"]*"\s*(,|$)"}) {
        set beresp.http.Surrogate-Control-Content = regsub(beresp.http.Surrogate-Control, {"(?i)^(?:.*,)?\s*content\s*=\s*"([^"]*)"\s*(?:,.*)?$"}, "\1");
    }
}

sub edge_arch_set_surrogate_control {

    ###
    # Update ;edge_arch_vtc
    #
    if ("edge_arch_vtc" == server.identity && beresp.http.Surrogate-Control ~ {"(?i)(^|,)\s*content\s*=\s*"[^"]*"\s*;\s*edge_arch_vtc\s*(,|$)"}) {
        # Empty Surrogate-Control so unset
        if (beresp.http.Surrogate-Control-Content ~ "^\s*$" &&
                beresp.http.Surrogate-Control ~ {"(?i)^\s*content\s*=\s*"[^"]*"\s*;\s*edge_arch_vtc\s*$"}) {
            unset beresp.http.Surrogate-Control;
        }
        # Empty Surrogate-Control-Content so remove
        elsif (beresp.http.Surrogate-Control-Content ~ "^\s*$") {
            set beresp.http.Surrogate-Control = regsub(regsub(beresp.http.Surrogate-Control,
                {"(?i)(^|,)\s*content\s*=\s*"[^"]*"\s*;\s*edge_arch_vtc\s*(?=,|$)"}, ""), "^\s*(,\s*)?", "");
        }
        # Update changed Surrogate-Control-Content
        elsif (beresp.http.Surrogate-Control-Content != regsub(beresp.http.Surrogate-Control,
                {"(?i)^(?:.*,)?\s*content\s*=\s*"([^"]*)"\s*;\s*edge_arch_vtc\s*(?:,.*)?$"}, "\1")) {
            set beresp.http.Surrogate-Control = regsub(beresp.http.Surrogate-Control,
                {"(?i)((?:^|,)\s*content\s*=\s*")[^"]*("\s*;\s*edge_arch_vtc\s*)(?=,|$)"},
                "\1"+beresp.http.Surrogate-Control-Content+"\2");
        }
    }

    ###
    # Update ;www
    #
    elsif ("www" == server.identity && beresp.http.Surrogate-Control ~ {"(?i)(^|,)\s*content\s*=\s*"[^"]*"\s*;\s*www\s*(,|$)"}) {
        # Empty Surrogate-Control so unset
        if (beresp.http.Surrogate-Control-Content ~ "^\s*$" &&
                beresp.http.Surrogate-Control ~ {"(?i)^\s*content\s*=\s*"[^"]*"\s*;\s*www\s*$"}) {
            unset beresp.http.Surrogate-Control;
        }
        # Empty Surrogate-Control-Content so remove
        elsif (beresp.http.Surrogate-Control-Content ~ "^\s*$") {
            set beresp.http.Surrogate-Control = regsub(regsub(beresp.http.Surrogate-Control,
                {"(?i)(^|,)\s*content\s*=\s*"[^"]*"\s*;\s*www\s*(?=,|$)"}, ""), "^\s*(,\s*)?", "");
        }
        # Update changed Surrogate-Control-Content
        elsif (beresp.http.Surrogate-Control-Content != regsub(beresp.http.Surrogate-Control,
                {"(?i)^(?:.*,)?\s*content\s*=\s*"([^"]*)"\s*;\s*www\s*(?:,.*)?$"}, "\1")) {
            set beresp.http.Surrogate-Control = regsub(beresp.http.Surrogate-Control,
                {"(?i)((?:^|,)\s*content\s*=\s*")[^"]*("\s*;\s*www\s*)(?=,|$)"},
                "\1"+beresp.http.Surrogate-Control-Content+"\2");
        }
    }

    ###
    # Update ;varnishdrupalol
    #
    elsif ("varnishdrupalol" == server.identity && beresp.http.Surrogate-Control ~ {"(?i)(^|,)\s*content\s*=\s*"[^"]*"\s*;\s*varnishdrupalol\s*(,|$)"}) {
        # Empty Surrogate-Control so unset
        if (beresp.http.Surrogate-Control-Content ~ "^\s*$" &&
                beresp.http.Surrogate-Control ~ {"(?i)^\s*content\s*=\s*"[^"]*"\s*;\s*varnishdrupalol\s*$"}) {
            unset beresp.http.Surrogate-Control;
        }
        # Empty Surrogate-Control-Content so remove
        elsif (beresp.http.Surrogate-Control-Content ~ "^\s*$") {
            set beresp.http.Surrogate-Control = regsub(regsub(beresp.http.Surrogate-Control,
                {"(?i)(^|,)\s*content\s*=\s*"[^"]*"\s*;\s*varnishdrupalol\s*(?=,|$)"}, ""), "^\s*(,\s*)?", "");
        }
        # Update changed Surrogate-Control-Content
        elsif (beresp.http.Surrogate-Control-Content != regsub(beresp.http.Surrogate-Control,
                {"(?i)^(?:.*,)?\s*content\s*=\s*"([^"]*)"\s*;\s*varnishdrupalol\s*(?:,.*)?$"}, "\1")) {
            set beresp.http.Surrogate-Control = regsub(beresp.http.Surrogate-Control,
                {"(?i)((?:^|,)\s*content\s*=\s*")[^"]*("\s*;\s*varnishdrupalol\s*)(?=,|$)"},
                "\1"+beresp.http.Surrogate-Control-Content+"\2");
        }
    }

    ###
    # Update Apply to all
    #
    elsif (beresp.http.Surrogate-Control ~ {"(?i)(^|,)\s*content\s*=\s*"[^"]*"\s*(,|$)"}) {
        # Empty Surrogate-Control so unset
        if (beresp.http.Surrogate-Control-Content ~ "^\s*$" &&
                beresp.http.Surrogate-Control ~ {"(?i)^\s*content\s*=\s*"[^"]*"\s*$"}) {
            unset beresp.http.Surrogate-Control;
        }
        # Empty Surrogate-Control-Content so remove
        elsif (beresp.http.Surrogate-Control-Content ~ "^\s*$") {
            set beresp.http.Surrogate-Control = regsub(regsub(beresp.http.Surrogate-Control,
                {"(?i)(^|,)\s*content\s*=\s*"[^"]*"\s*(?=,|$)"}, ""), "^\s*(,\s*)?", "");
        }
        # Update changed Surrogate-Control-Content
        elsif (beresp.http.Surrogate-Control-Content != regsub(beresp.http.Surrogate-Control,
                {"(?i)^(?:.*,)?\s*content\s*=\s*"([^"]*)"\s*(?:,.*)?$"}, "\1")) {
            set beresp.http.Surrogate-Control = regsub(beresp.http.Surrogate-Control,
                {"(?i)((?:^|,)\s*content\s*=\s*")[^"]*("\s*)(?=,|$)"},
                "\1"+beresp.http.Surrogate-Control-Content+"\2");
        }
    }

}

#######################################################################
# Each Surrogate Control Targeted identities

sub edge_arch_set_x_cacheable {

    ###
    # Process ;edge_arch_vtc
    #
    if ("edge_arch_vtc" == server.identity && beresp.http.Surrogate-Control ~
            "(?i)(^|,)\s*(no-store|no-store-remote|max-age\s*=\s*\d+(\s*\+\s*\d+)?)\s*;\s*edge_arch_vtc\s*(,|$)") {
        if (beresp.http.Surrogate-Control ~ "(?i)(^|,)\s*no-store\s*;\s*edge_arch_vtc\s*(,|$)") {
            set beresp.http.X-Cacheable = "sc:no-store;edge_arch_vtc";
        }
        elsif (beresp.http.Surrogate-Control ~ "(?i)(^|,)\s*max-age\s*=\s*\d*(\s*\+\s*\d*)?\s*;\s*edge_arch_vtc\s*(,|$)") {
            set beresp.http.X-Cacheable = "sc:max-age="+regsub(beresp.http.Surrogate-Control,
                "(?i)^(?:.*,)?\s*max-age\s*=\s*(\d*)(?:\s*(\+)\s*(\d*))?\s*;\s*edge_arch_vtc\s*(?:,.*)?$",
                "\1\2\3")+";edge_arch_vtc";
        }
    }
    ###
    # Process ;www
    #
    elsif ("www" == server.identity && beresp.http.Surrogate-Control ~
            "(?i)(^|,)\s*(no-store|no-store-remote|max-age\s*=\s*\d+(\s*\+\s*\d+)?)\s*;\s*www\s*(,|$)") {
        if (beresp.http.Surrogate-Control ~ "(?i)(^|,)\s*no-store\s*;\s*www\s*(,|$)") {
            set beresp.http.X-Cacheable = "sc:no-store;www";
        }
        elsif (beresp.http.Surrogate-Control ~ "(?i)(^|,)\s*max-age\s*=\s*\d*(\s*\+\s*\d*)?\s*;\s*www\s*(,|$)") {
            set beresp.http.X-Cacheable = "sc:max-age="+regsub(beresp.http.Surrogate-Control,
                "(?i)^(?:.*,)?\s*max-age\s*=\s*(\d*)(?:\s*(\+)\s*(\d*))?\s*;\s*www\s*(?:,.*)?$",
                "\1\2\3")+";www";
        }
    }
    ###
    # Process ;varnishdrupalol
    #
    elsif ("varnishdrupalol" == server.identity && beresp.http.Surrogate-Control ~
            "(?i)(^|,)\s*(no-store|no-store-remote|max-age\s*=\s*\d+(\s*\+\s*\d+)?)\s*;\s*varnishdrupalol\s*(,|$)") {
        if (beresp.http.Surrogate-Control ~ "(?i)(^|,)\s*no-store\s*;\s*varnishdrupalol\s*(,|$)") {
            set beresp.http.X-Cacheable = "sc:no-store;varnishdrupalol";
        }
        elsif (beresp.http.Surrogate-Control ~ "(?i)(^|,)\s*max-age\s*=\s*\d*(\s*\+\s*\d*)?\s*;\s*varnishdrupalol\s*(,|$)") {
            set beresp.http.X-Cacheable = "sc:max-age="+regsub(beresp.http.Surrogate-Control,
                "(?i)^(?:.*,)?\s*max-age\s*=\s*(\d*)(?:\s*(\+)\s*(\d*))?\s*;\s*varnishdrupalol\s*(?:,.*)?$",
                "\1\2\3")+";varnishdrupalol";
        }
    }
    ###
    # Process apply to all others
    #
    elsif (beresp.http.Surrogate-Control ~ "(?i)(^|,)\s*(no-store|no-store-remote|max-age\s*=\s*\d+(\s*\+\s*\d+)?)\s*(,|$)") {
        if (beresp.http.Surrogate-Control ~ "(?i)(^|,)\s*no-store\s*(,|$)") {
            set beresp.http.X-Cacheable = "sc:no-store";
        }
        elsif (beresp.http.Surrogate-Control ~ "(?i)(^|,)\s*max-age\s*=\s*\d*(\s*\+\s*\d*)?\s*(,|$)") {
            set beresp.http.X-Cacheable = "sc:max-age="+regsub(beresp.http.Surrogate-Control,
                "(?i)^(?:.*,)?\s*max-age\s*=\s*(\d*)(?:\s*(\+)\s*(\d*))?\s*(?:,.*)?$",
                "\1\2\3")+";edge_arch_vtc";
        }
    }
}

//
// Edge Architecture rules for Varnish 4
// http://www.w3.org/TR/2001/NOTE-edge-arch-20010804
//
// Author Karto Martin <source@karto.net>
// Copyright (c) 2015 Karto Martin. All Right Reserved.
// License The MIT License
//

#######################################################################
# Client side

#sub edge_arch_recv {
#}
#sub edge_arch_pipe {
#}
#sub edge_arch_pass {
#}
#sub edge_arch_hash {
#}
#sub edge_arch_purge {
#}
#sub edge_arch_hit {
#}
#sub edge_arch_miss {
#}
#sub edge_arch_deliver {
#}
#sub edge_arch_synth {
#}

#######################################################################
# Backend Fetch

sub edge_arch_backend_fetch {
    # Send Surrogate-Capability headers to announce ESI support to backend
    if (bereq.http.Surrogate-Capability) {
        set bereq.http.Surrogate-Capability = bereq.http.Surrogate-Capability + ", " + server.identity + {"="Surrogate/1.0 ESI/1.0""};
    }
    else {
        set bereq.http.Surrogate-Capability = server.identity + {"="Surrogate/1.0 ESI/1.0""};
    }
}
sub edge_arch_backend_response {

    ###
    # Parse Surrogate-Control for targeted or apply to all content=""
    #
    call edge_arch_set_surrogate_control_content;

    ###
    # There are content to process
    #
    if (beresp.http.Surrogate-Control-Content) {

        ###
        # Just remove Surrogate/1.0
        #
        if (beresp.http.Surrogate-Control-Content ~ "(?i)(^|\s)Surrogate/1\.0(\s|$)") {
            set beresp.http.Surrogate-Control-Content = regsub(regsub(beresp.http.Surrogate-Control-Content, "(^|\s)Surrogate/1\.0(?=\s|$)", ""), "^\s+", "");
        }

        ###
        # Set do_esi and remove ESI/1.0
        #
        if (beresp.http.Surrogate-Control-Content ~ "(?i)(^|\s)ESI/1\.0(\s|$)") {
            set beresp.http.Surrogate-Control-Content = regsub(regsub(beresp.http.Surrogate-Control-Content, "(^|\s)ESI/1\.0(?=\s|$)", ""), "^\s+", "");
            set beresp.do_esi = true;
        }

        ###
        # Update Surrogate-Control with changes to targeted or apply to all content=""
        #
        call edge_arch_set_surrogate_control;

        ###
        # Remove temp variable
        #
        unset beresp.http.Surrogate-Control-Content;

    }

}
sub edge_arch_backend_response_cacheable {

    ###
    # Only if caching have not already been determined
    #
    if ( ! beresp.http.X-Cacheable) {

        ###
        # Process Surrogate-Control and set X-Cacheable
        #
        call edge_arch_set_x_cacheable;

        ###
        # Surrogate-Control has a no-store directive
        #
        if (beresp.http.X-Cacheable ~ "^sc:no-store") {
            set beresp.ttl = 121s;
            set beresp.uncacheable = true;
        }

        ###
        # Surrogate-Control has a max-age directive
        #
        elsif (beresp.http.X-Cacheable ~ "^sc:max-age") {
            set beresp.ttl = std.duration(regsub(beresp.http.X-Cacheable, "^.*=(\d*)(\+\d*)?(;.*)?$", "\1")+"s", 0s);
            # Grace period
            if (beresp.http.Surrogate-Control ~ "=\d*\+\d*") {
                set beresp.grace = std.duration(regsub(beresp.http.X-Cacheable, "^.*=\d*\+(\d*)(;.*)?$", "\1")+"s", 0s);
            }
            // https://tools.ietf.org/html/rfc7234#section-4.2
            // half Last-Modified: Sat, 23 May 2015 22:09:02 +0000
            # Calculating Heuristic Grace
            elsif (beresp.http.Last-Modified && std.time(beresp.http.Last-Modified, now) < now) {
                if ((now - std.time(beresp.http.Last-Modified, now)) * 0.5 > beresp.grace &&
                        (now - std.time(beresp.http.Last-Modified, now)) * 0.5 < beresp.keep) {
                    if ("edge_arch_vtc" == server.identity) {
                        set beresp.grace = std.duration(regsub(""+((now - std.time(beresp.http.Last-Modified, now)) * 0.5), "\.\d+$", "s"), 0s);
                    }
                    else {
                        set beresp.grace = (now - std.time(beresp.http.Last-Modified, now)) * 0.5;
                    }
                }
                elsif ((now - std.time(beresp.http.Last-Modified, now)) * 0.5 >= beresp.keep) {
                    set beresp.grace = beresp.keep;
                }
            }
            set beresp.uncacheable = false;
        }

    }
}
#sub edge_arch_backend_error {
#}

#######################################################################
# Housekeeping

#sub edge_arch_init {
#}
#sub edge_arch_fini {
#}

#######################################################################
# Each Surrogate Control content

sub edge_arch_set_surrogate_control_content {
    if ("edge_arch_vtc" != server.identity && "www" != server.identity && "drupal" != server.identity) {
        std.log("edge_arch_backend_response: Need configuration to do Surrogate-Control Targeting for "+server.identity+"");
        std.syslog(156, "edge_arch_backend_response: Need configuration to do Surrogate-Control Targeting for "+server.identity+"");
        header.append(beresp.http.Warning, "699 "+server.identity+{" "Need configuration to do Surrogate-Control Targeting for "}+server.identity+{"" ""}+now+{"""});
    }
    if ("edge_arch_vtc" == server.identity && beresp.http.Surrogate-Control ~ {"(?i)(^|,)\s*content\s*=\s*"[^"]*"\s*;\s*edge_arch_vtc\s*(,|$)"}) {
        set beresp.http.Surrogate-Control-Content = regsub(beresp.http.Surrogate-Control, {"(?i)^(?:.*,)?\s*content\s*=\s*"([^"]*)"\s*;\s*edge_arch_vtc\s*(?:,.*)?$"}, "\1");
    }
    elsif ("www" == server.identity && beresp.http.Surrogate-Control ~ {"(?i)(^|,)\s*content\s*=\s*"[^"]*"\s*;\s*www\s*(,|$)"}) {
        set beresp.http.Surrogate-Control-Content = regsub(beresp.http.Surrogate-Control, {"(?i)^(?:.*,)?\s*content\s*=\s*"([^"]*)"\s*;\s*www\s*(?:,.*)?$"}, "\1");
    }
    elsif ("drupal" == server.identity && beresp.http.Surrogate-Control ~ {"(?i)(^|,)\s*content\s*=\s*"[^"]*"\s*;\s*drupal\s*(,|$)"}) {
        set beresp.http.Surrogate-Control-Content = regsub(beresp.http.Surrogate-Control, {"(?i)^(?:.*,)?\s*content\s*=\s*"([^"]*)"\s*;\s*drupal\s*(?:,.*)?$"}, "\1");
    }
    elsif (beresp.http.Surrogate-Control ~ {"(?i)(^|,)\s*content\s*=\s*"[^"]*"\s*(,|$)"}) {
        set beresp.http.Surrogate-Control-Content = regsub(beresp.http.Surrogate-Control, {"(?i)^(?:.*,)?\s*content\s*=\s*"([^"]*)"\s*(?:,.*)?$"}, "\1");
    }
}

sub edge_arch_set_surrogate_control {

    ###
    # Update ;edge_arch_vtc
    #
    if ("edge_arch_vtc" == server.identity && beresp.http.Surrogate-Control ~ {"(?i)(^|,)\s*content\s*=\s*"[^"]*"\s*;\s*edge_arch_vtc\s*(,|$)"}) {
        # Empty Surrogate-Control so unset
        if (beresp.http.Surrogate-Control-Content ~ "^\s*$" &&
                beresp.http.Surrogate-Control ~ {"(?i)^\s*content\s*=\s*"[^"]*"\s*;\s*edge_arch_vtc\s*$"}) {
            unset beresp.http.Surrogate-Control;
        }
        # Empty Surrogate-Control-Content so remove
        elsif (beresp.http.Surrogate-Control-Content ~ "^\s*$") {
            set beresp.http.Surrogate-Control = regsub(regsub(beresp.http.Surrogate-Control,
                {"(?i)(^|,)\s*content\s*=\s*"[^"]*"\s*;\s*edge_arch_vtc\s*(?=,|$)"}, ""), "^\s*(,\s*)?", "");
        }
        # Update changed Surrogate-Control-Content
        elsif (beresp.http.Surrogate-Control-Content != regsub(beresp.http.Surrogate-Control,
                {"(?i)^(?:.*,)?\s*content\s*=\s*"([^"]*)"\s*;\s*edge_arch_vtc\s*(?:,.*)?$"}, "\1")) {
            set beresp.http.Surrogate-Control = regsub(beresp.http.Surrogate-Control,
                {"(?i)((?:^|,)\s*content\s*=\s*")[^"]*("\s*;\s*edge_arch_vtc\s*)(?=,|$)"},
                "\1"+beresp.http.Surrogate-Control-Content+"\2");
        }
    }

    ###
    # Update ;www
    #
    elsif ("www" == server.identity && beresp.http.Surrogate-Control ~ {"(?i)(^|,)\s*content\s*=\s*"[^"]*"\s*;\s*www\s*(,|$)"}) {
        # Empty Surrogate-Control so unset
        if (beresp.http.Surrogate-Control-Content ~ "^\s*$" &&
                beresp.http.Surrogate-Control ~ {"(?i)^\s*content\s*=\s*"[^"]*"\s*;\s*www\s*$"}) {
            unset beresp.http.Surrogate-Control;
        }
        # Empty Surrogate-Control-Content so remove
        elsif (beresp.http.Surrogate-Control-Content ~ "^\s*$") {
            set beresp.http.Surrogate-Control = regsub(regsub(beresp.http.Surrogate-Control,
                {"(?i)(^|,)\s*content\s*=\s*"[^"]*"\s*;\s*www\s*(?=,|$)"}, ""), "^\s*(,\s*)?", "");
        }
        # Update changed Surrogate-Control-Content
        elsif (beresp.http.Surrogate-Control-Content != regsub(beresp.http.Surrogate-Control,
                {"(?i)^(?:.*,)?\s*content\s*=\s*"([^"]*)"\s*;\s*www\s*(?:,.*)?$"}, "\1")) {
            set beresp.http.Surrogate-Control = regsub(beresp.http.Surrogate-Control,
                {"(?i)((?:^|,)\s*content\s*=\s*")[^"]*("\s*;\s*www\s*)(?=,|$)"},
                "\1"+beresp.http.Surrogate-Control-Content+"\2");
        }
    }

    ###
    # Update ;drupal
    #
    elsif ("drupal" == server.identity && beresp.http.Surrogate-Control ~ {"(?i)(^|,)\s*content\s*=\s*"[^"]*"\s*;\s*drupal\s*(,|$)"}) {
        # Empty Surrogate-Control so unset
        if (beresp.http.Surrogate-Control-Content ~ "^\s*$" &&
                beresp.http.Surrogate-Control ~ {"(?i)^\s*content\s*=\s*"[^"]*"\s*;\s*drupal\s*$"}) {
            unset beresp.http.Surrogate-Control;
        }
        # Empty Surrogate-Control-Content so remove
        elsif (beresp.http.Surrogate-Control-Content ~ "^\s*$") {
            set beresp.http.Surrogate-Control = regsub(regsub(beresp.http.Surrogate-Control,
                {"(?i)(^|,)\s*content\s*=\s*"[^"]*"\s*;\s*drupal\s*(?=,|$)"}, ""), "^\s*(,\s*)?", "");
        }
        # Update changed Surrogate-Control-Content
        elsif (beresp.http.Surrogate-Control-Content != regsub(beresp.http.Surrogate-Control,
                {"(?i)^(?:.*,)?\s*content\s*=\s*"([^"]*)"\s*;\s*drupal\s*(?:,.*)?$"}, "\1")) {
            set beresp.http.Surrogate-Control = regsub(beresp.http.Surrogate-Control,
                {"(?i)((?:^|,)\s*content\s*=\s*")[^"]*("\s*;\s*drupal\s*)(?=,|$)"},
                "\1"+beresp.http.Surrogate-Control-Content+"\2");
        }
    }

    ###
    # Update Apply to all
    #
    elsif (beresp.http.Surrogate-Control ~ {"(?i)(^|,)\s*content\s*=\s*"[^"]*"\s*(,|$)"}) {
        # Empty Surrogate-Control so unset
        if (beresp.http.Surrogate-Control-Content ~ "^\s*$" &&
                beresp.http.Surrogate-Control ~ {"(?i)^\s*content\s*=\s*"[^"]*"\s*$"}) {
            unset beresp.http.Surrogate-Control;
        }
        # Empty Surrogate-Control-Content so remove
        elsif (beresp.http.Surrogate-Control-Content ~ "^\s*$") {
            set beresp.http.Surrogate-Control = regsub(regsub(beresp.http.Surrogate-Control,
                {"(?i)(^|,)\s*content\s*=\s*"[^"]*"\s*(?=,|$)"}, ""), "^\s*(,\s*)?", "");
        }
        # Update changed Surrogate-Control-Content
        elsif (beresp.http.Surrogate-Control-Content != regsub(beresp.http.Surrogate-Control,
                {"(?i)^(?:.*,)?\s*content\s*=\s*"([^"]*)"\s*(?:,.*)?$"}, "\1")) {
            set beresp.http.Surrogate-Control = regsub(beresp.http.Surrogate-Control,
                {"(?i)((?:^|,)\s*content\s*=\s*")[^"]*("\s*)(?=,|$)"},
                "\1"+beresp.http.Surrogate-Control-Content+"\2");
        }
    }

}

#######################################################################
# Each Surrogate Control Targeted identities

sub edge_arch_set_x_cacheable {

    ###
    # Process ;edge_arch_vtc
    #
    if ("edge_arch_vtc" == server.identity && beresp.http.Surrogate-Control ~
            "(?i)(^|,)\s*(no-store|no-store-remote|max-age\s*=\s*\d+(\s*\+\s*\d+)?)\s*;\s*edge_arch_vtc\s*(,|$)") {
        if (beresp.http.Surrogate-Control ~ "(?i)(^|,)\s*no-store\s*;\s*edge_arch_vtc\s*(,|$)") {
            set beresp.http.X-Cacheable = "sc:no-store;edge_arch_vtc";
        }
        elsif (beresp.http.Surrogate-Control ~ "(?i)(^|,)\s*max-age\s*=\s*\d*(\s*\+\s*\d*)?\s*;\s*edge_arch_vtc\s*(,|$)") {
            set beresp.http.X-Cacheable = "sc:max-age="+regsub(beresp.http.Surrogate-Control,
                "(?i)^(?:.*,)?\s*max-age\s*=\s*(\d*)(?:\s*(\+)\s*(\d*))?\s*;\s*edge_arch_vtc\s*(?:,.*)?$",
                "\1\2\3")+";edge_arch_vtc";
        }
    }
    ###
    # Process ;www
    #
    elsif ("www" == server.identity && beresp.http.Surrogate-Control ~
            "(?i)(^|,)\s*(no-store|no-store-remote|max-age\s*=\s*\d+(\s*\+\s*\d+)?)\s*;\s*www\s*(,|$)") {
        if (beresp.http.Surrogate-Control ~ "(?i)(^|,)\s*no-store\s*;\s*www\s*(,|$)") {
            set beresp.http.X-Cacheable = "sc:no-store;www";
        }
        elsif (beresp.http.Surrogate-Control ~ "(?i)(^|,)\s*max-age\s*=\s*\d*(\s*\+\s*\d*)?\s*;\s*www\s*(,|$)") {
            set beresp.http.X-Cacheable = "sc:max-age="+regsub(beresp.http.Surrogate-Control,
                "(?i)^(?:.*,)?\s*max-age\s*=\s*(\d*)(?:\s*(\+)\s*(\d*))?\s*;\s*www\s*(?:,.*)?$",
                "\1\2\3")+";www";
        }
    }
    ###
    # Process ;drupal
    #
    elsif ("drupal" == server.identity && beresp.http.Surrogate-Control ~
            "(?i)(^|,)\s*(no-store|no-store-remote|max-age\s*=\s*\d+(\s*\+\s*\d+)?)\s*;\s*drupal\s*(,|$)") {
        if (beresp.http.Surrogate-Control ~ "(?i)(^|,)\s*no-store\s*;\s*drupal\s*(,|$)") {
            set beresp.http.X-Cacheable = "sc:no-store;drupal";
        }
        elsif (beresp.http.Surrogate-Control ~ "(?i)(^|,)\s*max-age\s*=\s*\d*(\s*\+\s*\d*)?\s*;\s*drupal\s*(,|$)") {
            set beresp.http.X-Cacheable = "sc:max-age="+regsub(beresp.http.Surrogate-Control,
                "(?i)^(?:.*,)?\s*max-age\s*=\s*(\d*)(?:\s*(\+)\s*(\d*))?\s*;\s*drupal\s*(?:,.*)?$",
                "\1\2\3")+";drupal";
        }
    }
    ###
    # Process apply to all others
    #
    elsif (beresp.http.Surrogate-Control ~ "(?i)(^|,)\s*(no-store|no-store-remote|max-age\s*=\s*\d+(\s*\+\s*\d+)?)\s*(,|$)") {
        if (beresp.http.Surrogate-Control ~ "(?i)(^|,)\s*no-store\s*(,|$)") {
            set beresp.http.X-Cacheable = "sc:no-store";
        }
        elsif (beresp.http.Surrogate-Control ~ "(?i)(^|,)\s*max-age\s*=\s*\d*(\s*\+\s*\d*)?\s*(,|$)") {
            set beresp.http.X-Cacheable = "sc:max-age="+regsub(beresp.http.Surrogate-Control,
                "(?i)^(?:.*,)?\s*max-age\s*=\s*(\d*)(?:\s*(\+)\s*(\d*))?\s*(?:,.*)?$",
                "\1\2\3")+";edge_arch_vtc";
        }
    }
}


Configuration for Varnish.

Configuration for Varnish.

# This is a Varnish 4.x VCL file
vcl 4.0;

backend default {
    .host = "127.0.0.1";
    .port = "3000";
}

backend sinatra {
    .host = "127.0.0.1";
    .port = "4567";
}

backend nodejs {
    .host = "127.0.0.1";
    .port = "5678";
}

# Handle the HTTP request received by the client
sub vcl_recv {
	if (req.url ~ "^/api/v1/products") {
        set req.backend_hint = sinatra;
    } else if (req.url ~ "^/api/v2/products") {
        set req.backend_hint = sinatra;
    } else if (req.url ~ "^/api/v3/products") {
        set req.backend_hint = nodejs;
    } else {
        set req.backend_hint = default;
    }
}

# This is a Varnish 4.x VCL file
vcl 4.0;

backend default {
    .host = "127.0.0.1";
    .port = "3000";
    # .probe = {
    #     .url = "/ping";
    #     .timeout   = 1s;
    #     .interval  = 10s;
    #     .window    = 5;
    #     .threshold = 2;
    # }
    # .first_byte_timeout     = 300s;   # How long to wait before we receive a first byte from our backend?
    # .connect_timeout        = 5s;     # How long to wait for a backend connection?
    # .between_bytes_timeout  = 2s;     # How long to wait between bytes received from our backend?
}

backend sinatra {
    .host = "127.0.0.1";
    .port = "4567";
}

# backend nodejs {
#     .host = "127.0.0.1";
#     .port = "5678";
# }

# Handle the HTTP request received by the client
sub vcl_recv {
	if (req.url ~ "^/api/v1/products") {
        set req.backend_hint = sinatra;
    } else if (req.url ~ "^/api/v2/products") {
        set req.backend_hint = sinatra;
    } else {
        set req.backend_hint = default;
    }
}


This needs to be a string

This needs to be a string

sub vcl_recv {
#FASTLY recv

    # We want to Force SSL for the WebUI by redirecting to the HTTPS version of
    # the page, however for API calls we want to return an error code directing
    # people to instead use HTTPS.
    if (!req.http.Fastly-SSL) {

        # The /simple/ and /packages/ API.
        if (req.url ~ "^/(simple|packages)") {
            error 803 "SSL is required";
        }

        # The Legacy JSON API.
        if (req.url ~ "^/pypi/.+/json$") {
            error 803 "SSL is required";
        }

        # The Legacy ?:action= API.
        if (req.url ~ "^/pypi.*(\?|&)=:action") {
            error 803 "SSL is required";
        }

        # If we're on the /pypi page and we've received something other than a
        # GET or HEAD request, then we have no way to determine if a particular
        # request is an API call or not because it'll be in the request body
        # and that isn't available to us here. So in those cases, we won't
        # do a redirect.
        if (req.url ~ "^/pypi") {
            if (req.request == "GET" || req.request == "HEAD") {
                error 801 "Force SSL";
            }
        }
        else {
            # This isn't a /pypi URL so we'll just unconditionally redirect to
            # HTTPS.
            error 801 "Force SSL";
        }
    }

    # Tell Varnish to use X-Forwarded-Proto to set the "real" protocol http
    #   or https.
    if (req.http.Fastly-SSL) {
        set req.http.X-Forwarded-Proto = "https";
    }
    else {
        set req.http.X-Forwarded-Proto = "http";
    }

    # Do not bother to attempt to run the caching mechanisms for methods that
    # are not generally safe to cache.
    if (req.request != "HEAD" &&
        req.request != "GET" &&
        req.request != "FASTLYPURGE") {
      return(pass);
    }

    # Finally, return the default lookup action.
    return(lookup);
}

sub vcl_fetch {
#FASTLY fetch

    # Trigger a "SSL is required" error if the backend has indicated to do so.
    if (beresp.http.X-Fastly-Error == "803") {
        error 803 "SSL is required";
    }

    # If we've gotten a 500 or a 503 from the backend, we'll go ahead and retry
    # the request.
    if ((beresp.status == 500 || beresp.status == 503) &&
            req.restarts < 1 &&
            (req.request == "GET" || req.request == "HEAD")) {
        restart;
    }

    # If we've restarted, then we'll record the number of restarts.
    if(req.restarts > 0 ) {
        set beresp.http.Fastly-Restarts = req.restarts;
    }

    # If there is a Set-Cookie header, we'll ensure that we do not cache the
    # response.
    if (beresp.http.Set-Cookie) {
        set req.http.Fastly-Cachetype = "SETCOOKIE";
        return (pass);
    }

    # If the response has the private Cache-Control directive then we won't
    # cache it.
    if (beresp.http.Cache-Control ~ "private") {
        set req.http.Fastly-Cachetype = "PRIVATE";
        return (pass);
    }

    # If we've gotten an error after the restarts we'll deliver the response
    # with a very short cache time.
    if (beresp.status == 500 || beresp.status == 503) {
        set req.http.Fastly-Cachetype = "ERROR";
        set beresp.ttl = 1s;
        set beresp.grace = 5s;
        return (deliver);
    }

    # Apply a default TTL if there isn't a max-age or s-maxage.
    if (beresp.http.Expires ||
            beresp.http.Surrogate-Control ~ "max-age" ||
            beresp.http.Cache-Control ~"(s-maxage|max-age)") {
        # Keep the ttl here
    }
    else {
        # Apply the default ttl
        set beresp.ttl = 3600s;
    }

    # Actually deliver the fetched response.
    return(deliver);
}

sub vcl_error {
#FASTLY error

    if (obj.status == 803) {
        set obj.status = 403;
        set obj.response = "SSL is required";
        set obj.http.Content-Type = "text/plain; charset=UTF-8";
        synthetic {"SSL is required."};
        return (deliver);
    }
}

sub vcl_recv {
#FASTLY recv

    # We want to Force SSL for the WebUI by redirecting to the HTTPS version of
    # the page, however for API calls we want to return an error code directing
    # people to instead use HTTPS.
    if (!req.http.Fastly-SSL) {

        # The /simple/ and /packages/ API.
        if (req.url ~ "^/(simple|packages)") {
            error 803 "SSL is required";
        }

        # The Legacy JSON API.
        if (req.url ~ "^/pypi/.+/json$") {
            error 803 "SSL is required";
        }

        # The Legacy ?:action= API.
        if (req.url ~ "^/pypi.*(\?|&)=:action") {
            error 803 "SSL is required";
        }

        # If we're on the /pypi page and we've received something other than a
        # GET or HEAD request, then we have no way to determine if a particular
        # request is an API call or not because it'll be in the request body
        # and that isn't available to us here. So in those cases, we won't
        # do a redirect.
        if (req.url ~ "^/pypi") {
            if (req.request == "GET" || req.request == "HEAD") {
                error 801 "Force SSL";
            }
        }
        else {
            # This isn't a /pypi URL so we'll just unconditionally redirect to
            # HTTPS.
            error 801 "Force SSL";
        }
    }

    # Tell Varnish to use X-Forwarded-Proto to set the "real" protocol http
    #   or https.
    if (req.http.Fastly-SSL) {
        set req.http.X-Forwarded-Proto = "https";
    }
    else {
        set req.http.X-Forwarded-Proto = "http";
    }

    # Do not bother to attempt to run the caching mechanisms for methods that
    # are not generally safe to cache.
    if (req.request != "HEAD" &&
        req.request != "GET" &&
        req.request != "FASTLYPURGE") {
      return(pass);
    }

    # Finally, return the default lookup action.
    return(lookup);
}

sub vcl_fetch {
#FASTLY fetch

    # Trigger a "SSL is required" error if the backend has indicated to do so.
    if (beresp.http.X-Fastly-Error == 803) {
        error 803 "SSL is required";
    }

    # If we've gotten a 500 or a 503 from the backend, we'll go ahead and retry
    # the request.
    if ((beresp.status == 500 || beresp.status == 503) &&
            req.restarts < 1 &&
            (req.request == "GET" || req.request == "HEAD")) {
        restart;
    }

    # If we've restarted, then we'll record the number of restarts.
    if(req.restarts > 0 ) {
        set beresp.http.Fastly-Restarts = req.restarts;
    }

    # If there is a Set-Cookie header, we'll ensure that we do not cache the
    # response.
    if (beresp.http.Set-Cookie) {
        set req.http.Fastly-Cachetype = "SETCOOKIE";
        return (pass);
    }

    # If the response has the private Cache-Control directive then we won't
    # cache it.
    if (beresp.http.Cache-Control ~ "private") {
        set req.http.Fastly-Cachetype = "PRIVATE";
        return (pass);
    }

    # If we've gotten an error after the restarts we'll deliver the response
    # with a very short cache time.
    if (beresp.status == 500 || beresp.status == 503) {
        set req.http.Fastly-Cachetype = "ERROR";
        set beresp.ttl = 1s;
        set beresp.grace = 5s;
        return (deliver);
    }

    # Apply a default TTL if there isn't a max-age or s-maxage.
    if (beresp.http.Expires ||
            beresp.http.Surrogate-Control ~ "max-age" ||
            beresp.http.Cache-Control ~"(s-maxage|max-age)") {
        # Keep the ttl here
    }
    else {
        # Apply the default ttl
        set beresp.ttl = 3600s;
    }

    # Actually deliver the fetched response.
    return(deliver);
}

sub vcl_error {
#FASTLY error

    if (obj.status == 803) {
        set obj.status = 403;
        set obj.response = "SSL is required";
        set obj.http.Content-Type = "text/plain; charset=UTF-8";
        synthetic {"SSL is required."};
        return (deliver);
    }
}


Tinker with TTLs on Cache-Control requests

backend default {
  .host = "127.0.0.1";
  .port = "80";
}

sub vcl_fetch {
  if (beresp.ttl < 1h) {
    if (beresp.http.Cache-Control ~ "(private|no-cache|no-store)") {
      set beresp.ttl = 5m;
    }
    else {
      set beresp.ttl = 1h;
    }
  }
}

sub vcl_recv {
  set req.http.Host = "www.trevorparker.com";
  if (req.http.X-Forwarded-Proto !~ "(?i)https") {
    set req.http.x-Redir-Url = "https://www.trevorparker.com" + req.url;
    error 750 req.http.x-Redir-Url;
  }
}

sub vcl_error {
  if (obj.status == 750) {
    set obj.http.Location = obj.response;
    set obj.status = 301;
    return(deliver);
  }
}

backend default {
  .host = "127.0.0.1";
  .port = "80";
}

sub vcl_fetch {
  set beresp.ttl = 1h;
}

sub vcl_recv {
  set req.http.Host = "www.trevorparker.com";
  if (req.http.X-Forwarded-Proto !~ "(?i)https") {
    set req.http.x-Redir-Url = "https://www.trevorparker.com" + req.url;
    error 750 req.http.x-Redir-Url;
  }
}

sub vcl_error {
  if (obj.status == 750) {
    set obj.http.Location = obj.response;
    set obj.status = 301;
    return(deliver);
  }
}


Make sure we don't maint mode admin pages in case user accidentally locks themselves out

    # VCL required to support maintenance mode. Don't maintenance mode admin pages and supporting assets
    if (table.lookup(magentomodule_config, "allow_super_users_during_maint", "0") == "1" &&
        !req.http.Fastly-Client-Ip ~ maint_allowlist &&
        !req.url ~ "^/(index\.php/)?####ADMIN_PATH####/" &&
        !req.url ~ "^/pub/static/") {
        error 503 "Maintenance mode";
    }

    # 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;
    }

    unset req.http.x-long-cache;

    # We want to force long cache times on any of the versioned assets
    if (req.url.path ~ "^/static/version\d*/") {
       set req.http.x-long-cache = "1";
    }

    # User's Cookie may contain some Magento Vary items we should vary on
    if (req.http.cookie:X-Magento-Vary ) {
        set req.http.X-Magento-Vary = req.http.cookie:X-Magento-Vary;
    } else {
        unset req.http.X-Magento-Vary;
    }

    ############################################################################################################
    # 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 {
                error 403;
            }
        } else {
            set req.http.Fastly-Purge-Requires-Auth = "1";
        }
    }

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

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

    # Make sure we lookup end user geo not shielding. More at https://docs.fastly.com/vcl/geolocation/#using-geographic-variables-with-shielding
    set client.geo.ip_override = req.http.Fastly-Client-IP;

    # geoip lookup
    if (req.url ~ "fastlyCdn/geoip/getaction/") {
        # check if GeoIP has been already processed by client. this normally happens before essential cookies are set.
        if (req.http.cookie:X-Magento-Vary || req.http.cookie:form_key) {
            error 980 "GeoIP already processed";
        } else {
            # append parameter with country code only if it doesn't exist already
            if ( req.url.qs !~ "country_code=" ) {
                set req.url = req.url "?country_code=" if ( req.http.geo_override, req.http.geo_override, client.geo.country_code);
            }
        }
    } else {
        # Per suggestions in https://github.com/sdinteractive/SomethingDigital_PageCacheParams
        # we'll strip out query parameters used in Google AdWords, Mailchimp tracking by default
        # and allow custom parameters to be set. List of parameters is configurable in admin
        set req.http.Magento-Original-URL = req.url;
        # Change the list of ignored parameters by configuring them in the Advanced section
        set req.url = querystring.regfilter(req.url, "^(####QUERY_PARAMETERS####)");
    }

    # Don't allow clients to force a pass
    if (req.restarts == 0) {
        unset req.http.x-pass;
        unset req.http.Rate-Limit;
    }

    # Pass on checkout URLs. Because it's a snippet we want to execute this after backend selection so we handle it
    # in the request condition
    if (!req.http.x-long-cache && req.url ~ "/(catalogsearch|checkout|customer/section/load)") {
        set req.http.x-pass = "1";
    # Pass all admin actions
    # ####ADMIN_PATH#### is replaced with value of frontName from app/etc/env.php
    } else if ( req.url ~ "^/(index\.php/)?####ADMIN_PATH####/" ) {
        set req.http.x-pass = "1";
    } else {
        # Sort the query arguments to increase cache hit ratio with query arguments that
        # may be out od order. In case you want to restore the unsorted URL add a snippet
        # after this one that sets req.url to req.http.Magento-Original-URL
        if ( !req.http.x-pass ) {
            set req.url = boltsort.sort(req.url);
        }
    }

    # static files are always cacheable. remove SSL flag and cookie
    if (req.http.x-long-cache || req.url ~ "^/(pub/)?(media|static)/.*") {
        unset req.http.Https;
        unset req.http.Cookie;
    }

    # VCL required to support maintenance mode
    if (table.lookup(magentomodule_config, "allow_super_users_during_maint", "0") == "1" && !req.http.Fastly-Client-Ip ~ maint_allowlist ) {
        error 503 "Maintenance mode";
    }

    # 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;
    }

    unset req.http.x-long-cache;

    # We want to force long cache times on any of the versioned assets
    if (req.url.path ~ "^/static/version\d*/") {
       set req.http.x-long-cache = "1";
    }

    # User's Cookie may contain some Magento Vary items we should vary on
    if (req.http.cookie:X-Magento-Vary ) {
        set req.http.X-Magento-Vary = req.http.cookie:X-Magento-Vary;
    } else {
        unset req.http.X-Magento-Vary;
    }

    ############################################################################################################
    # 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 {
                error 403;
            }
        } else {
            set req.http.Fastly-Purge-Requires-Auth = "1";
        }
    }

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

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

    # Make sure we lookup end user geo not shielding. More at https://docs.fastly.com/vcl/geolocation/#using-geographic-variables-with-shielding
    set client.geo.ip_override = req.http.Fastly-Client-IP;

    # geoip lookup
    if (req.url ~ "fastlyCdn/geoip/getaction/") {
        # check if GeoIP has been already processed by client. this normally happens before essential cookies are set.
        if (req.http.cookie:X-Magento-Vary || req.http.cookie:form_key) {
            error 980 "GeoIP already processed";
        } else {
            # append parameter with country code only if it doesn't exist already
            if ( req.url.qs !~ "country_code=" ) {
                set req.url = req.url "?country_code=" if ( req.http.geo_override, req.http.geo_override, client.geo.country_code);
            }
        }
    } else {
        # Per suggestions in https://github.com/sdinteractive/SomethingDigital_PageCacheParams
        # we'll strip out query parameters used in Google AdWords, Mailchimp tracking by default
        # and allow custom parameters to be set. List of parameters is configurable in admin
        set req.http.Magento-Original-URL = req.url;
        # Change the list of ignored parameters by configuring them in the Advanced section
        set req.url = querystring.regfilter(req.url, "^(####QUERY_PARAMETERS####)");
    }

    # Don't allow clients to force a pass
    if (req.restarts == 0) {
        unset req.http.x-pass;
        unset req.http.Rate-Limit;
    }

    # Pass on checkout URLs. Because it's a snippet we want to execute this after backend selection so we handle it
    # in the request condition
    if (!req.http.x-long-cache && req.url ~ "/(catalogsearch|checkout|customer/section/load)") {
        set req.http.x-pass = "1";
    # Pass all admin actions
    # ####ADMIN_PATH#### is replaced with value of frontName from app/etc/env.php
    } else if ( req.url ~ "^/(index\.php/)?####ADMIN_PATH####/" ) {
        set req.http.x-pass = "1";
    } else {
        # Sort the query arguments to increase cache hit ratio with query arguments that
        # may be out od order. In case you want to restore the unsorted URL add a snippet
        # after this one that sets req.url to req.http.Magento-Original-URL
        if ( !req.http.x-pass ) {
            set req.url = boltsort.sort(req.url);
        }
    }

    # static files are always cacheable. remove SSL flag and cookie
    if (req.http.x-long-cache || req.url ~ "^/(pub/)?(media|static)/.*") {
        unset req.http.Https;
        unset req.http.Cookie;
    }