update static files regex

update static files regex

/* Varnish 3 example config for Drupal 7 / Pressflow 6 & 7 */
# Original source: https://github.com/NITEMAN/varnish-bites/varnish4/drupal-base.vcl
# Copyright (c) 2015 Pedro González Serrano and individual contributors.
# MIT License

# Intended to be used both in simple production environments and with learning/teaching purposes.
# Loosely based on http://www.lullabot.com/sites/default/files/default_varnish3.vcl_.txt

/* Backend probes / healthchecks */
# See https://www.varnish-cache.org/docs/3.0/reference/vcl.html#backend-probes
probe basic {
  /* Only test that backend's IP serves content for '/' */
  # This might be a too heavy probe
  # .url = "/";
  /* Only test that backend's IP has apache working */
  # Nginx would fail this probe with a default config
  .request =
    "OPTIONS * HTTP/1.1"
    "Host: *"
    "Connection: close";
  .interval = 10s;
  .timeout = 2s;
  .window = 8;
  .threshold = 6;
}

/* Backend definitions.*/
# See https://www.varnish-cache.org/docs/3.0/reference/vcl.html#backend-declarations
backend default {
  /* Default backend on the same machine. WARNING: timeouts could be not big enought for certain POST request */
  .host = "127.0.0.1";
  .port = "8008";
  .connect_timeout = 60s;
  .first_byte_timeout = 60s;
  .between_bytes_timeout = 60s;
  .probe = basic;
}

/* Directors. */
# See https://www.varnish-cache.org/docs/3.0/reference/vcl.html#directors
# Empty in simple configs

/* Access Control Lists */
# See https://www.varnish-cache.org/docs/3.0/reference/vcl.html#acls
acl purge_ban {
  /* Simple access control list for allowing item purge for the self machine */
  "127.0.0.1"/32; // We can use '"localhost";' instead
}
acl allowed_monitors {
  /* Simple access control list for allowing item purge for the self machine */
  "127.0.0.1"/32; // We can use'"localhost";' instead
}
acl own_proxys {
  "127.0.0.1"/32; // We can use'"localhost";' instead
}

/* Custom routines */
# Empty in simple configs.
# We can name subs with no restrictions but as a task can need several chunks of code in diferent states,
# it's a good idea to identify what main sub will call each with a suffix.
# /* Example 301 client redirection removing "www" prefix from request */
# sub perm_redirections_recv {
#   if ( req.http.host ~ "^www.*$" ) {
#     error 751 "http://" + regsub(req.http.host, "^www\.", "") + req.url;
#   }
# }
# sub perm_redirections_error {
#   if (obj.status == 751) {
#     /* Get new URL from the response */
#     set obj.http.Location = obj.response;
#     /* Set HTTP 301 for permanent redirect */
#     set obj.status = 301;
#     return(deliver);
#   }
# }

/* VCL logigc overrides */
# Note that the built-in logic will be appended to our code if no return is performed before.
# Built-in logic is included commented out right after our's for reference purposes in that cases.
# See https://www.varnish-cache.org/trac/wiki/VCLExampleDefault
# See https://www.varnish-cache.org/docs/3.0/reference/vcl.html#subroutines

# 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.
sub vcl_recv {
  /* 0th: general bypass, general return & authorization checks */
  # Empty in simple configs.
  # Useful for debugging we can pipe or pass the request to default backend here to bypass completely Varnish.
  # return (pipe);
  # return (pass);
  # We can also return here a 200 Ok for network performance benchmarking.
  # error 200 "Ok";
  # Finally we can perform basic HTTP authentification here by example.
  # See http://blog.tenya.me/blog/2011/12/14/varnish-http-authentication/

  /* 1st: Check for Varnish special requests */
  # Purge logic.
  # See https://www.varnish-cache.org/docs/3.0/tutorial/purging.html#http-purges
  # See https://www.varnish-software.com/static/book/Cache_invalidation.html#removing-a-single-object
  if (req.request == "PURGE") {
    if (!client.ip ~ purge_ban) {
      error 405 "Not allowed.";
    }
    return (lookup);
  }
  # Ban logic. See https://www.varnish-cache.org/docs/3.0/tutorial/purging.html#bans
  if (req.request == "BAN") {
    if (!client.ip ~ purge_ban) {
      error 405 "Not allowed.";
    }
    ban("req.http.host == " + req.http.host +
      "&& req.url == " + req.url);
    error 200 "Ban added";
  }
  # Custom response implementation example in order to check that Varnish is working properly.
  # This is usefull for automatic monitoring with monit or when Varnish is behind another proxies like HAProxy.
  if ( ( req.http.host == "monitor.server.health"
      || req.http.host == "health.varnish" )
    && client.ip ~ allowed_monitors
    && ( req.request == "OPTIONS" || req.request == "GET" )
    ) {
    error 200 "Ok";
  }

  /* 2nd: Do some Varnish black magic such as custom client redirections */
  # Empty in simple configs.
  # call perm_redirections_recv;
  # Here we can also enforce SSL when Varnish run behind some SSL termination point.

  /* 3rd: Time for backend choice */
  # Empty in simple configs.

  /* 4th: Set custom headers for backend like X-Forwarded-For (copied from built-in logic) */
  if ( req.restarts == 0 ) {
    /* See also vcl_pipe section */
    if ( ! client.ip ~ own_proxys ) {
      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;
      }
    }
  }

  /* 5th: Bypass breakpoint 1 */
  # Useful for debugging we can now pipe or pass the request to backend with headers setted.
  # return (pipe);
  # return (pass);

  /* 6th: Decide if we should deal with a request (mostly copied from built-in logic) */
  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.http.Authorization) {
    /* Not cacheable by default */
    return (pass);
  }

  /* 7th: Access control for some URLs by ACL */
  # Empty in simple configs.
  # By example denial some URLs depending on client-ip, we'll need to define corresponding ACL 'internal'.
  # if (req.url ~ "^/(cron|install)\.php$" && !client.ip ~ internal) {
  #   # Have Varnish throw the error directly.
  #   error 403 "Forbidden.";
  #   # Use a custom error page that you've defined in Drupal at the path "404".
  #   # set req.url = "/403";
  # }

  /* 8th: Custom exceptions */
  # Host exception example:
  # if (req.http.host == "ejemplo.exception.com") {
  #     return (pass);
  # }
  # Drupal exceptions, edit if we want to cache some AJAX/AHAH request.
  # Add here filters for never cache URLs such as Payment Gateway's callbacks
  if (req.url ~ "^/status\.php$" ||
      req.url ~ "^/update\.php$" ||
      req.url ~ "^/ooyala/ping$" ||
      req.url ~ "^/admin/build/features" ||
      req.url ~ "^/info/.*$" ||
      req.url ~ "^/flag/.*$" ||
      req.url ~ "^.*/ajax/.*$" ||
      req.url ~ "^.*/ahah/.*$") {
    /* Do not cache these paths */
    return (pass);
  }
  # Pipe these paths directly to backend for streaming.
  if (req.url ~ "^/admin/content/backup_migrate/export") {
    return (pipe);
  }
  if (req.url ~ "^/system/files") {
    return (pipe);
  }

  /* 9th: Enable grace mode */
  # See https://www.varnish-cache.org/docs/3.0/tutorial/handling_misbehaving_servers.html#grace-mode
  if (! req.backend.healthy) {
    /* Use a longer grace period if all backends are down */
    set req.grace = 1h;
    /* Use anonymous, cached pages if all backends are down. */
    unset req.http.Cookie;
    # TO-DO: Add sick marker
  } else {
    /* Allow the backend to serve up stale content if it is responding slowly. */
    set req.grace = 30s;
  }

  /* 10th: Deal with compression and the Accept-Encoding header */
  # Althought Varnish 3 handles gziped content itself by default, just to be sure we want to
  # remove Accept-Encoding for some compressed formats.
  # See https://www.varnish-cache.org/docs/3.0/phk/gzip.html#what-does-http-gzip-support-do
  # See https://www.varnish-cache.org/docs/3.0/tutorial/compression.html
  # See https://www.varnish-cache.org/docs/3.0/reference/varnishd.html?highlight=http_gzip_support
  # See (for older configs) https://www.varnish-cache.org/trac/wiki/VCLExampleNormalizeAcceptEncoding
  if (req.http.Accept-Encoding) {
    if (req.url ~ "\.(jpg|jpeg|png|gif|gz|tgz|bz2|tbz|mp3|ogg|swf|mp4|flv|zip|rar)$") {
      /* Already compressed formats, no sense trying to compress again */
      remove req.http.Accept-Encoding;
    }
  }

  /* 11th: Another header manipulation */
  # Empty in simple configs.
  # We could add here a custom header grouping User-agent families.

  /* 12th: Cookie removal */
  # Always cache the following static file types for all users.
  # Use with care if we control certain downloads depending on cookies.
  # Be carefull also if appending .htm[l] via Drupal's clean URLs.
  if ( req.url ~ "(?i)\.(png|gif|jpeg|jpg|ico|swf|css|js|html|htm|gz|tgz|bz2|tbz|mp3|ogg|zip|rar|otf|ttf|eot|woff|svg|pdf)(\?(itok=)?[a-z0-9_=\.\-]+)?$"
    && req.url !~ "/system/storage/serve"
  ) {
      unset req.http.Cookie;
  }
  # Remove all cookies that backend doesn't need to know about.
  # See https://www.varnish-cache.org/trac/wiki/VCLExampleRemovingSomeCookies
  if (req.http.Cookie) {
    /* Warning: Not a pretty solution */
    /* Prefix header containing cookies with ';' */
    set req.http.Cookie = ";" + req.http.Cookie;
    /* Remove any spaces after ';' in header containing cookies */
    set req.http.Cookie = regsuball(req.http.Cookie, "; +", ";");
    /* Prefix cookies we want to preserve with one space */
    /* 'S{1,2}ESS[a-z0-9]+' is the regular expression matching a Drupal session cookie ({1,2} added for HTTPS support) */
    /* 'NO_CACHE' is usually set after a POST request to make sure issuing user see the results of his post */
    /* 'OATMEAL' & 'CHOCOLATECHIP' are special cookies used by Drupal's Bakery module to provide Single Sign On */
    /* Keep in mind we should add here any cookie that should reach the backend such as splahs avoiding cookies */
    set req.http.Cookie = regsuball(req.http.Cookie, ";(S{1,2}ESS[a-z0-9]+|NO_CACHE|OATMEAL|CHOCOLATECHIP)=", "; \1=");
    /* Remove from the header any single Cookie not prefixed with a space until next ';' separator */
    set req.http.Cookie = regsuball(req.http.Cookie, ";[^ ][^;]*", "");
    /* Remove any '; ' at the start or the end of the header */
    set req.http.Cookie = regsuball(req.http.Cookie, "^[; ]+|[; ]+$", "");

    if (req.http.Cookie == "") {
      /* If there are no remaining cookies, remove the cookie header. */
      unset req.http.Cookie;
    }
  }

  /* 13th: Session cookie & special cookies bypass caching stage */
  # As we might want to cache some requests, hashed with its cookies,
  # we don't simply pass when some cookies remain present at this point.
  # Instead we look for request that must be passed due to the cookie header.
  if (req.http.Cookie ~ "SESS" ||
      req.http.Cookie ~ "SSESS" ||
      req.http.Cookie ~ "NO_CACHE" ||
      req.http.Cookie ~ "OATMEAL" ||
      req.http.Cookie ~ "CHOCOLATECHIP") {
    return (pass);
  }

  /* 14th: Bypass breakpoint 2 */
  # Useful for debugging we can now pipe or pass the request to backend to bypass cache.
  # return (pipe);
  # return (pass);

  /* 15th: Bypass built-in logic */
  # We make sure no built-in logic is processed after ours returning a lookup.
  return (lookup);
}

# vcl_pipe: Called upon entering pipe mode.
# In this mode, the request is passed on to the backend,
# and any further data from either client or backend is passed on unaltered until either end closes the connection.
sub vcl_pipe {
  /* Prevent connection re-using for piped requests */
  # Note that only the first request to the backend will have X-Forwarded-For set.
  # As we use X-Forwarded-For and want to have it set for all requests,
  # we have to make sure connection won't be reused after the request.
  # It is not set by default as it might break some broken web applications, like IIS with NTLM authentication.
  # See https://www.varnish-cache.org/trac/wiki/VCLExamplePipe
  # TO-DO: make sure this is compatible with websockets piping,
  # reference https://www.varnish-cache.org/docs/3.0/tutorial/websockets.html
  set bereq.http.connection = "close";

  /* Bypass built-in logic */
  # We make sure no built-in logic is processed after ours returning at this point.
  return (pipe);
}

# vcl_pass: Called upon entering pass mode.
# In this mode, the request is passed on to the backend, and the backend's response is passed on to the client,
# but is not entered into the cache.
# Subsequent requests submitted over the same client connection are handled normally.
# sub vcl_pass {
#     return (pass);
# }

# vcl_hash: You may call hash_data() on the data you would like to add to the hash.
# Hash is used by Varnish to uniquely identify objects.
sub vcl_hash {
  /* Hash cookie data */
  # As requests with same URL and host can produce diferent results when issued with different cookies,
  # we need to store items hashed with the associated cookies. Note that cookies are already sanitized when we reach this point.
  if (req.http.Cookie) {
    /* Include cookie in cache hash */
    hash_data(req.http.Cookie);
  }

  /* Custom header hashing */
  # Empty in simple configs.
  # Example for caching differents object versions by device previously detected (when static content could also vary):
  # if (req.http.X-UA-Device) {
  #   hash_data(req.http.X-UA-Device);
  # }
  # Example for caching diferent object versions by X-Forwarded-Proto, trying to be smart about what kind of request
  # could generate diffetent responses.
  # if (req.http.X-Forwarded-Proto &&
  #     req.url !~ "(?i)\.(png|gif|jpeg|jpg|ico|gz|tgz|bz2|tbz|mp3|ogg|zip|rar|otf|ttf|eot|woff|svg|pdf)$") {
  #   hash_data(req.http.X-Forwarded-Proto);
  # }

  /* Continue with built-in logic */
  # We want built-in logic to be processed after ours so we don't call return.
}
# sub vcl_hash {
#     hash_data(req.url);
#     if (req.http.host) {
#         hash_data(req.http.host);
#     } else {
#         hash_data(server.ip);
#     }
#     return (hash);
# }

# vcl_hit: Called after a cache lookup if the requested document was found in the cache.
sub vcl_hit {
  /* Check for Varnish special requests */
  # Purge logic.
  # See https://www.varnish-cache.org/docs/3.0/tutorial/purging.html#http-purges
  # See https://www.varnish-software.com/static/book/Cache_invalidation.html#removing-a-single-object
  if (req.request == "PURGE") {
    purge;
    error 200 "Purged.";
  }

  /* Continue with built-in logic */
  # We want built-in logic to be processed after ours so we don't call return.
}
# sub vcl_hit {
#     return (deliver);
# }

# vcl_miss: Called after a cache lookup if the requested document was not found in the cache.
# Its purpose is to decide whether or not to attempt to retrieve the document from the backend, and which backend to use.
sub vcl_miss {
  /* Check for Varnish special requests */
  # Purge logic.
  # See https://www.varnish-cache.org/docs/3.0/tutorial/purging.html#http-purges
  # See https://www.varnish-software.com/static/book/Cache_invalidation.html#removing-a-single-object
  if (req.request == "PURGE") {
    purge;
    error 200 "Purged.";
  }

  /* Continue with built-in logic */
  # We want built-in logic to be processed after ours so we don't call return.
}
# sub vcl_miss {
#     return (fetch);
# }

# vcl_fetch: Called after a document has been successfully retrieved from the backend.
sub vcl_fetch {
  /* Caching exceptions */
  # Varnish will cache objects with response codes: 200, 203, 300, 301, 302, 307, 404 & 410.
  # See https://www.varnish-software.com/static/book/VCL_Basics.html#the-initial-value-of-beresp-ttl
  # Drupal's Imagecache module can return a 307 redirection to the requested url itself and, depending on Drupal's cache settings,
  # this could lead to a redirection loop being cached for a long time but also we want Varnish to shield a little the backend.
  # See http://drupal.org/node/1248010
  # See http://drupal.org/node/310656
  if (beresp.status == 307 &&
      /* TO-DO: verify that this work better than 'req.url ~ "imagecache"' */
      beresp.http.Location == req.url &&
      beresp.ttl > 5s) {
    set beresp.ttl = 5s;
    set beresp.http.cache-control = "max-age=5";
  }

  /* Enable grace mode. Related with our 9th stage on vcl_recv */
  # See https://www.varnish-cache.org/docs/3.0/tutorial/handling_misbehaving_servers.html#grace-mode
  set beresp.grace = 1h;

  /* Enable saint mode. Related with our 9th stage on vcl_recv */
  # See https://www.varnish-cache.org/docs/3.0/tutorial/handling_misbehaving_servers.html#saint-mode
  if (beresp.status == 500) {
    set beresp.saintmode = 20s;
    # TO-DO: consider not restarting POST requests as seen on https://www.varnish-cache.org/trac/wiki/VCLExampleSaintMode
    return(restart);
  }

  /* Strip cookies from the following static file types for all users. Related with our 12th stage on vcl_recv */
  if (req.url ~ "(?i)\.(png|gif|jpeg|jpg|ico|swf|css|js|html|htm|gz|tgz|bz2|tbz|mp3|ogg|zip|rar|otf|ttf|eot|woff|svg|pdf)(\?(itok=)?[a-z0-9_=\.\-]+)?$") {
    unset beresp.http.set-cookie;
  }

  /* Gzip response */
  # Empty in simple configs
  # Use Varnish to Gzip respone, if suitable, before storing it on cache
  # See https://www.varnish-cache.org/docs/3.0/tutorial/compression.html
  # See https://www.varnish-cache.org/docs/3.0/phk/gzip.html
  # if (! beresp.http.Content-Encoding &&
  #     (beresp.http.content-type ~ "text" ||
  #      beresp.http.content-type ~ "application/x-javascript" ||
  #      beresp.http.content-type ~ "application/javascript" ||
  #      beresp.http.content-type ~ "application/rss+xml" ||
  #      beresp.http.content-type ~ "application/xml" ||
  #      beresp.http.content-type ~ "Application/JSON")
  # ) {
  #   set beresp.do_gzip = true;
  #   if ( beresp.http.Vary ) {
  #     if ( ! beresp.http.Vary ~ "Accept-Encoding" ) {
  #       set beresp.http.Vary = beresp.http.Vary + ",Accept-Encoding";
  #     }
  #   } else {
  #     set beresp.http.Vary = "Accept-Encoding";
  #   }
  # }

  /* Debugging headers */
  # Please consider the risks of showing publicly this information, we can wrap this with an ACL
  # We can add the name of the backend that has processed the request:
  # set beresp.http.X-Backend = beresp.backend.name;
  # We can use a header to tell if the object was gziped by Varnish
  # if (beresp.do_gzip) {
  #   set beresp.http.X-Varnish-Gzipped = "yes";
  # } else {
  #   set beresp.http.X-Varnish-Gizipped = "no";
  # }
  # We can also add headers informing whether the object is cacheable or not and why.
  # https://www.varnish-cache.org/trac/wiki/VCLExampleHitMissHeader#Varnish3.0
  if (beresp.ttl <= 0s) {
    /* Varnish determined the object was not cacheable */
    set beresp.http.X-Cacheable = "NO:Not Cacheable";
  } elsif (req.http.Cookie ~ "(SESS|SSESS|NO_CACHE|OATMEAL|CHOCOLATECHIP)") {
    /* We don't wish to cache content for logged in users or with certain cookies. Related with our 9th stage on vcl_recv */
    set beresp.http.X-Cacheable = "NO:Cookies";
    # return(hit_for_pass);
  } elsif (beresp.http.Cache-Control ~ "private") {
    /* We are respecting the Cache-Control=private header from the backend */
    set beresp.http.X-Cacheable = "NO:Cache-Control=private";
    # return(hit_for_pass);
  } else {
    /* Varnish determined the object was cacheable */
    set beresp.http.X-Cacheable = "YES";
  }

  /* Further header manipulation */
  # Empty in simple configs.
  # We can also unset some headers to prevent information disclosure and save some cache space
  # unset beresp.http.Server;
  # unset beresp.http.X-Powered-By;

  /* Continue with built-in logic */
  # We want built-in logic to be processed after ours so we don't call return.
}
# sub vcl_fetch {
#     if (beresp.ttl <= 0s ||
#         beresp.http.Set-Cookie ||
#         beresp.http.Vary == "*") {
#               /*
#                * Mark as "Hit-For-Pass" for the next 2 minutes
#                */
#               set beresp.ttl = 120 s;
#               return (hit_for_pass);
#     }
#     return (deliver);
# }

# vcl_deliver: Called before an object is delivered to the client
sub vcl_deliver {
  /* Debugging headers */
  # Please consider the risks of showing publicly this information, we can wrap this with an ACL
  # Add whether the object is a cache hit or miss and the number of hits for the object.
  # https://www.varnish-cache.org/trac/wiki/VCLExampleHitMissHeader#Addingaheaderindicatinghitmiss
  if (obj.hits > 0) {
    set resp.http.X-Cache = "HIT";
    set resp.http.X-Cache-Hits = obj.hits;
  } else {
    set resp.http.X-Cache = "MISS";
    /* Show the results of cookie sanitization */
    set resp.http.X-Cookie = req.http.Cookie;
  }
  # TO-DO: Add sick marker
  # Restart count
  if ( req.restarts > 0) {
    set resp.http.X-Restarts = req.restarts;
  }
  # Add the Varnish server hostname
  set resp.http.X-Varnish-Server = server.hostname;
  # If we have setted a custom header with device family detected we can show it:
  # if (req.http.X-UA-Device) {
  #   set resp.http.X-UA-Device = req.http.X-UA-Device;
  # }
  # If we have recived a custom header with the protocol in the request we can show it:
  # if (req.http.X-Forwarded-Proto) {
  #   set resp.http.X-Forwarded-Proto = req.http.X-Forwarded-Proto;
  # }

  /* Vary header manipulation */
  # Empty in simple configs.
  # By example, if we are storing & serving diferent objects depending on User-Agent header we must set the correct Vary header:
  # if (resp.http.Vary) {
  #   set resp.http.Vary = resp.http.Vary + ",User-Agent";
  # } else {
  #   set resp.http.Vary = "User-Agent";
  # }

  /* Fake headers */
  # Empty in simple configs
  # We can fake server headers here, by example:
  # set resp.http.Server = "Deep thought";
  # set resp.http.X-Powered-By = "BOFH";
  # Or have some fun with headers:
  # See http://www.nextthing.org/archives/2005/08/07/fun-with-http-headers
  # See http://royal.pingdom.com/2012/08/15/fun-and-unusual-http-response-headers/
  # set resp.http.X-Thank-You = "for bothering to look at my HTTP headers";
  # set resp.http.X-Answer = "42";

  /* Continue with built-in logic */
  # We want built-in logic to be processed after ours so we don't call return.
}
# sub vcl_deliver {
#     return (deliver);
# }

# vcl_error: Called when we hit an error, either explicitly or implicitly due to backend or internal errors.
sub vcl_error {
  /* Avoid DOS vulnerability CVE-2013-4484 */
  # See https://www.varnish-cache.org/lists/pipermail/varnish-announce/2013-October/000686.html
  if (obj.status == 400 || obj.status == 413) {
    return(deliver);
  }

  /* Do some Varnish black magic such as custom client redirections */
  # Empty in simple configs.
  # call perm_redirections_error;

  /* Try to restart request in case of failure */
  # Note that max_restarts defaults to 4
  # See https://www.varnish-cache.org/trac/wiki/VCLExampleRestarts
  if (obj.status == 503 && req.restarts < 4) {
    set obj.http.X-Restarts = req.restarts;
    return(restart);
  }

  /* Set common headers for synthetic responses */
  set obj.http.Content-Type = "text/html; charset=utf-8";

  /* HTTP Authentification client request */
  # Empty in simple configs. See http://blog.tenya.me/blog/2011/12/14/varnish-http-authentication/

  /* We're using error 200 for monitoring puposes */
  # Consider adding some analytics stuff to trace accesses
  if (obj.status == 200) {
    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">
      <html>
        <head>
          <title>"} + obj.status + " " + obj.response + {"</title>
      </head>
      <body><h1>"} + obj.status + ": " + obj.response + {"</h1></body>
    "};
    return(deliver);
  }

  /* Error page & refresh / redirections */
  # We have plenty of choices when we have to serve an error to the client,
  # from the default error page to javascript black magic or plain redirections.
  # Adding some external statistic javascript to track failures served to clients is strongly suggested.
  # We can't use external resources on synthetic content, everything must be inlined.
  # If we need to include images we can embed them in base64 encoding.
  # Here is the default error page for Varnish 3 (not so pretty)
  set obj.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">
<html>
  <head>
    <title>"} + obj.status + " " + obj.response + {"</title>
  </head>
  <body>
    <h1>Error "} + obj.status + " " + obj.response + {"</h1>
    <p>"} + obj.response + {"</p>
    <h3>Guru Meditation:</h3>
    <p>XID: "} + req.xid + {"</p>
    <hr>
    <hr>
    <p>Varnish cache server</p>
  </body>
</html>
"};

  /* Bypass built-in logic */
  # We make sure no built-in logic is processed after ours returning at this point.
  return (deliver);
}

# vcl_init: Called when VCL is loaded, before any requests pass through it. Typically used to initialize VMODs.
# sub vcl_init {
#       return (ok);
# }

# vcl_fini: Called when VCL is discarded only after all requests have exited the VCL. Typically used to clean up VMODs.
# sub vcl_fini {
#       return (ok);
# }

/* Varnish 3 example config for Drupal 7 / Pressflow 6 & 7 */
# Original source: https://github.com/NITEMAN/varnish-bites/varnish4/drupal-base.vcl
# Copyright (c) 2015 Pedro González Serrano and individual contributors.
# MIT License

# Intended to be used both in simple production environments and with learning/teaching purposes.
# Loosely based on http://www.lullabot.com/sites/default/files/default_varnish3.vcl_.txt

/* Backend probes / healthchecks */
# See https://www.varnish-cache.org/docs/3.0/reference/vcl.html#backend-probes
probe basic {
  /* Only test that backend's IP serves content for '/' */
  # This might be a too heavy probe
  # .url = "/";
  /* Only test that backend's IP has apache working */
  # Nginx would fail this probe with a default config
  .request =
    "OPTIONS * HTTP/1.1"
    "Host: *"
    "Connection: close";
  .interval = 10s;
  .timeout = 2s;
  .window = 8;
  .threshold = 6;
}

/* Backend definitions.*/
# See https://www.varnish-cache.org/docs/3.0/reference/vcl.html#backend-declarations
backend default {
  /* Default backend on the same machine. WARNING: timeouts could be not big enought for certain POST request */
  .host = "127.0.0.1";
  .port = "8008";
  .connect_timeout = 60s;
  .first_byte_timeout = 60s;
  .between_bytes_timeout = 60s;
  .probe = basic;
}

/* Directors. */
# See https://www.varnish-cache.org/docs/3.0/reference/vcl.html#directors
# Empty in simple configs

/* Access Control Lists */
# See https://www.varnish-cache.org/docs/3.0/reference/vcl.html#acls
acl purge_ban {
  /* Simple access control list for allowing item purge for the self machine */
  "127.0.0.1"/32; // We can use '"localhost";' instead
}
acl allowed_monitors {
  /* Simple access control list for allowing item purge for the self machine */
  "127.0.0.1"/32; // We can use'"localhost";' instead
}
acl own_proxys {
  "127.0.0.1"/32; // We can use'"localhost";' instead
}

/* Custom routines */
# Empty in simple configs.
# We can name subs with no restrictions but as a task can need several chunks of code in diferent states,
# it's a good idea to identify what main sub will call each with a suffix.
# /* Example 301 client redirection removing "www" prefix from request */
# sub perm_redirections_recv {
#   if ( req.http.host ~ "^www.*$" ) {
#     error 751 "http://" + regsub(req.http.host, "^www\.", "") + req.url;
#   }
# }
# sub perm_redirections_error {
#   if (obj.status == 751) {
#     /* Get new URL from the response */
#     set obj.http.Location = obj.response;
#     /* Set HTTP 301 for permanent redirect */
#     set obj.status = 301;
#     return(deliver);
#   }
# }

/* VCL logigc overrides */
# Note that the built-in logic will be appended to our code if no return is performed before.
# Built-in logic is included commented out right after our's for reference purposes in that cases.
# See https://www.varnish-cache.org/trac/wiki/VCLExampleDefault
# See https://www.varnish-cache.org/docs/3.0/reference/vcl.html#subroutines

# 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.
sub vcl_recv {
  /* 0th: general bypass, general return & authorization checks */
  # Empty in simple configs.
  # Useful for debugging we can pipe or pass the request to default backend here to bypass completely Varnish.
  # return (pipe);
  # return (pass);
  # We can also return here a 200 Ok for network performance benchmarking.
  # error 200 "Ok";
  # Finally we can perform basic HTTP authentification here by example.
  # See http://blog.tenya.me/blog/2011/12/14/varnish-http-authentication/

  /* 1st: Check for Varnish special requests */
  # Purge logic.
  # See https://www.varnish-cache.org/docs/3.0/tutorial/purging.html#http-purges
  # See https://www.varnish-software.com/static/book/Cache_invalidation.html#removing-a-single-object
  if (req.request == "PURGE") {
    if (!client.ip ~ purge_ban) {
      error 405 "Not allowed.";
    }
    return (lookup);
  }
  # Ban logic. See https://www.varnish-cache.org/docs/3.0/tutorial/purging.html#bans
  if (req.request == "BAN") {
    if (!client.ip ~ purge_ban) {
      error 405 "Not allowed.";
    }
    ban("req.http.host == " + req.http.host +
      "&& req.url == " + req.url);
    error 200 "Ban added";
  }
  # Custom response implementation example in order to check that Varnish is working properly.
  # This is usefull for automatic monitoring with monit or when Varnish is behind another proxies like HAProxy.
  if ( ( req.http.host == "monitor.server.health"
      || req.http.host == "health.varnish" )
    && client.ip ~ allowed_monitors
    && ( req.request == "OPTIONS" || req.request == "GET" )
    ) {
    error 200 "Ok";
  }

  /* 2nd: Do some Varnish black magic such as custom client redirections */
  # Empty in simple configs.
  # call perm_redirections_recv;
  # Here we can also enforce SSL when Varnish run behind some SSL termination point.

  /* 3rd: Time for backend choice */
  # Empty in simple configs.

  /* 4th: Set custom headers for backend like X-Forwarded-For (copied from built-in logic) */
  if ( req.restarts == 0 ) {
    /* See also vcl_pipe section */
    if ( ! client.ip ~ own_proxys ) {
      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;
      }
    }
  }

  /* 5th: Bypass breakpoint 1 */
  # Useful for debugging we can now pipe or pass the request to backend with headers setted.
  # return (pipe);
  # return (pass);

  /* 6th: Decide if we should deal with a request (mostly copied from built-in logic) */
  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.http.Authorization) {
    /* Not cacheable by default */
    return (pass);
  }

  /* 7th: Access control for some URLs by ACL */
  # Empty in simple configs.
  # By example denial some URLs depending on client-ip, we'll need to define corresponding ACL 'internal'.
  # if (req.url ~ "^/(cron|install)\.php$" && !client.ip ~ internal) {
  #   # Have Varnish throw the error directly.
  #   error 403 "Forbidden.";
  #   # Use a custom error page that you've defined in Drupal at the path "404".
  #   # set req.url = "/403";
  # }

  /* 8th: Custom exceptions */
  # Host exception example:
  # if (req.http.host == "ejemplo.exception.com") {
  #     return (pass);
  # }
  # Drupal exceptions, edit if we want to cache some AJAX/AHAH request.
  # Add here filters for never cache URLs such as Payment Gateway's callbacks
  if (req.url ~ "^/status\.php$" ||
      req.url ~ "^/update\.php$" ||
      req.url ~ "^/ooyala/ping$" ||
      req.url ~ "^/admin/build/features" ||
      req.url ~ "^/info/.*$" ||
      req.url ~ "^/flag/.*$" ||
      req.url ~ "^.*/ajax/.*$" ||
      req.url ~ "^.*/ahah/.*$") {
    /* Do not cache these paths */
    return (pass);
  }
  # Pipe these paths directly to backend for streaming.
  if (req.url ~ "^/admin/content/backup_migrate/export") {
    return (pipe);
  }
  if (req.url ~ "^/system/files") {
    return (pipe);
  }

  /* 9th: Enable grace mode */
  # See https://www.varnish-cache.org/docs/3.0/tutorial/handling_misbehaving_servers.html#grace-mode
  if (! req.backend.healthy) {
    /* Use a longer grace period if all backends are down */
    set req.grace = 1h;
    /* Use anonymous, cached pages if all backends are down. */
    unset req.http.Cookie;
    # TO-DO: Add sick marker
  } else {
    /* Allow the backend to serve up stale content if it is responding slowly. */
    set req.grace = 30s;
  }

  /* 10th: Deal with compression and the Accept-Encoding header */
  # Althought Varnish 3 handles gziped content itself by default, just to be sure we want to
  # remove Accept-Encoding for some compressed formats.
  # See https://www.varnish-cache.org/docs/3.0/phk/gzip.html#what-does-http-gzip-support-do
  # See https://www.varnish-cache.org/docs/3.0/tutorial/compression.html
  # See https://www.varnish-cache.org/docs/3.0/reference/varnishd.html?highlight=http_gzip_support
  # See (for older configs) https://www.varnish-cache.org/trac/wiki/VCLExampleNormalizeAcceptEncoding
  if (req.http.Accept-Encoding) {
    if (req.url ~ "\.(jpg|jpeg|png|gif|gz|tgz|bz2|tbz|mp3|ogg|swf|mp4|flv|zip|rar)$") {
      /* Already compressed formats, no sense trying to compress again */
      remove req.http.Accept-Encoding;
    }
  }

  /* 11th: Another header manipulation */
  # Empty in simple configs.
  # We could add here a custom header grouping User-agent families.

  /* 12th: Cookie removal */
  # Always cache the following static file types for all users.
  # Use with care if we control certain downloads depending on cookies.
  # Be carefull also if appending .htm[l] via Drupal's clean URLs.
  if (req.url ~ "(?i)\.(png|gif|jpeg|jpg|ico|swf|css|js|html|htm|gz|tgz|bz2|tbz|mp3|ogg|zip|rar|otf|ttf|eot|woff|svg|pdf)(\?[a-z0-9]+)?$") {
      unset req.http.Cookie;
  }
  # Remove all cookies that backend doesn't need to know about.
  # See https://www.varnish-cache.org/trac/wiki/VCLExampleRemovingSomeCookies
  if (req.http.Cookie) {
    /* Warning: Not a pretty solution */
    /* Prefix header containing cookies with ';' */
    set req.http.Cookie = ";" + req.http.Cookie;
    /* Remove any spaces after ';' in header containing cookies */
    set req.http.Cookie = regsuball(req.http.Cookie, "; +", ";");
    /* Prefix cookies we want to preserve with one space */
    /* 'S{1,2}ESS[a-z0-9]+' is the regular expression matching a Drupal session cookie ({1,2} added for HTTPS support) */
    /* 'NO_CACHE' is usually set after a POST request to make sure issuing user see the results of his post */
    /* 'OATMEAL' & 'CHOCOLATECHIP' are special cookies used by Drupal's Bakery module to provide Single Sign On */
    /* Keep in mind we should add here any cookie that should reach the backend such as splahs avoiding cookies */
    set req.http.Cookie = regsuball(req.http.Cookie, ";(S{1,2}ESS[a-z0-9]+|NO_CACHE|OATMEAL|CHOCOLATECHIP)=", "; \1=");
    /* Remove from the header any single Cookie not prefixed with a space until next ';' separator */
    set req.http.Cookie = regsuball(req.http.Cookie, ";[^ ][^;]*", "");
    /* Remove any '; ' at the start or the end of the header */
    set req.http.Cookie = regsuball(req.http.Cookie, "^[; ]+|[; ]+$", "");

    if (req.http.Cookie == "") {
      /* If there are no remaining cookies, remove the cookie header. */
      unset req.http.Cookie;
    }
  }

  /* 13th: Session cookie & special cookies bypass caching stage */
  # As we might want to cache some requests, hashed with its cookies,
  # we don't simply pass when some cookies remain present at this point.
  # Instead we look for request that must be passed due to the cookie header.
  if (req.http.Cookie ~ "SESS" ||
      req.http.Cookie ~ "SSESS" ||
      req.http.Cookie ~ "NO_CACHE" ||
      req.http.Cookie ~ "OATMEAL" ||
      req.http.Cookie ~ "CHOCOLATECHIP") {
    return (pass);
  }

  /* 14th: Bypass breakpoint 2 */
  # Useful for debugging we can now pipe or pass the request to backend to bypass cache.
  # return (pipe);
  # return (pass);

  /* 15th: Bypass built-in logic */
  # We make sure no built-in logic is processed after ours returning a lookup.
  return (lookup);
}

# vcl_pipe: Called upon entering pipe mode.
# In this mode, the request is passed on to the backend,
# and any further data from either client or backend is passed on unaltered until either end closes the connection.
sub vcl_pipe {
  /* Prevent connection re-using for piped requests */
  # Note that only the first request to the backend will have X-Forwarded-For set.
  # As we use X-Forwarded-For and want to have it set for all requests,
  # we have to make sure connection won't be reused after the request.
  # It is not set by default as it might break some broken web applications, like IIS with NTLM authentication.
  # See https://www.varnish-cache.org/trac/wiki/VCLExamplePipe
  # TO-DO: make sure this is compatible with websockets piping,
  # reference https://www.varnish-cache.org/docs/3.0/tutorial/websockets.html
  set bereq.http.connection = "close";

  /* Bypass built-in logic */
  # We make sure no built-in logic is processed after ours returning at this point.
  return (pipe);
}

# vcl_pass: Called upon entering pass mode.
# In this mode, the request is passed on to the backend, and the backend's response is passed on to the client,
# but is not entered into the cache.
# Subsequent requests submitted over the same client connection are handled normally.
# sub vcl_pass {
#     return (pass);
# }

# vcl_hash: You may call hash_data() on the data you would like to add to the hash.
# Hash is used by Varnish to uniquely identify objects.
sub vcl_hash {
  /* Hash cookie data */
  # As requests with same URL and host can produce diferent results when issued with different cookies,
  # we need to store items hashed with the associated cookies. Note that cookies are already sanitized when we reach this point.
  if (req.http.Cookie) {
    /* Include cookie in cache hash */
    hash_data(req.http.Cookie);
  }

  /* Custom header hashing */
  # Empty in simple configs.
  # Example for caching differents object versions by device previously detected (when static content could also vary):
  # if (req.http.X-UA-Device) {
  #   hash_data(req.http.X-UA-Device);
  # }
  # Example for caching diferent object versions by X-Forwarded-Proto, trying to be smart about what kind of request
  # could generate diffetent responses.
  # if (req.http.X-Forwarded-Proto &&
  #     req.url !~ "(?i)\.(png|gif|jpeg|jpg|ico|gz|tgz|bz2|tbz|mp3|ogg|zip|rar|otf|ttf|eot|woff|svg|pdf)$") {
  #   hash_data(req.http.X-Forwarded-Proto);
  # }

  /* Continue with built-in logic */
  # We want built-in logic to be processed after ours so we don't call return.
}
# sub vcl_hash {
#     hash_data(req.url);
#     if (req.http.host) {
#         hash_data(req.http.host);
#     } else {
#         hash_data(server.ip);
#     }
#     return (hash);
# }

# vcl_hit: Called after a cache lookup if the requested document was found in the cache.
sub vcl_hit {
  /* Check for Varnish special requests */
  # Purge logic.
  # See https://www.varnish-cache.org/docs/3.0/tutorial/purging.html#http-purges
  # See https://www.varnish-software.com/static/book/Cache_invalidation.html#removing-a-single-object
  if (req.request == "PURGE") {
    purge;
    error 200 "Purged.";
  }

  /* Continue with built-in logic */
  # We want built-in logic to be processed after ours so we don't call return.
}
# sub vcl_hit {
#     return (deliver);
# }

# vcl_miss: Called after a cache lookup if the requested document was not found in the cache.
# Its purpose is to decide whether or not to attempt to retrieve the document from the backend, and which backend to use.
sub vcl_miss {
  /* Check for Varnish special requests */
  # Purge logic.
  # See https://www.varnish-cache.org/docs/3.0/tutorial/purging.html#http-purges
  # See https://www.varnish-software.com/static/book/Cache_invalidation.html#removing-a-single-object
  if (req.request == "PURGE") {
    purge;
    error 200 "Purged.";
  }

  /* Continue with built-in logic */
  # We want built-in logic to be processed after ours so we don't call return.
}
# sub vcl_miss {
#     return (fetch);
# }

# vcl_fetch: Called after a document has been successfully retrieved from the backend.
sub vcl_fetch {
  /* Caching exceptions */
  # Varnish will cache objects with response codes: 200, 203, 300, 301, 302, 307, 404 & 410.
  # See https://www.varnish-software.com/static/book/VCL_Basics.html#the-initial-value-of-beresp-ttl
  # Drupal's Imagecache module can return a 307 redirection to the requested url itself and, depending on Drupal's cache settings,
  # this could lead to a redirection loop being cached for a long time but also we want Varnish to shield a little the backend.
  # See http://drupal.org/node/1248010
  # See http://drupal.org/node/310656
  if (beresp.status == 307 &&
      /* TO-DO: verify that this work better than 'req.url ~ "imagecache"' */
      beresp.http.Location == req.url &&
      beresp.ttl > 5s) {
    set beresp.ttl = 5s;
    set beresp.http.cache-control = "max-age=5";
  }

  /* Enable grace mode. Related with our 9th stage on vcl_recv */
  # See https://www.varnish-cache.org/docs/3.0/tutorial/handling_misbehaving_servers.html#grace-mode
  set beresp.grace = 1h;

  /* Enable saint mode. Related with our 9th stage on vcl_recv */
  # See https://www.varnish-cache.org/docs/3.0/tutorial/handling_misbehaving_servers.html#saint-mode
  if (beresp.status == 500) {
    set beresp.saintmode = 20s;
    # TO-DO: consider not restarting POST requests as seen on https://www.varnish-cache.org/trac/wiki/VCLExampleSaintMode
    return(restart);
  }

  /* Strip cookies from the following static file types for all users. Related with our 12th stage on vcl_recv */
  if (req.url ~ "(?i)\.(png|gif|jpeg|jpg|ico|swf|css|js|html|htm|gz|tgz|bz2|tbz|mp3|ogg|zip|rar|otf|ttf|eot|woff|svg|pdf)(\?[a-z0-9]+)?$") {
    unset beresp.http.set-cookie;
  }

  /* Gzip response */
  # Empty in simple configs
  # Use Varnish to Gzip respone, if suitable, before storing it on cache
  # See https://www.varnish-cache.org/docs/3.0/tutorial/compression.html
  # See https://www.varnish-cache.org/docs/3.0/phk/gzip.html
  # if (! beresp.http.Content-Encoding &&
  #     (beresp.http.content-type ~ "text" ||
  #      beresp.http.content-type ~ "application/x-javascript" ||
  #      beresp.http.content-type ~ "application/javascript" ||
  #      beresp.http.content-type ~ "application/rss+xml" ||
  #      beresp.http.content-type ~ "application/xml" ||
  #      beresp.http.content-type ~ "Application/JSON")
  # ) {
  #   set beresp.do_gzip = true;
  #   if ( beresp.http.Vary ) {
  #     if ( ! beresp.http.Vary ~ "Accept-Encoding" ) {
  #       set beresp.http.Vary = beresp.http.Vary + ",Accept-Encoding";
  #     }
  #   } else {
  #     set beresp.http.Vary = "Accept-Encoding";
  #   }
  # }

  /* Debugging headers */
  # Please consider the risks of showing publicly this information, we can wrap this with an ACL
  # We can add the name of the backend that has processed the request:
  # set beresp.http.X-Backend = beresp.backend.name;
  # We can use a header to tell if the object was gziped by Varnish
  # if (beresp.do_gzip) {
  #   set beresp.http.X-Varnish-Gzipped = "yes";
  # } else {
  #   set beresp.http.X-Varnish-Gizipped = "no";
  # }
  # We can also add headers informing whether the object is cacheable or not and why.
  # https://www.varnish-cache.org/trac/wiki/VCLExampleHitMissHeader#Varnish3.0
  if (beresp.ttl <= 0s) {
    /* Varnish determined the object was not cacheable */
    set beresp.http.X-Cacheable = "NO:Not Cacheable";
  } elsif (req.http.Cookie ~ "(SESS|SSESS|NO_CACHE|OATMEAL|CHOCOLATECHIP)") {
    /* We don't wish to cache content for logged in users or with certain cookies. Related with our 9th stage on vcl_recv */
    set beresp.http.X-Cacheable = "NO:Cookies";
    # return(hit_for_pass);
  } elsif (beresp.http.Cache-Control ~ "private") {
    /* We are respecting the Cache-Control=private header from the backend */
    set beresp.http.X-Cacheable = "NO:Cache-Control=private";
    # return(hit_for_pass);
  } else {
    /* Varnish determined the object was cacheable */
    set beresp.http.X-Cacheable = "YES";
  }

  /* Further header manipulation */
  # Empty in simple configs.
  # We can also unset some headers to prevent information disclosure and save some cache space
  # unset beresp.http.Server;
  # unset beresp.http.X-Powered-By;

  /* Continue with built-in logic */
  # We want built-in logic to be processed after ours so we don't call return.
}
# sub vcl_fetch {
#     if (beresp.ttl <= 0s ||
#         beresp.http.Set-Cookie ||
#         beresp.http.Vary == "*") {
#               /*
#                * Mark as "Hit-For-Pass" for the next 2 minutes
#                */
#               set beresp.ttl = 120 s;
#               return (hit_for_pass);
#     }
#     return (deliver);
# }

# vcl_deliver: Called before an object is delivered to the client
sub vcl_deliver {
  /* Debugging headers */
  # Please consider the risks of showing publicly this information, we can wrap this with an ACL
  # Add whether the object is a cache hit or miss and the number of hits for the object.
  # https://www.varnish-cache.org/trac/wiki/VCLExampleHitMissHeader#Addingaheaderindicatinghitmiss
  if (obj.hits > 0) {
    set resp.http.X-Cache = "HIT";
    set resp.http.X-Cache-Hits = obj.hits;
  } else {
    set resp.http.X-Cache = "MISS";
    /* Show the results of cookie sanitization */
    set resp.http.X-Cookie = req.http.Cookie;
  }
  # TO-DO: Add sick marker
  # Restart count
  if ( req.restarts > 0) {
    set resp.http.X-Restarts = req.restarts;
  }
  # Add the Varnish server hostname
  set resp.http.X-Varnish-Server = server.hostname;
  # If we have setted a custom header with device family detected we can show it:
  # if (req.http.X-UA-Device) {
  #   set resp.http.X-UA-Device = req.http.X-UA-Device;
  # }
  # If we have recived a custom header with the protocol in the request we can show it:
  # if (req.http.X-Forwarded-Proto) {
  #   set resp.http.X-Forwarded-Proto = req.http.X-Forwarded-Proto;
  # }

  /* Vary header manipulation */
  # Empty in simple configs.
  # By example, if we are storing & serving diferent objects depending on User-Agent header we must set the correct Vary header:
  # if (resp.http.Vary) {
  #   set resp.http.Vary = resp.http.Vary + ",User-Agent";
  # } else {
  #   set resp.http.Vary = "User-Agent";
  # }

  /* Fake headers */
  # Empty in simple configs
  # We can fake server headers here, by example:
  # set resp.http.Server = "Deep thought";
  # set resp.http.X-Powered-By = "BOFH";
  # Or have some fun with headers:
  # See http://www.nextthing.org/archives/2005/08/07/fun-with-http-headers
  # See http://royal.pingdom.com/2012/08/15/fun-and-unusual-http-response-headers/
  # set resp.http.X-Thank-You = "for bothering to look at my HTTP headers";
  # set resp.http.X-Answer = "42";

  /* Continue with built-in logic */
  # We want built-in logic to be processed after ours so we don't call return.
}
# sub vcl_deliver {
#     return (deliver);
# }

# vcl_error: Called when we hit an error, either explicitly or implicitly due to backend or internal errors.
sub vcl_error {
  /* Avoid DOS vulnerability CVE-2013-4484 */
  # See https://www.varnish-cache.org/lists/pipermail/varnish-announce/2013-October/000686.html
  if (obj.status == 400 || obj.status == 413) {
    return(deliver);
  }

  /* Do some Varnish black magic such as custom client redirections */
  # Empty in simple configs.
  # call perm_redirections_error;

  /* Try to restart request in case of failure */
  # Note that max_restarts defaults to 4
  # See https://www.varnish-cache.org/trac/wiki/VCLExampleRestarts
  if (obj.status == 503 && req.restarts < 4) {
    set obj.http.X-Restarts = req.restarts;
    return(restart);
  }

  /* Set common headers for synthetic responses */
  set obj.http.Content-Type = "text/html; charset=utf-8";

  /* HTTP Authentification client request */
  # Empty in simple configs. See http://blog.tenya.me/blog/2011/12/14/varnish-http-authentication/

  /* We're using error 200 for monitoring puposes */
  # Consider adding some analytics stuff to trace accesses
  if (obj.status == 200) {
    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">
      <html>
        <head>
          <title>"} + obj.status + " " + obj.response + {"</title>
      </head>
      <body><h1>"} + obj.status + ": " + obj.response + {"</h1></body>
    "};
    return(deliver);
  }

  /* Error page & refresh / redirections */
  # We have plenty of choices when we have to serve an error to the client,
  # from the default error page to javascript black magic or plain redirections.
  # Adding some external statistic javascript to track failures served to clients is strongly suggested.
  # We can't use external resources on synthetic content, everything must be inlined.
  # If we need to include images we can embed them in base64 encoding.
  # Here is the default error page for Varnish 3 (not so pretty)
  set obj.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">
<html>
  <head>
    <title>"} + obj.status + " " + obj.response + {"</title>
  </head>
  <body>
    <h1>Error "} + obj.status + " " + obj.response + {"</h1>
    <p>"} + obj.response + {"</p>
    <h3>Guru Meditation:</h3>
    <p>XID: "} + req.xid + {"</p>
    <hr>
    <hr>
    <p>Varnish cache server</p>
  </body>
</html>
"};

  /* Bypass built-in logic */
  # We make sure no built-in logic is processed after ours returning at this point.
  return (deliver);
}

# vcl_init: Called when VCL is loaded, before any requests pass through it. Typically used to initialize VMODs.
# sub vcl_init {
#       return (ok);
# }

# vcl_fini: Called when VCL is discarded only after all requests have exited the VCL. Typically used to clean up VMODs.
# sub vcl_fini {
#       return (ok);
# }


restore original implementation of normalise_querystring_parameters_for_polyfill_bundle

import querystring;

sub sort_comma_separated_value {
	# This function takes a CSV and tranforms it into a url where each
	# comma-separated-value is a query-string parameter and then uses
	# Fastly's querystring.sort function to sort the values. Once sorted
	# it then turn the query-parameters back into a CSV.
	# Set the CSV on the header `Sort-Value`.
	# Returns the sorted CSV on the header `Sorted-Value`.
	declare local var.value STRING;
	set var.value = req.http.Sort-value;

	# If query value does not exist or is empty, set it to ""
	set var.value = if(var.value != "", var.value, "");

	# Replace all `&` characters with `^`, this is because `&` would break the value up into pieces.
	set var.value = regsuball(var.value, "&", "^");

	# Replace all `,` characters with `&` to break them into individual query values
	# Append `1-` infront of all the query values to make them simpler to transform later
	set var.value = "1-" regsuball(var.value, ",", "&1-");

	# Create a querystring-like string in order for querystring.sort to work.
	set var.value = querystring.sort("?" var.value);

	# Grab all the query values from the sorted url
	set var.value = regsub(var.value, "\?", "");

	# Reverse all the previous transformations to get back the single `features` query value value
	set var.value = regsuball(var.value, "1-", "");
	set var.value = regsuball(var.value, "&", ",");
	set var.value = regsuball(var.value, "\^", "&");

	set req.http.Sorted-Value = var.value;
}

sub normalise_querystring_parameters_for_polyfill_bundle {
	# Store the url without the querystring into a temporary header.
	declare local var.url STRING;
	set var.url = querystring.remove(req.url);

	declare local var.querystring STRING;
	set var.querystring = "?";

	# Remove all querystring parameters which are not part of the public API.
	# set req.url = querystring.regfilter_except(req.url, "^(features|excludes|rum|unknown|flags|version|ua|callback|compression)$");

	# (?i) makes the regex case-insensitive
	# The regex will match only if their are characters after `features=` which are not an ampersand (&).
	if (req.url.qs ~ "(?i)[^&=]*features=([^&]+)") {
		# Parameter has already been set, use the already set value.
		# re.group.1 is the first regex capture group in the regex above.
		if (std.strlen(re.group.1) < 100) {
			# We add the value of the features parameter to this header
			# This is to be able to have sort_comma_separated_value sort the value
			set req.http.Sort-Value = urldecode(re.group.1);
			call sort_comma_separated_value;
			# The header Sorted-Value now contains the sorted version of the features parameter.
			set var.querystring = querystring.set(var.querystring, "features", req.http.Sorted-Value);
		} else {
			set var.querystring = querystring.set(var.querystring, "features", urldecode(re.group.1));
		}
	} else {
		# Parameter has not been set, use the default value.
		set var.querystring = querystring.set(var.querystring, "features", "default");
	}

	# (?i) makes the regex case-insensitive
	# The regex will match only if their are characters after `excludes=` which are not an ampersand (&).
	if (req.url.qs ~ "(?i)[^&=]*excludes=([^&]+)") {
		# Parameter has already been set, use the already set value.
		# re.group.1 is the first regex capture group in the regex above.
		if (std.strlen(re.group.1) < 100) {
			# We add the value of the excludes parameter to this header
			# This is to be able to have sort_comma_separated_value sort the value
			set req.http.Sort-Value = urldecode(re.group.1);
			call sort_comma_separated_value;
			# The header Sorted-Value now contains the sorted version of the excludes parameter.
			set var.querystring = querystring.set(var.querystring, "excludes", req.http.Sorted-Value);
		} else {
			set var.querystring = querystring.set(var.querystring, "excludes", urldecode(re.group.1));
		}
	} else {
		# If excludes is not set, set to default value ""
		set var.querystring = var.querystring "&excludes=";
	}

	# If rum is not set, set to default value "0"
	if (req.url.qs !~ "(?i)[^&=]*rum=([^&]+)") {
		set var.querystring = querystring.set(var.querystring, "rum", "0");
	} else {
		set var.querystring = querystring.set(var.querystring, "rum", re.group.1);
	}

	# If unknown is not set, set to default value "polyfill"
	if (req.url.qs !~ "(?i)[^&=]*unknown=([^&]+)") {
		set var.querystring = querystring.set(var.querystring, "unknown", "polyfill");
	} else {
		set var.querystring = querystring.set(var.querystring, "unknown", re.group.1);
	}

	# If flags is not set, set to default value ""
	if (req.url.qs !~ "(?i)[^&=]*flags=([^&]+)") {
		set var.querystring = var.querystring "&flags=";
	} else {
		set var.querystring = querystring.set(var.querystring, "flags", urldecode(re.group.1));
	}

	# If version is not set, set to default value ""
	declare local var.version STRING;
	if (req.url.qs !~ "(?i)[^&=]*version=([^&]+)") {
		set var.querystring = var.querystring "&version=";
	} else {
		if (re.group.1 == "3.110.1" || re.group.1 == "3.109.0" || re.group.1 == "3.108.0" || re.group.1 == "3.104.0" || re.group.1 == "3.103.0" || re.group.1 == "3.101.0" || re.group.1 == "3.98.0" || re.group.1 == "3.96.0" || re.group.1 == "3.89.4" || re.group.1 == "3.53.1" || re.group.1 == "3.52.3" || re.group.1 == "3.52.2" || re.group.1 == "3.52.1" || re.group.1 == "3.52.0" || re.group.1 == "3.51.0" || re.group.1 == "3.50.2" || re.group.1 == "3.48.0" || re.group.1 == "3.46.0" || re.group.1 == "3.42.0" || re.group.1 == "3.41.0" || re.group.1 == "3.40.0" || re.group.1 == "3.39.0" || re.group.1 == "3.34.0" || re.group.1 == "3.27.4" || re.group.1 == "3.25.1") {
			set var.querystring = querystring.set(var.querystring, "version", re.group.1);
		} else {
			set var.querystring = var.querystring "&version=";
		}
	}

	# If ua is not set, normalise the User-Agent header based upon the version of the polyfill-library that has been requested.
	if (req.url.qs !~ "(?i)[^&=]*ua=([^&]+)") {
		if (req.url.qs ~ "(?i)[^&=]*version=3\.25\.1(&|$)") {
			call normalise_user_agent_3_25_1;
		} else {
			call normalise_user_agent_1_10_1;
		}
		set var.querystring = querystring.set(var.querystring, "ua", req.http.Normalized-User-Agent);
	} else {
		set var.querystring = querystring.set(var.querystring, "ua", urldecode(re.group.1));
	}

	# If callback is not set, set to default value ""
	if (req.url.qs !~ "(?i)[^&=]*callback=([^&]+)") {
		set var.querystring = var.querystring "&callback=";
	} else {
		set var.querystring = querystring.set(var.querystring, "callback", re.group.1);
	}

	# If compression is not set, use the best compression that the user-agent supports.
	if (req.url.qs !~ "(?i)[^&=]*compression=([^&]+)") {
		# When Fastly adds Brotli into the Accept-Encoding normalisation we can replace this with:
		# `set var.querystring = querystring.set(var.querystring, "compression", req.http.Accept-Encoding || "")`

		# Before SP2, IE/6 doesn't always read and cache gzipped content correctly.
		if (req.http.Fastly-Orig-Accept-Encoding && req.http.User-Agent !~ "MSIE 6") {
			if (req.http.Fastly-Orig-Accept-Encoding ~ "br") {
				set var.querystring = querystring.set(var.querystring, "compression", "br");
			} elsif (req.http.Fastly-Orig-Accept-Encoding ~ "gzip") {
				set var.querystring = querystring.set(var.querystring, "compression", "gzip");
			} else {
				set var.querystring = var.querystring "&compression=";
			}
		} else {
			set var.querystring = var.querystring "&compression=";
		}
	} else {
		set var.querystring = querystring.set(var.querystring, "compression", re.group.1);
	}
	set req.url = var.url var.querystring;
}

include "top_pops.vcl";
include "normalise-user-agent-3-25-1.vcl";
include "normalise-user-agent.vcl";

# The Fastly VCL boilerplate.
include "fastly-boilerplate-begin.vcl";

include "breadcrumbs.vcl";

sub vcl_recv {
	if (req.http.Fastly-Debug) {
		call breadcrumb_recv;
	}
}

sub vcl_recv {
	# js-compute-runtime currently can not handle a uri which is bigger than 1024
	# this has been fixed in js-compute-runtime but not yet released -- https://github.com/fastly/js-compute-runtime/commit/f36dee28789b1187652874fe76d1801c9a8e5974
	# when the fix is released and added into @fastly/js-compute we can remove this if statement
	if (std.strlen(req.url) < 900) {
		# We use this edge dictionary item as a flag for whether to use compute-at-edge for any requests.
		# If the item is not found, then we default to `"0"`, which we interpret to mean the flag is off.
		declare local var.compute-at-edge-active BOOL;
		if (std.atoi(table.lookup(compute_at_edge_config, "active", "0")) == 1) {
			set var.compute-at-edge-active = true;
		} else {
			set var.compute-at-edge-active = false;
		}
		if (var.compute-at-edge-active) {
			# if the querystring parameter `use-compute-at-edge-backend` is set to yes`
			# then use the c@e backend.
			# The querystring parameter is used within our tests, so that we can confirm
			# the compute-at-edge backend is working as we expect it to.
			declare local var.use-compute-at-edge-backend STRING;
			set var.use-compute-at-edge-backend = querystring.get(req.url, "use-compute-at-edge-backend");
			if (var.use-compute-at-edge-backend == "yes") {
				set req.backend = F_compute_at_edge;
				if (req.backend.healthy) {
					# if using the c@e backend then do not use the cache at all
					return(pass);
				}
			# requests can set the querystring parameter to `no` to avoid ever user compute-at-edge
			# if the querystring parameter is not set to no, then we run some logic to decide whether
			# to use compute-at-edge for this request or not.
			} else if (var.use-compute-at-edge-backend != "no") {
				declare local var.selected BOOL;
				set var.selected = randombool(1, std.atoi(table.lookup(compute_at_edge_config, "sample", "1000")));
				# this logic is saying, only use compute-at-edge for a request which has come from outside this Fastly
				# service. I.E. The request has directly from a client.
				# We also have logic which decides how many of those direct client requests use compute-at-edge.
				# We default to 1 in 1000 requests using compute-at-edge, this can be configured by the edge dictionary
				# named `compute_at_edge_config` and the item in the dictionary named `sample`
				if (var.selected && fastly_info.edge.is_tls && !req.is_background_fetch && !req.is_purge && req.restarts == 0 && fastly.ff.visits_this_service == 0) {
					set req.backend = F_compute_at_edge;
					if (req.backend.healthy) {
						# if using the c@e backend then do not use the cache at all
						return(pass);
					}
				}
			}
		}
	}
}

include "redirects.vcl";
include "synthetic-responses.vcl";
include "polyfill-service.vcl";

# Finally include the last bit of VCL, this _must_ be last!
include "fastly-boilerplate-end.vcl";

import querystring;

sub sort_comma_separated_value {
	# This function takes a CSV and tranforms it into a url where each
	# comma-separated-value is a query-string parameter and then uses
	# Fastly's querystring.sort function to sort the values. Once sorted
	# it then turn the query-parameters back into a CSV.
	# Set the CSV on the header `Sort-Value`.
	# Returns the sorted CSV on the header `Sorted-Value`.
	declare local var.value STRING;
	set var.value = req.http.Sort-value;

	# If query value does not exist or is empty, set it to ""
	set var.value = if(var.value != "", var.value, "");

	# Replace all `&` characters with `^`, this is because `&` would break the value up into pieces.
	set var.value = regsuball(var.value, "&", "^");

	# Replace all `,` characters with `&` to break them into individual query values
	# Append `1-` infront of all the query values to make them simpler to transform later
	set var.value = "1-" regsuball(var.value, ",", "&1-");

	# Create a querystring-like string in order for querystring.sort to work.
	set var.value = querystring.sort("?" var.value);

	# Grab all the query values from the sorted url
	set var.value = regsub(var.value, "\?", "");

	# Reverse all the previous transformations to get back the single `features` query value value
	set var.value = regsuball(var.value, "1-", "");
	set var.value = regsuball(var.value, "&", ",");
	set var.value = regsuball(var.value, "\^", "&");

	set req.http.Sorted-Value = var.value;
}

sub normalise_querystring_parameters_for_polyfill_bundle {
	# Store the url without the querystring into a temporary header.
	declare local var.url STRING;
	set var.url = querystring.remove(req.url);

	declare local var.querystring STRING;
	set var.querystring = "?";

	# Remove all querystring parameters which are not part of the public API.
	# set req.url = querystring.regfilter_except(req.url, "^(features|excludes|rum|unknown|flags|version|ua|callback|compression)$");

	# (?i) makes the regex case-insensitive
	# The regex will match only if their are characters after `features=` which are not an ampersand (&).
	if (req.url.qs ~ "(?i)[^&=]*features=([^&]+)") {
		# Parameter has already been set, use the already set value.
		# re.group.1 is the first regex capture group in the regex above.
		if (std.strlen(re.group.1) < 100) {
			# We add the value of the features parameter to this header
			# This is to be able to have sort_comma_separated_value sort the value
			set req.http.Sort-Value = urldecode(re.group.1);
			call sort_comma_separated_value;
			# The header Sorted-Value now contains the sorted version of the features parameter.
			set var.querystring = var.querystring "features=" req.http.Sorted-Value;
		} else {
			set var.querystring = var.querystring "features=" urldecode(re.group.1);
		}
	} else {
		# Parameter has not been set, use the default value.
		set var.querystring = var.querystring "features=" "default";
	}

	# (?i) makes the regex case-insensitive
	# The regex will match only if their are characters after `excludes=` which are not an ampersand (&).
	if (req.url.qs ~ "(?i)[^&=]*excludes=([^&]+)") {
		# Parameter has already been set, use the already set value.
		# re.group.1 is the first regex capture group in the regex above.
		if (std.strlen(re.group.1) < 100) {
			# We add the value of the excludes parameter to this header
			# This is to be able to have sort_comma_separated_value sort the value
			set req.http.Sort-Value = urldecode(re.group.1);
			call sort_comma_separated_value;
			# The header Sorted-Value now contains the sorted version of the excludes parameter.
			set var.querystring = var.querystring "&excludes=" req.http.Sorted-Value;
		} else {
			set var.querystring = var.querystring "&excludes=" urldecode(re.group.1);
		}
	} else {
		# If excludes is not set, set to default value ""
		set var.querystring = var.querystring "&excludes=";
	}

	# If rum is not set, set to default value "0"
	if (req.url.qs !~ "(?i)[^&=]*rum=([^&]+)") {
		set var.querystring = var.querystring "&rum=" "0";
	} else {
		set var.querystring = var.querystring "&rum=" re.group.1;
	}

	# If unknown is not set, set to default value "polyfill"
	if (req.url.qs !~ "(?i)[^&=]*unknown=([^&]+)") {
		set var.querystring = var.querystring "&unknown=" "polyfill";
	} else {
		set var.querystring = var.querystring "&unknown=" re.group.1;
	}

	# If flags is not set, set to default value ""
	if (req.url.qs !~ "(?i)[^&=]*flags=([^&]+)") {
		set var.querystring = var.querystring "&flags=";
	} else {
		set var.querystring = var.querystring "&flags=" urldecode(re.group.1);
	}

	# If version is not set, set to default value ""
	declare local var.version STRING;
	if (req.url.qs !~ "(?i)[^&=]*version=([^&]+)") {
		set var.querystring = var.querystring "&version=";
	} else {
		if (re.group.1 == "3.110.1" || re.group.1 == "3.109.0" || re.group.1 == "3.108.0" || re.group.1 == "3.104.0" || re.group.1 == "3.103.0" || re.group.1 == "3.101.0" || re.group.1 == "3.98.0" || re.group.1 == "3.96.0" || re.group.1 == "3.89.4" || re.group.1 == "3.53.1" || re.group.1 == "3.52.3" || re.group.1 == "3.52.2" || re.group.1 == "3.52.1" || re.group.1 == "3.52.0" || re.group.1 == "3.51.0" || re.group.1 == "3.50.2" || re.group.1 == "3.48.0" || re.group.1 == "3.46.0" || re.group.1 == "3.42.0" || re.group.1 == "3.41.0" || re.group.1 == "3.40.0" || re.group.1 == "3.39.0" || re.group.1 == "3.34.0" || re.group.1 == "3.27.4" || re.group.1 == "3.25.1") {
			set var.querystring = var.querystring "&version=" re.group.1;
		} else {
			set var.querystring = var.querystring "&version=";
		}
	}

	# If ua is not set, normalise the User-Agent header based upon the version of the polyfill-library that has been requested.
	if (req.url.qs !~ "(?i)[^&=]*ua=([^&]+)") {
		if (req.url.qs ~ "(?i)[^&=]*version=3\.25\.1(&|$)") {
			call normalise_user_agent_3_25_1;
		} else {
			call normalise_user_agent_1_10_1;
		}
		set var.querystring = var.querystring "&ua=" req.http.Normalized-User-Agent;
	} else {
		set var.querystring = var.querystring "&ua=" urldecode(re.group.1);
	}

	# If callback is not set, set to default value ""
	if (req.url.qs !~ "(?i)[^&=]*callback=([^&]+)") {
		set var.querystring = var.querystring "&callback=";
	} else {
		set var.querystring = var.querystring "&callback=" re.group.1;
	}

	# If compression is not set, use the best compression that the user-agent supports.
	if (req.url.qs !~ "(?i)[^&=]*compression=([^&]+)") {
		# When Fastly adds Brotli into the Accept-Encoding normalisation we can replace this with:
		# `set var.querystring = var.querystring "&compression=" req.http.Accept-Encoding || ""`

		# Before SP2, IE/6 doesn't always read and cache gzipped content correctly.
		if (req.http.Fastly-Orig-Accept-Encoding && req.http.User-Agent !~ "MSIE 6") {
			if (req.http.Fastly-Orig-Accept-Encoding ~ "br") {
				set var.querystring = var.querystring "&compression=" "br";
			} elsif (req.http.Fastly-Orig-Accept-Encoding ~ "gzip") {
				set var.querystring = var.querystring "&compression=" "gzip";
			} else {
				set var.querystring = var.querystring "&compression=";
			}
		} else {
			set var.querystring = var.querystring "&compression=";
		}
	} else {
		set var.querystring = var.querystring "&compression=" re.group.1;
	}
	set req.url = var.url var.querystring;
}

include "top_pops.vcl";
include "normalise-user-agent-3-25-1.vcl";
include "normalise-user-agent.vcl";

# The Fastly VCL boilerplate.
include "fastly-boilerplate-begin.vcl";

include "breadcrumbs.vcl";

sub vcl_recv {
	if (req.http.Fastly-Debug) {
		call breadcrumb_recv;
	}
}

sub vcl_recv {
	# js-compute-runtime currently can not handle a uri which is bigger than 1024
	# this has been fixed in js-compute-runtime but not yet released -- https://github.com/fastly/js-compute-runtime/commit/f36dee28789b1187652874fe76d1801c9a8e5974
	# when the fix is released and added into @fastly/js-compute we can remove this if statement
	if (std.strlen(req.url) < 900) {
		# We use this edge dictionary item as a flag for whether to use compute-at-edge for any requests.
		# If the item is not found, then we default to `"0"`, which we interpret to mean the flag is off.
		declare local var.compute-at-edge-active BOOL;
		if (std.atoi(table.lookup(compute_at_edge_config, "active", "0")) == 1) {
			set var.compute-at-edge-active = true;
		} else {
			set var.compute-at-edge-active = false;
		}
		if (var.compute-at-edge-active) {
			# if the querystring parameter `use-compute-at-edge-backend` is set to yes`
			# then use the c@e backend.
			# The querystring parameter is used within our tests, so that we can confirm
			# the compute-at-edge backend is working as we expect it to.
			declare local var.use-compute-at-edge-backend STRING;
			set var.use-compute-at-edge-backend = querystring.get(req.url, "use-compute-at-edge-backend");
			if (var.use-compute-at-edge-backend == "yes") {
				set req.backend = F_compute_at_edge;
				if (req.backend.healthy) {
					# if using the c@e backend then do not use the cache at all
					return(pass);
				}
			# requests can set the querystring parameter to `no` to avoid ever user compute-at-edge
			# if the querystring parameter is not set to no, then we run some logic to decide whether
			# to use compute-at-edge for this request or not.
			} else if (var.use-compute-at-edge-backend != "no") {
				declare local var.selected BOOL;
				set var.selected = randombool(1, std.atoi(table.lookup(compute_at_edge_config, "sample", "1000")));
				# this logic is saying, only use compute-at-edge for a request which has come from outside this Fastly
				# service. I.E. The request has directly from a client.
				# We also have logic which decides how many of those direct client requests use compute-at-edge.
				# We default to 1 in 1000 requests using compute-at-edge, this can be configured by the edge dictionary
				# named `compute_at_edge_config` and the item in the dictionary named `sample`
				if (var.selected && fastly_info.edge.is_tls && !req.is_background_fetch && !req.is_purge && req.restarts == 0 && fastly.ff.visits_this_service == 0) {
					set req.backend = F_compute_at_edge;
					if (req.backend.healthy) {
						# if using the c@e backend then do not use the cache at all
						return(pass);
					}
				}
			}
		}
	}
}

include "redirects.vcl";
include "synthetic-responses.vcl";
include "polyfill-service.vcl";

# Finally include the last bit of VCL, this _must_ be last!
include "fastly-boilerplate-end.vcl";


Update sample varnish config

Update sample varnish config

vcl 4.0;

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

sub vcl_recv {
    unset req.http.Cookie;
}

sub vcl_backend_response {
    # Add asset related cache control headers to fingerprinted assets
    if (bereq.url ~ "^/assets/.*-[a-f0-9]{32,64}\.[^\.]+$") {
        # Cache publicly for 1 year (HTTP spec suggests not specifying more then a year)
        set beresp.ttl = 31557600s;
        set beresp.http.Cache-Control = "max-age=31557600, public";

        # Some browsers still send conditional-GET requests if there's a
        # Last-Modified header or an ETag header even if they haven't
        # reached the expiry date sent in the Expires header.
        unset beresp.http.Last-Modified;
        unset beresp.http.ETag;

        set beresp.http.Access-Control-Allow-Origin = "https://linkedlist.org";
    }

    # Strip attempts to set cookies if ttl indicates the response will be cached
    if (beresp.ttl > 0s) {
        unset beresp.http.Set-Cookie;

        if (beresp.http.Content-Type ~ "html") {
            // Restore default header
            set beresp.http.Cache-Control = "max-age=0, private, must-revalidate";
            return (deliver);
        }
    }
}

# This is the VCL configuration Varnish will automatically append to your VCL
# file during compilation/loading. See the vcl(7) man page for details on syntax
# and semantics.
# New users is recommended to use the example.vcl file as a starting point.
#
# sub vcl_recv {
#     if (req.method == "PRI") {
# 	/* We do not support SPDY or HTTP/2.0 */
# 	return (synth(405));
#     }
#     if (req.method != "GET" &&
#       req.method != "HEAD" &&
#       req.method != "PUT" &&
#       req.method != "POST" &&
#       req.method != "TRACE" &&
#       req.method != "OPTIONS" &&
#       req.method != "DELETE") {
#         /* Non-RFC2616 or CONNECT which is weird. */
#         return (pipe);
#     }
#
#     if (req.method != "GET" && req.method != "HEAD") {
#         /* We only deal with GET and HEAD by default */
#         return (pass);
#     }
#     if (req.http.Authorization || req.http.Cookie) {
#         /* Not cacheable by default */
#         return (pass);
#     }
#     return (hash);
# }
#
# sub vcl_pipe {
#     # By default Connection: close is set on all piped requests, to stop
#     # connection reuse from sending future requests directly to the
#     # (potentially) wrong backend. If you do want this to happen, you can undo
#     # it here.
#     # unset bereq.http.connection;
#     return (pipe);
# }
#
# sub vcl_pass {
#     return (fetch);
# }
#
# sub vcl_hash {
#     hash_data(req.url);
#     if (req.http.host) {
#         hash_data(req.http.host);
#     } else {
#         hash_data(server.ip);
#     }
#     return (lookup);
# }
#
# sub vcl_purge {
#     return (synth(200, "Purged"));
# }
#
# sub vcl_hit {
#     if (obj.ttl >= 0s) {
#         // A pure unadultered hit, deliver it
#         return (deliver);
#     }
#     if (obj.ttl + obj.grace > 0s) {
#         // Object is in grace, deliver it
#         // Automatically triggers a background fetch
#         return (deliver);
#     }
#     // fetch & deliver once we get the result
#     return (fetch);
# }
#
# sub vcl_miss {
#     return (fetch);
# }
#
# sub vcl_deliver {
#     return (deliver);
# }
#
# /*
#  * We can come here "invisibly" with the following errors: 413, 417 & 503
#  */
# sub vcl_synth {
#     set resp.http.Content-Type = "text/html; charset=utf-8";
#     set resp.http.Retry-After = "5";
#     synthetic( {"<!DOCTYPE html>
#
#
#     <title>"} + resp.status + " " + resp.reason + {"</title>
#
#
#
Error "} + resp.status + " " + resp.reason + {"

#
"} + resp.reason + {"

#
Guru Meditation:

#
XID: "} + req.xid + {"

#

#
Varnish cache server

#
#
# "} );
#     return (deliver);
# }
#
# #######################################################################
# # Backend Fetch
#
# sub vcl_backend_fetch {
#     return (fetch);
# }
#
# sub vcl_backend_response {
#     if (beresp.ttl <= 0s ||
#       beresp.http.Set-Cookie ||
#       beresp.http.Surrogate-control ~ "no-store" ||
#       (!beresp.http.Surrogate-Control &&
#         beresp.http.Cache-Control ~ "no-cache|no-store|private") ||
#       beresp.http.Vary == "*") {
#         /*
#         * Mark as "Hit-For-Pass" for the next 2 minutes
#         */
#         set beresp.ttl = 120s;
#         set beresp.uncacheable = true;
#     }
#     return (deliver);
# }
#
# sub vcl_backend_error {
#     set beresp.http.Content-Type = "text/html; charset=utf-8";
#     set beresp.http.Retry-After = "5";
#     synthetic( {"<!DOCTYPE html>
#
#
#     <title>"} + beresp.status + " " + beresp.reason + {"</title>
#
#
#
Error "} + beresp.status + " " + beresp.reason + {"

#
"} + beresp.reason + {"

#
Guru Meditation:

#
XID: "} + bereq.xid + {"

#

#
Varnish cache server

#
#
# "} );
#     return (deliver);
# }
#
# #######################################################################
# # Housekeeping
#
# sub vcl_init {
#     return (ok);
# }
#
# sub vcl_fini {
#     return (ok);
# }

vcl 4.0;

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

sub vcl_recv {
  unset req.http.Cookie;
}

sub vcl_backend_response {
  if (beresp.ttl > 0s) {
    unset beresp.http.Set-Cookie;

    if (beresp.http.Content-Type ~ "html") {
      // Restore default header
      set beresp.http.Cache-Control = "max-age=0, private, must-revalidate";
      return (deliver);
    }
  }
}

# This is the VCL configuration Varnish will automatically append to your VCL
# file during compilation/loading. See the vcl(7) man page for details on syntax
# and semantics.
# New users is recommended to use the example.vcl file as a starting point.
#
# sub vcl_recv {
#     if (req.method == "PRI") {
# 	/* We do not support SPDY or HTTP/2.0 */
# 	return (synth(405));
#     }
#     if (req.method != "GET" &&
#       req.method != "HEAD" &&
#       req.method != "PUT" &&
#       req.method != "POST" &&
#       req.method != "TRACE" &&
#       req.method != "OPTIONS" &&
#       req.method != "DELETE") {
#         /* Non-RFC2616 or CONNECT which is weird. */
#         return (pipe);
#     }
#
#     if (req.method != "GET" && req.method != "HEAD") {
#         /* We only deal with GET and HEAD by default */
#         return (pass);
#     }
#     if (req.http.Authorization || req.http.Cookie) {
#         /* Not cacheable by default */
#         return (pass);
#     }
#     return (hash);
# }
#
# sub vcl_pipe {
#     # By default Connection: close is set on all piped requests, to stop
#     # connection reuse from sending future requests directly to the
#     # (potentially) wrong backend. If you do want this to happen, you can undo
#     # it here.
#     # unset bereq.http.connection;
#     return (pipe);
# }
#
# sub vcl_pass {
#     return (fetch);
# }
#
# sub vcl_hash {
#     hash_data(req.url);
#     if (req.http.host) {
#         hash_data(req.http.host);
#     } else {
#         hash_data(server.ip);
#     }
#     return (lookup);
# }
#
# sub vcl_purge {
#     return (synth(200, "Purged"));
# }
#
# sub vcl_hit {
#     if (obj.ttl >= 0s) {
#         // A pure unadultered hit, deliver it
#         return (deliver);
#     }
#     if (obj.ttl + obj.grace > 0s) {
#         // Object is in grace, deliver it
#         // Automatically triggers a background fetch
#         return (deliver);
#     }
#     // fetch & deliver once we get the result
#     return (fetch);
# }
#
# sub vcl_miss {
#     return (fetch);
# }
#
# sub vcl_deliver {
#     return (deliver);
# }
#
# /*
#  * We can come here "invisibly" with the following errors: 413, 417 & 503
#  */
# sub vcl_synth {
#     set resp.http.Content-Type = "text/html; charset=utf-8";
#     set resp.http.Retry-After = "5";
#     synthetic( {"<!DOCTYPE html>
#
#
#     <title>"} + resp.status + " " + resp.reason + {"</title>
#
#
#
Error "} + resp.status + " " + resp.reason + {"

#
"} + resp.reason + {"

#
Guru Meditation:

#
XID: "} + req.xid + {"

#

#
Varnish cache server

#
#
# "} );
#     return (deliver);
# }
#
# #######################################################################
# # Backend Fetch
#
# sub vcl_backend_fetch {
#     return (fetch);
# }
#
# sub vcl_backend_response {
#     if (beresp.ttl <= 0s ||
#       beresp.http.Set-Cookie ||
#       beresp.http.Surrogate-control ~ "no-store" ||
#       (!beresp.http.Surrogate-Control &&
#         beresp.http.Cache-Control ~ "no-cache|no-store|private") ||
#       beresp.http.Vary == "*") {
#         /*
#         * Mark as "Hit-For-Pass" for the next 2 minutes
#         */
#         set beresp.ttl = 120s;
#         set beresp.uncacheable = true;
#     }
#     return (deliver);
# }
#
# sub vcl_backend_error {
#     set beresp.http.Content-Type = "text/html; charset=utf-8";
#     set beresp.http.Retry-After = "5";
#     synthetic( {"<!DOCTYPE html>
#
#
#     <title>"} + beresp.status + " " + beresp.reason + {"</title>
#
#
#
Error "} + beresp.status + " " + beresp.reason + {"

#
"} + beresp.reason + {"

#
Guru Meditation:

#
XID: "} + bereq.xid + {"

#

#
Varnish cache server

#
#
# "} );
#     return (deliver);
# }
#
# #######################################################################
# # Housekeeping
#
# sub vcl_init {
#     return (ok);
# }
#
# sub vcl_fini {
#     return (ok);
# }


buff adjust

buff adjust

C{
	#include <stdio.h>
	#include <stdlib.h>
	#include <fcntl.h>
	#include <unistd.h>
	#include <string.h>
	#include <strings.h>
	#include <sys/mman.h>
	#include <sys/types.h>
	#include <bin/varnishd/cache.h>
}C

sub vcl_recv {
	if (req.request == "POST") {
		call vfw_load_post;
	}
}

sub vcl_deliver {
	if (req.request == "POST") {
		call vfw_cleanup_post;
	}
}

sub vfw_load_post {
	C{
		/*
		** Please read:
                ** -- bin/varnishd/cache_fetch.c: FetchReqBody()
                ** -- bin/varnishd/cache_httpconn.c: HTC_Read()
		**
		** -- http://sourceforge.net/projects/libdynamic/ (Cal Heldenbrand)
		*/

		char buf[4096], body[8192];
		unsigned long content_length;
		char *h_clen_ptr, *h_ctype_ptr, shmem_fname[64];
		int buf_size, rsize, wsize, shmem_htc_fd;

		body[0] = 0;

		syslog(LOG_INFO, "vfw_load_post - initiating...");

		h_ctype_ptr = VRT_GetHdr(sp, HDR_REQ, "\015Content-Type:");

		if (strcmp(h_ctype_ptr, "application/x-www-form-urlencoded")) {
			syslog(LOG_INFO, "vfw_load_post - Not Supported Content-Type: %s", h_ctype_ptr);
			return(0);
		}

		h_clen_ptr = VRT_GetHdr(sp, HDR_REQ, "\017Content-Length:");

		if (!h_clen_ptr) {
			syslog(LOG_INFO, "vfw_load_post - Empty Content-Length Header");
			return(0);
		}

		content_length = strtoul(h_clen_ptr, NULL, 10);

		if (content_length <= 0) {
			syslog(LOG_INFO, "vfw_load_post - Empty Content-Length Header");
			return(0);
		}

		// Open/Create shared memory file
		snprintf(shmem_fname, sizeof(shmem_fname), "varnish.httpbody.%d.%d", sp->id, sp->xid);
		shmem_htc_fd = shm_open(shmem_fname, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
		syslog(LOG_INFO, "vfw_load_post - shm_open()/%s: %s", shmem_fname, strerror(errno));

		if (shmem_htc_fd == -1) {
			return(0);
		}

		while (content_length) {
			if (content_length > sizeof(buf)) {
				buf_size = sizeof(buf) - 1;
			}
			else {
				buf_size = content_length;
			}

			// read body data into 'buf'
			rsize = HTC_Read(sp->htc, buf, buf_size);

			if (rsize <= 0) {
				syslog(LOG_INFO, "vfw_load_post - HTC_Read(): %d - %s", rsize, strerror(errno));
				return(0);
			}

			content_length -= rsize;

			// write body data to the shared memory file
			wsize = write(shmem_htc_fd, buf, buf_size);

			if (wsize <= 0) {
				syslog(LOG_INFO, "vfw_load_post - write(): %d - %s", wsize, strerror(errno));
				return(0);
			}

			// copy body data to header buffer
			strncat(body, buf, buf_size);
		}

		lseek(shmem_htc_fd, 0, SEEK_SET);
		syslog(LOG_INFO, "vfw_load_post - lseek(): %s", strerror(errno));
		sp->htc->fd = shmem_htc_fd;

		syslog(LOG_INFO, "vfw_load_post - DATA: Content-Length: %s, Content: %s", h_clen_ptr, body);

		VRT_SetHdr(sp, HDR_REQ, "\013X-VFW-Body:", body, vrt_magic_string_end);
	}C
}

sub vfw_cleanup_post {
	C{
		if (VRT_GetHdr(sp, HDR_REQ, "\013X-VFW-Body:")) {
			syslog(LOG_INFO, "vfw_load_post - cleaning up...");
			close(sp->htc->fd);
			syslog(LOG_INFO, "vfw_cleanup_post - close(): %s", strerror(errno));
		}
	}C
}

C{
	#include <stdio.h>
	#include <stdlib.h>
	#include <fcntl.h>
	#include <unistd.h>
	#include <string.h>
	#include <strings.h>
	#include <sys/mman.h>
	#include <sys/types.h>
	#include <bin/varnishd/cache.h>
}C

sub vcl_recv {
	if (req.request == "POST") {
		call vfw_load_post;
	}
}

sub vcl_deliver {
	if (req.request == "POST") {
		call vfw_cleanup_post;
	}
}

sub vfw_load_post {
	C{
		/*
		** Please read:
                ** -- bin/varnishd/cache_fetch.c: FetchReqBody()
                ** -- bin/varnishd/cache_httpconn.c: HTC_Read()
		**
		** -- http://sourceforge.net/projects/libdynamic/ (Cal Heldenbrand)
		*/

		char buf[3], body[8192];
		unsigned long content_length;
		char *h_clen_ptr, *h_ctype_ptr, shmem_fname[64];
		int buf_size, rsize, wsize, shmem_htc_fd;

		body[0] = 0;

		syslog(LOG_INFO, "vfw_load_post - initiating...");

		h_ctype_ptr = VRT_GetHdr(sp, HDR_REQ, "\015Content-Type:");

		if (strcmp(h_ctype_ptr, "application/x-www-form-urlencoded")) {
			syslog(LOG_INFO, "vfw_load_post - Not Supported Content-Type: %s", h_ctype_ptr);
			return(0);
		}

		h_clen_ptr = VRT_GetHdr(sp, HDR_REQ, "\017Content-Length:");

		if (!h_clen_ptr) {
			syslog(LOG_INFO, "vfw_load_post - Empty Content-Length Header");
			return(0);
		}

		content_length = strtoul(h_clen_ptr, NULL, 10);

		if (content_length <= 0) {
			syslog(LOG_INFO, "vfw_load_post - Empty Content-Length Header");
			return(0);
		}

		// Open/Create shared memory file
		snprintf(shmem_fname, sizeof(shmem_fname), "varnish.httpbody.%d.%d", sp->id, sp->xid);
		shmem_htc_fd = shm_open(shmem_fname, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
		syslog(LOG_INFO, "vfw_load_post - shm_open()/%s: %s", shmem_fname, strerror(errno));

		if (shmem_htc_fd == -1) {
			return(0);
		}

		while (content_length) {
			if (content_length > sizeof(buf)) {
				buf_size = sizeof(buf) - 1;
			}
			else {
				buf_size = content_length;
			}

			// read body data into 'buf'
			rsize = HTC_Read(sp->htc, buf, buf_size);

			if (rsize <= 0) {
				syslog(LOG_INFO, "vfw_load_post - HTC_Read(): %d - %s", rsize, strerror(errno));
				return(0);
			}

			content_length -= rsize;

			// write body data to the shared memory file
			wsize = write(shmem_htc_fd, buf, buf_size);

			if (wsize <= 0) {
				syslog(LOG_INFO, "vfw_load_post - write(): %d - %s", wsize, strerror(errno));
				return(0);
			}

			// copy body data to header buffer
			strncat(body, buf, buf_size);
		}

		lseek(shmem_htc_fd, 0, SEEK_SET);
		syslog(LOG_INFO, "vfw_load_post - lseek(): %s", strerror(errno));
		sp->htc->fd = shmem_htc_fd;

		syslog(LOG_INFO, "vfw_load_post - DATA: Content-Length: %s, Content: %s", h_clen_ptr, body);

		VRT_SetHdr(sp, HDR_REQ, "\013X-VFW-Body:", body, vrt_magic_string_end);
	}C
}

sub vfw_cleanup_post {
	C{
		if (VRT_GetHdr(sp, HDR_REQ, "\013X-VFW-Body:")) {
			syslog(LOG_INFO, "vfw_load_post - cleaning up...");
			close(sp->htc->fd);
			syslog(LOG_INFO, "vfw_cleanup_post - close(): %s", strerror(errno));
		}
	}C
}