Tweaked regexp for BrowserProfile.

Tweaked regexp for BrowserProfile.

vcl 4.0;

import directors;
import std;

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

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

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

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

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

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

  ###
  # Avoid processing uncacheable requests
  ###

  if (req.http.Authorization) {
    return (pass);
  }

  ###
  # Inject X-Forwarded-For headers, but only once.
  ###

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

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

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

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

{% if varnish_cookie_sanitization_enabled %}
  ###
  # Cookie Sanitization
  ###

  ### Google Analytics
  set req.http.Cookie = regsuball(req.http.Cookie, "***REMOVED***=[^;]+(; )?", "");
  set req.http.Cookie = regsuball(req.http.Cookie, "***REMOVED***=[^;]+(; )?", "");
  set req.http.Cookie = regsuball(req.http.Cookie, "***REMOVED***=[^;]+(; )?", "");
  set req.http.Cookie = regsuball(req.http.Cookie, "***REMOVED***=[^;]+(; )?", "");
  set req.http.Cookie = regsuball(req.http.Cookie, "***REMOVED***=[^;]+(; )?", "");

  ### Quantcast
  set req.http.Cookie = regsuball(req.http.Cookie, "***REMOVED***=[^;]+(; )?", "");

  ### AddThis
  set req.http.Cookie = regsuball(req.http.Cookie, "***REMOVED***=[^;]+(; )?", "");

  ### General Cleanup
  set req.http.Cookie = regsuball(req.http.Cookie, "^;\s*", "");
  if (req.http.cookie ~ "^\s*$") {
      unset req.http.cookie;
  }
{% endif %}

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

  if (req.url ~ "(\?|&)(utm_source|utm_medium|utm_campaign|gclid|cx|ie|cof|siteurl)=") {
    set req.url = regsuball(req.url, "&(utm_source|utm_medium|utm_campaign|gclid|cx|ie|cof|siteurl)=([A-z0-9_\-\.%25]+)", "");
    set req.url = regsuball(req.url, "\?(utm_source|utm_medium|utm_campaign|gclid|cx|ie|cof|siteurl)=([A-z0-9_\-\.%25]+)", "?");
    set req.url = regsub(req.url, "\?&", "?");
    set req.url = regsub(req.url, "\?$", "");
  }
  if (req.url ~ "\#") {
    set req.url = regsub(req.url, "\#.*$", "");
  }

{% endif %}

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

  if (req.http.Cookie ~ "BrowserProfile") {
    set req.http.X-Language = regsuball(req.http.Cookie, "(language..:\\.)(.*)\\.,", "\2");
    set req.http.X-Province = regsuball(req.http.Cookie, "(region..:\\.)(.*)\\.,", "\2");
  } else {
    if (req.http.Cookie ~ "lang=") {
      set req.http.X-Language = regsuball(req.http.Cookie, "lang=(.*);", "\1");
    } else {
      set req.http.X-Language = "en";
    }

    if (req.http.Cookie ~ "prov=") {
      set req.http.X-Province = regsuball(req.http.Cookie, "prov=(.*);", "\1");
    } else {
      set req.http.X-Province = "BC";
    }
  }

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

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

{% if varnish_blacklist_enabled %}
  if ((req.url ~ "{{ varnish_blacklist_regexp }}")) {
    set req.http.X-Passthrough-Reason = "Path in Blacklist";
    return(pass);
  } else {
{% if varnish_discards_client_cookies %}
    unset req.http.Cookie;
{% endif %}
  }
{% endif %}

}

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

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

  return (fetch);
{% endif %}
}

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

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

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

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

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

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

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

vcl 4.0;

import directors;
import std;

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

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

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

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

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

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

  ###
  # Avoid processing uncacheable requests
  ###

  if (req.http.Authorization) {
    return (pass);
  }

  ###
  # Inject X-Forwarded-For headers, but only once.
  ###

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

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

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

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

{% if varnish_cookie_sanitization_enabled %}
  ###
  # Cookie Sanitization
  ###

  ### Google Analytics
  set req.http.Cookie = regsuball(req.http.Cookie, "***REMOVED***=[^;]+(; )?", "");
  set req.http.Cookie = regsuball(req.http.Cookie, "***REMOVED***=[^;]+(; )?", "");
  set req.http.Cookie = regsuball(req.http.Cookie, "***REMOVED***=[^;]+(; )?", "");
  set req.http.Cookie = regsuball(req.http.Cookie, "***REMOVED***=[^;]+(; )?", "");
  set req.http.Cookie = regsuball(req.http.Cookie, "***REMOVED***=[^;]+(; )?", "");

  ### Quantcast
  set req.http.Cookie = regsuball(req.http.Cookie, "***REMOVED***=[^;]+(; )?", "");

  ### AddThis
  set req.http.Cookie = regsuball(req.http.Cookie, "***REMOVED***=[^;]+(; )?", "");

  ### General Cleanup
  set req.http.Cookie = regsuball(req.http.Cookie, "^;\s*", "");
  if (req.http.cookie ~ "^\s*$") {
      unset req.http.cookie;
  }
{% endif %}

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

  if (req.url ~ "(\?|&)(utm_source|utm_medium|utm_campaign|gclid|cx|ie|cof|siteurl)=") {
    set req.url = regsuball(req.url, "&(utm_source|utm_medium|utm_campaign|gclid|cx|ie|cof|siteurl)=([A-z0-9_\-\.%25]+)", "");
    set req.url = regsuball(req.url, "\?(utm_source|utm_medium|utm_campaign|gclid|cx|ie|cof|siteurl)=([A-z0-9_\-\.%25]+)", "?");
    set req.url = regsub(req.url, "\?&", "?");
    set req.url = regsub(req.url, "\?$", "");
  }
  if (req.url ~ "\#") {
    set req.url = regsub(req.url, "\#.*$", "");
  }

{% endif %}

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

  if (req.http.Cookie ~ "BrowserProfile") {
    set req.http.X-Language = regsuball(req.http.Cookie, "language..:\\.(.*)\\.,", "\1");
    set req.http.X-Province = regsuball(req.http.Cookie, "region..:\\.(.*)\\.,", "\1");
  } else {
    if (req.http.Cookie ~ "lang=") {
      set req.http.X-Language = regsuball(req.http.Cookie, "lang=(.*);", "\1");
    } else {
      set req.http.X-Language = "en";
    }

    if (req.http.Cookie ~ "prov=") {
      set req.http.X-Province = regsuball(req.http.Cookie, "prov=(.*);", "\1");
    } else {
      set req.http.X-Province = "BC";
    }
  }

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

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

{% if varnish_blacklist_enabled %}
  if ((req.url ~ "{{ varnish_blacklist_regexp }}")) {
    set req.http.X-Passthrough-Reason = "Path in Blacklist";
    return(pass);
  } else {
{% if varnish_discards_client_cookies %}
    unset req.http.Cookie;
{% endif %}
  }
{% endif %}

}

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

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

  return (fetch);
{% endif %}
}

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

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

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

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

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

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

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


Enabling processing ESIs inside CDATA

Enabling processing ESIs inside CDATA

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

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

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

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

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

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

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

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

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

    # formkey lookup
    if (req.url.path ~ "/fastlycdn/getformkey/") {
        # check if we have a formkey cookie and that it's validly formed
        if (req.http.Cookie:FASTLY_CDN_FORMKEY &&
std.strlen(req.http.Cookie:FASTLY_CDN_FORMKEY) < 40 && req.http.Cookie:FASTLY_CDN_FORMKEY ~ "^[a-zA-Z0-9-_]*$" )  {
            set req.http.Formkey = req.http.Cookie:FASTLY_CDN_FORMKEY;
        } else {
            # create formkey
            set req.http.seed = req.http.Cookie client.ip remote.port client.geo.longitude client.geo.latitude client.geo.postal_code;
            set req.http.Formkey = regsub(digest.hash_md5(req.http.seed), "^0x", "");
        }
        error 760 req.http.Formkey;
    }

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

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

    # check for ESI calls
    if (req.url.qs ~ "esi_data=") {
        # check for valid cookie data
        declare local var.esi_data_field STRING;
        declare local var.cookie_data STRING;
        # Based on esi_data value requested we will need to search for cookie FASTLY_CDN-<type> e.g. FASTLY_CDN-customer_quote
        set var.esi_data_field = "FASTLY_CDN-" subfield(req.url.qs, "esi_data", "&");
        # We can't use variables in either subfield or regex so we need to use this workaround
        # to extract value of cookie that we compiled in esi_data_field
        set var.cookie_data = std.strstr(req.http.Cookie,var.esi_data_field);
        set var.cookie_data = regsub(var.cookie_data,"^[^=]*=([^;]*).*","\1");
        # If found a value we replace the query string with the contents of that cookie
        if ( var.cookie_data != "" ) {
          set req.url = querystring.set(req.url, "esi_data", var.cookie_data);
        }
    }

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

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

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

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

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

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

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

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

    # formkey lookup
    if (req.url.path ~ "/fastlycdn/getformkey/") {
        # check if we have a formkey cookie and that it's validly formed
        if (req.http.Cookie:FASTLY_CDN_FORMKEY &&
std.strlen(req.http.Cookie:FASTLY_CDN_FORMKEY) < 40 && req.http.Cookie:FASTLY_CDN_FORMKEY ~ "^[a-zA-Z0-9-_]*$" )  {
            set req.http.Formkey = req.http.Cookie:FASTLY_CDN_FORMKEY;
        } else {
            # create formkey
            set req.http.seed = req.http.Cookie client.ip remote.port client.geo.longitude client.geo.latitude client.geo.postal_code;
            set req.http.Formkey = regsub(digest.hash_md5(req.http.seed), "^0x", "");
        }
        error 760 req.http.Formkey;
    }

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

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

    # check for ESI calls
    if (req.url.qs ~ "esi_data=") {
        # check for valid cookie data
        declare local var.esi_data_field STRING;
        declare local var.cookie_data STRING;
        # Based on esi_data value requested we will need to search for cookie FASTLY_CDN-<type> e.g. FASTLY_CDN-customer_quote
        set var.esi_data_field = "FASTLY_CDN-" subfield(req.url.qs, "esi_data", "&");
        # We can't use variables in either subfield or regex so we need to use this workaround
        # to extract value of cookie that we compiled in esi_data_field
        set var.cookie_data = std.strstr(req.http.Cookie,var.esi_data_field);
        set var.cookie_data = regsub(var.cookie_data,"^[^=]*=([^;]*).*","\1");
        # If found a value we replace the query string with the contents of that cookie
        if ( var.cookie_data != "" ) {
          set req.url = querystring.set(req.url, "esi_data", var.cookie_data);
        }
    }

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


Syntax tweaks

Syntax tweaks

# Source: https://raw.githubusercontent.com/magento/magento2/2.1/app/code/Magento/PageCache/etc/varnish4.vcl
# This VCL is part of the Magento2 application code should be maintained by the website developer or Service Integrator.
# Modifications from source: .host, .port, acl purge ranges, and use of X-Forwarded-Proto header.

vcl 4.0;

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

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

acl purge {
 "127.0.0.1";
 "192.168.0.0"/16;
 "172.24.0.0"/16;
 "10.0.0.0"/8;
}

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

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

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

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

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

    # collect all cookies
    std.collect(req.http.Cookie);

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

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

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

    return (hash);
}

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

    # For multi site configurations to not cache each other's content
    if (req.http.host) {
        hash_data(req.http.host);
    } else {
        hash_data(server.ip);
    }

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

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

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

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

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

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

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

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

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

# Source: https://raw.githubusercontent.com/magento/magento2/2.1/app/code/Magento/PageCache/etc/varnish4.vcl
# This VCL is part of the Magento2 application code should be maintained by the website developer or Service Integrator.
# Modifications from source: .host, .port, acl purge ranges, and use of X-Forwarded-Proto header.

vcl 4.0;

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

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

acl purge {
 "127.0.0.1"
 "192.168.0.0"/16
 "172.24.0.0"/16
 "10.0.0.0"/8
}

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

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

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

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

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

    # collect all cookies
    std.collect(req.http.Cookie);

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

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

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

    return (hash);
}

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

    # For multi site configurations to not cache each other's content
    if (req.http.host) {
        hash_data(req.http.host);
    } else {
        hash_data(server.ip);
    }

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

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

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

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

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

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

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

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

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


change from req.http.url to req.url

change from req.http.url to req.url

sub vcl_recv {
	# Fastly recommend using the 900-999 range of status codes for custom errors within an application's VCL.
	if (req.http.Orig-URL ~ "^/v3/normalizeUa") {
		error 903 "Normalise User Agent";
	}
	if (req.http.Orig-URL ~ "^/v3/parseUa") {
		error 904 "Parse User Agent";
	}
	if (req.http.Orig-URL ~ "^/v3/normalise_querystring_parameters_for_polyfill_bundle") {
		error 905 "Normalise querystring parameters for polyfill bundle";
	}
	if (req.http.Orig-URL ~ "^/robots.txt") {
		error 906;
	}
	if (req.url ~ "^/https://cdn.polyfill.io") {
		error 907;
	}
	if (req.url ~ "^/https://polyfill.io") {
		error 907;
	}
	if (req.url ~ "^/pages/fixedData") {
		error 907;
	}
}

sub vcl_error {
	# Normalise User Agent API
	if (obj.status == 903) {
		call normalise_user_agent_1_5_0;
		set obj.status = 200;
		set obj.response = "OK";
		set obj.http.Content-Type = "text/plain; charset=utf-8";
		set obj.http.Normalized-User-Agent = req.http.Normalized-User-Agent;
		synthetic req.http.Normalized-User-Agent;
		return (deliver);
	}

	# Parse User Agent API
	if (obj.status == 904) {
		call useragent_parser;
		set obj.status = 200;
		set obj.response = "OK";
		set obj.http.Content-Type = "text/plain; charset=utf-8";
		set obj.http.useragent_parser_family = req.http.useragent_parser_family;
		set obj.http.useragent_parser_major = req.http.useragent_parser_major;
		set obj.http.useragent_parser_minor = req.http.useragent_parser_minor;
		set obj.http.useragent_parser_patch = req.http.useragent_parser_patch;
		synthetic req.http.useragent_parser_family "/"  req.http.useragent_parser_major "." req.http.useragent_parser_minor "." req.http.useragent_parser_patch;
		return (deliver);
	}

	# Normalise querystring parameters
	if (obj.status == 905) {
		call normalise_querystring_parameters_for_polyfill_bundle;
		set obj.status = 200;
		set obj.response = "OK";
		set obj.http.Content-Type = "application/json; charset=utf-8";
		set obj.http.features = subfield(req.url.qs, "features", "&");
		set obj.http.excludes = subfield(req.url.qs, "excludes", "&");
		set obj.http.rum = subfield(req.url.qs, "rum", "&");
		set obj.http.unknown = subfield(req.url.qs, "unknown", "&");
		set obj.http.flags = subfield(req.url.qs, "flags", "&");
		set obj.http.ua = subfield(req.url.qs, "ua", "&");
		set obj.http.callback = subfield(req.url.qs, "callback", "&");
		set obj.http.compression = subfield(req.url.qs, "compression", "&");
		set obj.http.version = subfield(req.url.qs, "version", "&");
		synthetic "{"
			{"""} "features" {"""} ":" {"""} obj.http.features {"""}
			if (obj.http.excludes, ","{"""} "excludes" {"""} ":" {"""} obj.http.excludes {"""}, "")
			","{"""} "rum" {"""} ":" {"""} obj.http.rum {"""}
			","{"""} "unknown" {"""} ":" {"""} obj.http.unknown {"""}
			if (obj.http.flags, ","{"""} "flags" {"""} ":" {"""} obj.http.flags {"""}, "")
			","{"""} "ua" {"""} ":" {"""} obj.http.ua {"""}
			if (obj.http.callback, ","{"""} "callback" {"""} ":" {"""} obj.http.callback {"""}, "")
			if (obj.http.compression, ","{"""} "compression" {"""} ":" {"""} obj.http.compression {"""}, "")
			if (obj.http.version, ","{"""} "version" {"""} ":" {"""} obj.http.version {"""}, "")
		"}";
		return (deliver);
	}

	# /robots.txt endpoint
	if (obj.status == 906) {
		set obj.status = 200;
		set obj.response = "OK";
		set obj.http.Content-Type = "text/plain; charset=utf-8";
		synthetic {"User-agent: *
Disallow:"};
		return (deliver);
	}

	# return 404
	if (obj.status == 907) {
		set obj.status = 404;
		set obj.response = "Not Found";
		set obj.http.Content-Type = "text/plain; charset=utf-8";
		synthetic {"Not Found."};
		return (deliver);
	}
}

sub vcl_recv {
	# Fastly recommend using the 900-999 range of status codes for custom errors within an application's VCL.
	if (req.http.Orig-URL ~ "^/v3/normalizeUa") {
		error 903 "Normalise User Agent";
	}
	if (req.http.Orig-URL ~ "^/v3/parseUa") {
		error 904 "Parse User Agent";
	}
	if (req.http.Orig-URL ~ "^/v3/normalise_querystring_parameters_for_polyfill_bundle") {
		error 905 "Normalise querystring parameters for polyfill bundle";
	}
	if (req.http.Orig-URL ~ "^/robots.txt") {
		error 906;
	}
	if (req.http.url ~ "^/https://cdn.polyfill.io") {
		error 907;
	}
	if (req.http.url ~ "^/https://polyfill.io") {
		error 907;
	}
	if (req.http.url ~ "^/pages/fixedData") {
		error 907;
	}
}

sub vcl_error {
	# Normalise User Agent API
	if (obj.status == 903) {
		call normalise_user_agent_1_5_0;
		set obj.status = 200;
		set obj.response = "OK";
		set obj.http.Content-Type = "text/plain; charset=utf-8";
		set obj.http.Normalized-User-Agent = req.http.Normalized-User-Agent;
		synthetic req.http.Normalized-User-Agent;
		return (deliver);
	}

	# Parse User Agent API
	if (obj.status == 904) {
		call useragent_parser;
		set obj.status = 200;
		set obj.response = "OK";
		set obj.http.Content-Type = "text/plain; charset=utf-8";
		set obj.http.useragent_parser_family = req.http.useragent_parser_family;
		set obj.http.useragent_parser_major = req.http.useragent_parser_major;
		set obj.http.useragent_parser_minor = req.http.useragent_parser_minor;
		set obj.http.useragent_parser_patch = req.http.useragent_parser_patch;
		synthetic req.http.useragent_parser_family "/"  req.http.useragent_parser_major "." req.http.useragent_parser_minor "." req.http.useragent_parser_patch;
		return (deliver);
	}

	# Normalise querystring parameters
	if (obj.status == 905) {
		call normalise_querystring_parameters_for_polyfill_bundle;
		set obj.status = 200;
		set obj.response = "OK";
		set obj.http.Content-Type = "application/json; charset=utf-8";
		set obj.http.features = subfield(req.url.qs, "features", "&");
		set obj.http.excludes = subfield(req.url.qs, "excludes", "&");
		set obj.http.rum = subfield(req.url.qs, "rum", "&");
		set obj.http.unknown = subfield(req.url.qs, "unknown", "&");
		set obj.http.flags = subfield(req.url.qs, "flags", "&");
		set obj.http.ua = subfield(req.url.qs, "ua", "&");
		set obj.http.callback = subfield(req.url.qs, "callback", "&");
		set obj.http.compression = subfield(req.url.qs, "compression", "&");
		set obj.http.version = subfield(req.url.qs, "version", "&");
		synthetic "{"
			{"""} "features" {"""} ":" {"""} obj.http.features {"""}
			if (obj.http.excludes, ","{"""} "excludes" {"""} ":" {"""} obj.http.excludes {"""}, "")
			","{"""} "rum" {"""} ":" {"""} obj.http.rum {"""}
			","{"""} "unknown" {"""} ":" {"""} obj.http.unknown {"""}
			if (obj.http.flags, ","{"""} "flags" {"""} ":" {"""} obj.http.flags {"""}, "")
			","{"""} "ua" {"""} ":" {"""} obj.http.ua {"""}
			if (obj.http.callback, ","{"""} "callback" {"""} ":" {"""} obj.http.callback {"""}, "")
			if (obj.http.compression, ","{"""} "compression" {"""} ":" {"""} obj.http.compression {"""}, "")
			if (obj.http.version, ","{"""} "version" {"""} ":" {"""} obj.http.version {"""}, "")
		"}";
		return (deliver);
	}

	# /robots.txt endpoint
	if (obj.status == 906) {
		set obj.status = 200;
		set obj.response = "OK";
		set obj.http.Content-Type = "text/plain; charset=utf-8";
		synthetic {"User-agent: *
Disallow:"};
		return (deliver);
	}

	# return 404
	if (obj.status == 907) {
		set obj.status = 404;
		set obj.response = "Not Found";
		set obj.http.Content-Type = "text/plain; charset=utf-8";
		synthetic {"Not Found."};
		return (deliver);
	}
}


Correct condition for HTML caching

Correct condition for HTML caching

    if (beresp.status >= 500 && beresp.status < 600) {
        # let SOAP errors pass - better debugging
        if (beresp.http.Content-Type ~ "text/xml") {
            return (deliver);
        }

        /* deliver stale if the object is available */
        if (stale.exists) {
            return(deliver_stale);
        }

        if (req.restarts < 1 && (req.request == "GET" || req.request == "HEAD")) {
            restart;
        }

        /* else go to vcl_error to deliver a synthetic */
        error 503;

    }

    if (req.restarts > 0 ) {
        set beresp.http.Fastly-Restarts = req.restarts;
    }

    if (beresp.http.Content-Type ~ "text/(html|xml)") {
        # enable ESI feature for Magento response by default
        esi;
        if (!beresp.http.Vary ~ "Fastly-Cdn-Env,Https") {
            if (beresp.http.Vary) {
                    set beresp.http.Vary = beresp.http.Vary ",Fastly-Cdn-Env,Https";
                } else {
                    set beresp.http.Vary = "Fastly-Cdn-Env,Https";
                }
        }
        # Since varnish doesn't compress ESIs we need to hint to the HTTP/2 terminators to
        # compress it
        set beresp.http.x-compress-hint = "on";
    } else {
        # enable gzip for all static content
        if (http_status_matches(beresp.status, "200,404") && (beresp.http.content-type ~ "^(application\/x\-javascript|text\/css|application\/javascript|text\/javascript|application\/json|application\/vnd\.ms\-fontobject|application\/x\-font\-opentype|application\/x\-font\-truetype|application\/x\-font\-ttf|application\/xml|font\/eot|font\/opentype|font\/otf|image\/svg\+xml|image\/vnd\.microsoft\.icon|text\/plain)\s*($|;)" || req.url.ext ~ "(?i)(css|js|html|eot|ico|otf|ttf|json)" ) ) {
            # always set vary to make sure uncompressed versions dont always win
            if (!beresp.http.Vary ~ "Accept-Encoding") {
                if (beresp.http.Vary) {
                    set beresp.http.Vary = beresp.http.Vary ", Accept-Encoding";
                } else {
                    set beresp.http.Vary = "Accept-Encoding";
                }
            }
            if (req.http.Accept-Encoding == "gzip") {
                set beresp.gzip = true;
            }
        }
    }

    if ( beresp.ttl > 0s && (req.request == "GET" || req.request == "HEAD") && !req.http.x-pass ) {
        if (beresp.http.Content-Type ~ "text/(html|xml)") {
            # marker for vcl_deliver to reset Age:
            set beresp.http.magentomarker = "1";

            # Don't cache cookies
            unset beresp.http.set-cookie;
        } else {
            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;
            }
        }

        # init surrogate keys
        if (beresp.http.Surrogate-Key) {
            set beresp.http.Surrogate-Key = beresp.http.Surrogate-Key " text";
        } else {
            set beresp.http.Surrogate-Key = "text";
        }

        # set surrogate keys by content type
        if (beresp.http.Content-Type ~ "image") {
            set beresp.http.Surrogate-Key = "image";
        } elsif (beresp.http.Content-Type ~ "script") {
            set beresp.http.Surrogate-Key = "script";
        } elsif (beresp.http.Content-Type ~ "css") {
            set beresp.http.Surrogate-Key = "css";
        }

    }

    if (beresp.status >= 500 && beresp.status < 600) {
        # let SOAP errors pass - better debugging
        if (beresp.http.Content-Type ~ "text/xml") {
            return (deliver);
        }

        /* deliver stale if the object is available */
        if (stale.exists) {
            return(deliver_stale);
        }

        if (req.restarts < 1 && (req.request == "GET" || req.request == "HEAD")) {
            restart;
        }

        /* else go to vcl_error to deliver a synthetic */
        error 503;

    }

    if (req.restarts > 0 ) {
        set beresp.http.Fastly-Restarts = req.restarts;
    }

    if (beresp.http.Content-Type ~ "text/(html|xml)") {
        # enable ESI feature for Magento response by default
        esi;
        if (!beresp.http.Vary ~ "Fastly-Cdn-Env,Https") {
            if (beresp.http.Vary) {
                    set beresp.http.Vary = beresp.http.Vary ",Fastly-Cdn-Env,Https";
                } else {
                    set beresp.http.Vary = "Fastly-Cdn-Env,Https";
                }
        }
        # Since varnish doesn't compress ESIs we need to hint to the HTTP/2 terminators to
        # compress it
        set beresp.http.x-compress-hint = "on";
    } else {
        # enable gzip for all static content
        if (http_status_matches(beresp.status, "200,404") && (beresp.http.content-type ~ "^(application\/x\-javascript|text\/css|application\/javascript|text\/javascript|application\/json|application\/vnd\.ms\-fontobject|application\/x\-font\-opentype|application\/x\-font\-truetype|application\/x\-font\-ttf|application\/xml|font\/eot|font\/opentype|font\/otf|image\/svg\+xml|image\/vnd\.microsoft\.icon|text\/plain)\s*($|;)" || req.url.ext ~ "(?i)(css|js|html|eot|ico|otf|ttf|json)" ) ) {
            # always set vary to make sure uncompressed versions dont always win
            if (!beresp.http.Vary ~ "Accept-Encoding") {
                if (beresp.http.Vary) {
                    set beresp.http.Vary = beresp.http.Vary ", Accept-Encoding";
                } else {
                    set beresp.http.Vary = "Accept-Encoding";
                }
            }
            if (req.http.Accept-Encoding == "gzip") {
                set beresp.gzip = true;
            }
        }
    }

    if (http_status_matches(beresp.status, "200,301,404") && !req.http.X-Pass) {
        if (beresp.http.Content-Type ~ "text/(html|xml)") {
            # marker for vcl_deliver to reset Age:
            set beresp.http.magentomarker = "1";

            # Don't cache cookies
            unset beresp.http.set-cookie;
        } else {
            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;
            }
        }

        # init surrogate keys
        if (beresp.http.Surrogate-Key) {
            set beresp.http.Surrogate-Key = beresp.http.Surrogate-Key " text";
        } else {
            set beresp.http.Surrogate-Key = "text";
        }

        # set surrogate keys by content type
        if (beresp.http.Content-Type ~ "image") {
            set beresp.http.Surrogate-Key = "image";
        } elsif (beresp.http.Content-Type ~ "script") {
            set beresp.http.Surrogate-Key = "script";
        } elsif (beresp.http.Content-Type ~ "css") {
            set beresp.http.Surrogate-Key = "css";
        }

    }


In cases where image is not found Magento may end up processing the request which will set cookies which may overwrite any existing cookies

    if (beresp.status >= 500 && beresp.status < 600) {
        /* deliver stale if the object is available */
        if (stale.exists) {
            return(deliver_stale);
        }

        if (req.restarts < 1 && (req.request == "GET" || req.request == "HEAD")) {
            restart;
        }

        /* else go to vcl_error to deliver a synthetic */
        error beresp.status beresp.response;

    }

    if (beresp.http.X-Esi || beresp.http.Content-Type ~ "^text/(html|xml)") {
        # enable ESI feature for Magento response by default
        esi;
        if (!beresp.http.Vary ~ "Fastly-Cdn-Env,Https") {
            if (beresp.http.Vary) {
                    set beresp.http.Vary = beresp.http.Vary ",Fastly-Cdn-Env,Https";
                } else {
                    set beresp.http.Vary = "Fastly-Cdn-Env,Https";
                }
        }
        # marker for vcl_deliver to reset Age:
        set beresp.http.magentomarker = "1";
        # Since varnish doesn't compress ESIs we need to hint to the HTTP/2 terminators to
        # compress it
        set beresp.http.x-compress-hint = "on";
    } else {
        # enable gzip for all static content
        if (http_status_matches(beresp.status, "200,404") && (beresp.http.content-type ~ "^(application\/x\-javascript|text\/css|application\/javascript|text\/javascript|application\/json|application\/vnd\.ms\-fontobject|application\/x\-font\-opentype|application\/x\-font\-truetype|application\/x\-font\-ttf|application\/xml|font\/eot|font\/opentype|font\/otf|image\/svg\+xml|image\/vnd\.microsoft\.icon|text\/plain)\s*($|;)" || req.url.ext ~ "(?i)(css|js|html|eot|ico|otf|ttf|json)" ) ) {
            # always set vary to make sure uncompressed versions dont always win
            if (!beresp.http.Vary ~ "Accept-Encoding") {
                if (beresp.http.Vary) {
                    set beresp.http.Vary = beresp.http.Vary ", Accept-Encoding";
                } else {
                    set beresp.http.Vary = "Accept-Encoding";
                }
            }
            if (req.http.Accept-Encoding == "gzip") {
                set beresp.gzip = true;
            }
        }
    }

    # Just in case the Request Setting for x-pass is missing
    if (req.http.x-pass) {
        return (pass);
    }

    # Force any responses with private, no-cache or no-store in Cache-Control to pass
    if (beresp.http.Cache-Control ~ "private|no-cache|no-store") {
        set req.http.Fastly-Cachetype = "PRIVATE";
        return (pass);
    }

    # Varnish sets default TTL if none of these are present. Assume if they are not present that we don't want to cache
    if (!beresp.http.Expires && !beresp.http.Surrogate-Control ~ "max-age" && !beresp.http.Cache-Control ~ "(s-maxage|max-age)") {
        set beresp.ttl = 0s;
    }

    # If origin provides TTL for an object we cache it
    if ( beresp.ttl > 0s && (req.request == "GET" || req.request == "HEAD") && !req.http.x-pass ) {
        # Don't cache cookies - this is here because Magento sets cookies even for anonymous users
        # which busts cache
        unset beresp.http.set-cookie;

        # If surrogate keys have been set do not set them again
        if ( !beresp.http.surrogate-keys-set ) {
            if (beresp.http.magentomarker) {
                # init surrogate keys
                if (beresp.http.Surrogate-Key) {
                    set beresp.http.Surrogate-Key = beresp.http.Surrogate-Key " text";
                } else {
                    set beresp.http.Surrogate-Key = "text";
                }
            } else if (beresp.http.Content-Type ~ "(image|script|css)") {
                # set surrogate keys by content type if they are image/script or CSS
                if (beresp.http.Surrogate-Key) {
                    set beresp.http.Surrogate-Key = beresp.http.Surrogate-Key " " re.group.1;
                } else {
                    set beresp.http.Surrogate-Key = re.group.1;
                }
            }

            set beresp.http.surrogate-keys-set = "1";
        }

    }

    if (beresp.status == 404 && req.url.path ~ "^/(media|js|skin)/.*\.(png|jpg|jpeg|gif|css|js|swf|ico|webp|svg)$") {
        unset beresp.http.set-cookie;
    }


Triggering build?

Triggering build?

vcl 4.0;
# This is a basic VCL configuration file for varnish.  See the vcl(7)
# man page for details on VCL syntax and semantics.
#
# Default backend definition.  Set this to point to your content
# server.
#
backend default {
    .host = "${VARNISH_BACKEND_IP}";
    .port = "${VARNISH_BACKEND_PORT}";
}
#
#
# Below is a commented-out copy of the default VCL logic.  If you
# redefine any of these subroutines, the built-in logic will be
# appended to your code.
# sub vcl_recv {
#     if (req.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.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 || req.http.Cookie) {
#         /* Not cacheable by default */
#         return (pass);
#     }
#     return (lookup);
# }
#
# sub vcl_pipe {
#     # Note that only the first request to the backend will have
#     # X-Forwarded-For set.  If you use X-Forwarded-For and want to
#     # have it set for all requests, make sure to have:
#     # set bereq.http.connection = "close";
#     # here.  It is not set by default as it might break some broken web
#     # applications, like IIS with NTLM authentication.
#     return (pipe);
# }
#
# sub vcl_pass {
#     return (pass);
# }
#
# sub vcl_hash {
#     hash_data(req.url);
#     if (req.http.host) {
#         hash_data(req.http.host);
#     } else {
#         hash_data(server.ip);
#     }
#     return (hash);
# }
#
# sub vcl_hit {
#     return (deliver);
# }
#
# sub vcl_miss {
#     return (fetch);
# }
#
# 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);
# }
#
# sub vcl_deliver {
#     return (deliver);
# }
#
# sub vcl_error {
#     set obj.http.Content-Type = "text/html; charset=utf-8";
#     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">
#
#
#     <title>"} + obj.status + " " + obj.response + {"</title>
#
#
#
Error "} + obj.status + " " + obj.response + {"

#
"} + obj.response + {"

#
Guru Meditation:

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

#

#
Varnish cache server

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

vcl 4.0;
# This is a basic VCL configuration file for varnish.  See the vcl(7)
# man page for details on VCL syntax and semantics.
#
# Default backend definition.  Set this to point to your content
# server.
#
backend default {
    .host = "${VARNISH_BACKEND_IP}";
    .port = "${VARNISH_BACKEND_PORT}";
}
#
# Below is a commented-out copy of the default VCL logic.  If you
# redefine any of these subroutines, the built-in logic will be
# appended to your code.
# sub vcl_recv {
#     if (req.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.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 || req.http.Cookie) {
#         /* Not cacheable by default */
#         return (pass);
#     }
#     return (lookup);
# }
#
# sub vcl_pipe {
#     # Note that only the first request to the backend will have
#     # X-Forwarded-For set.  If you use X-Forwarded-For and want to
#     # have it set for all requests, make sure to have:
#     # set bereq.http.connection = "close";
#     # here.  It is not set by default as it might break some broken web
#     # applications, like IIS with NTLM authentication.
#     return (pipe);
# }
#
# sub vcl_pass {
#     return (pass);
# }
#
# sub vcl_hash {
#     hash_data(req.url);
#     if (req.http.host) {
#         hash_data(req.http.host);
#     } else {
#         hash_data(server.ip);
#     }
#     return (hash);
# }
#
# sub vcl_hit {
#     return (deliver);
# }
#
# sub vcl_miss {
#     return (fetch);
# }
#
# 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);
# }
#
# sub vcl_deliver {
#     return (deliver);
# }
#
# sub vcl_error {
#     set obj.http.Content-Type = "text/html; charset=utf-8";
#     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">
#
#
#     <title>"} + obj.status + " " + obj.response + {"</title>
#
#
#
Error "} + obj.status + " " + obj.response + {"

#
"} + obj.response + {"

#
Guru Meditation:

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

#

#
Varnish cache server

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


Reset cache control headers only on content that contains X-Magento-Tags. Otherwise we run the risk of unsetting Cache-Control headers of objects we didn't intend.

    # Send no cache headers to end users for non-static content created by Magento
    if (resp.http.X-Magento-Tags && !req.http.Fastly-FF ) {
        set resp.http.Cache-Control = "no-store, no-cache, must-revalidate, max-age=0";
    }

    # Execute only on the edge nodes
    if ( !req.http.Fastly-FF ) {
        # Remove X-Magento-Vary and HTTPs Vary served to the user
        set resp.http.Vary = regsub(resp.http.Vary, "X-Magento-Vary,Https", "Cookie");
        remove resp.http.X-Magento-Tags;
    }

    # Add an easy way to see whether custom Fastly VCL has been uploaded
    if ( req.http.Fastly-Debug ) {
        set resp.http.Fastly-Magento-VCL-Uploaded = "1.2.74";
    } else {
        remove resp.http.Fastly-Module-Enabled;
        remove resp.http.fastly-page-cacheable;
    }

    # debug info
    if (!resp.http.X-Magento-Debug) {
        # remove Varnish/proxy header
        remove resp.http.X-Magento-Debug;
        remove resp.http.X-Magento-Cache-Control;
        remove resp.http.X-Powered-By;
        remove resp.http.Server;
        remove resp.http.X-Varnish;
        remove resp.http.Via;
        remove resp.http.X-Purge-URL;
        remove resp.http.X-Purge-Host;
    }

    # Send no cache headers to end users for non-static content. Also make sure
    # we only set this on the edge nodes and not on shields
    if (req.url !~ "^/(pub/)?(media|static)/.*" && !req.http.Fastly-FF ) {
        set resp.http.Cache-Control = "no-store, no-cache, must-revalidate, max-age=0";
    }

    # Execute only on the edge nodes
    if ( !req.http.Fastly-FF ) {
        # Remove X-Magento-Vary and HTTPs Vary served to the user
        set resp.http.Vary = regsub(resp.http.Vary, "X-Magento-Vary,Https", "Cookie");
        remove resp.http.X-Magento-Tags;
    }

    # Add an easy way to see whether custom Fastly VCL has been uploaded
    if ( req.http.Fastly-Debug ) {
        set resp.http.Fastly-Magento-VCL-Uploaded = "1.2.74";
    } else {
        remove resp.http.Fastly-Module-Enabled;
        remove resp.http.fastly-page-cacheable;
    }

    # debug info
    if (!resp.http.X-Magento-Debug) {
        # remove Varnish/proxy header
        remove resp.http.X-Magento-Debug;
        remove resp.http.X-Magento-Cache-Control;
        remove resp.http.X-Powered-By;
        remove resp.http.Server;
        remove resp.http.X-Varnish;
        remove resp.http.Via;
        remove resp.http.X-Purge-URL;
        remove resp.http.X-Purge-Host;
    }


cache ttf fonts in varnish

cache ttf fonts in varnish

# This a configuration file for varnish.
# It was generated by plone.recipe.varnish.
# See the vcl(7) man page for details on VCL syntax and semantics.
#
vcl 4.0;

import std;

# configure all backends
backend backend_000 {
   .host = "127.0.0.1";
   .port = "5001";
   .connect_timeout = 0.4s;
   .first_byte_timeout = 300s;
   .between_bytes_timeout  = 60s;
}

sub vcl_init {

}

acl purge {
    "localhost";
    "127.0.0.1";
}

sub vcl_recv {
    set req.backend_hint = backend_000;
    set req.http.grace = "none";

    if (req.method == "PURGE") {
        # Not from an allowed IP? Then die with an error.
        if (!client.ip ~ purge) {
            return (synth(405, "This IP is not allowed to send PURGE requests."));
        }
        return(purge);
    }

    if (req.method == "BAN") {
            # Same ACL check as above:
            if (!client.ip ~ purge) {
            return(synth(403, "Not allowed."));
            }
            #ban("req.url ~ " + req.url);
        ban("req.http.host == " + req.http.host +
            " && req.url == " + req.url);
            # Throw a synthetic page so the
            # request won't go to the backend.
            return(synth(200, "Ban added"));
    }

    if (req.method == "USERINFO" ||
        req.method == "CONNECT" ||
        req.method == "QUIT") {
        return (synth(501, "Not implemented"));
    }

    # Only deal with "normal" types
    if (req.method != "GET" &&
           req.method != "HEAD" &&
           req.method != "PUT" &&
           req.method != "TRACE" &&
           req.method != "OPTIONS" &&
           req.method != "DELETE") {
        /* Non-RFC2616 or CONNECT which is weird. */
        return(pipe);
    }

    # Only cache GET or HEAD requests. This makes sure the POST requests are always passed.
    if (req.method != "GET" && req.method != "HEAD") {
        return(pass);
    }

    if (req.http.Expect) {
        return(pipe);
    }

    if (req.http.If-None-Match && !req.http.If-Modified-Since) {
        return(pass);
    }

    /* Do not cache other authorized content by default */
    if (req.http.Authenticate || req.http.Authorization) {
        return(pass);
    }

    if (req.url ~ "@@registration|@@login|@@resetpassword") {
        return(pass);
    }

    if (req.url ~ "^[^?]*\.(svg|css|js|png|gif|jpeg|jpg|ttf|woff|woff2)(\?.*)?$") {
       unset req.http.Cookie;
    }

    if (req.http.Cookie && req.http.Cookie ~ "auth_tkt") {
        return(pass);
    }

    unset req.http.Accept-Encoding;

    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);
    }
    if (req.http.X-Forwarded-Proto) {
        hash_data(req.http.X-Forwarded-Proto);
    }
    return (lookup);
}

sub vcl_purge {
    return (synth(200, "Purged"));
}

sub vcl_hit {
    if (obj.ttl >= 0s) {
        // A pure unadultered hit, deliver it
        # normal hit
        return (deliver);
    }

    // Object is in grace, deliver it
    // Automatically triggers a background fetch
    if (obj.ttl + obj.grace > 0s) {
        set req.http.grace = "normal(limited)";
        return (deliver);
    }

    if (req.method == "PURGE") {
        set req.method = "GET";
        set req.http.X-purger = "Purged";
        return(synth(200, "Purged. in hit " + req.url));
    }

    // fetch & deliver once we get the result
    return (fetch);
}

sub vcl_miss {
    if (req.method == "PURGE") {
        set req.method = "GET";
        set req.http.X-purger = "Purged-possibly";
        return(synth(200, "Purged. in miss " + req.url));
    }

    // fetch & deliver once we get the result
    return (fetch);
}

sub vcl_backend_fetch{
    return (fetch);
}

sub vcl_backend_response {
    # The object is not cacheable
    if (beresp.http.Set-Cookie) {
        set beresp.http.X-Cacheable = "NO - Set Cookie";
        set beresp.ttl = 0s;
        set beresp.uncacheable = true;
    } elsif (beresp.http.Cache-Control ~ "private") {
        set beresp.http.X-Cacheable = "NO - Cache-Control=private";
        set beresp.uncacheable = true;
        set beresp.ttl = 120s;
    } elsif (beresp.http.Surrogate-control ~ "no-store") {
        set beresp.http.X-Cacheable = "NO - Surrogate-control=no-store";
        set beresp.uncacheable = true;
        set beresp.ttl = 120s;
    } elsif (!beresp.http.Surrogate-Control && beresp.http.Cache-Control ~ "no-cache|no-store") {
        set beresp.http.X-Cacheable = "NO - Cache-Control=no-cache|no-store";
        set beresp.uncacheable = true;
        set beresp.ttl = 120s;
    } elsif (beresp.http.Vary == "*") {
        set beresp.http.X-Cacheable = "NO - Vary=*";
        set beresp.uncacheable = true;
        set beresp.ttl = 120s;

    # ttl handling
    } elsif (beresp.ttl < 0s) {
        set beresp.http.X-Cacheable = "NO - TTL < 0";
        set beresp.uncacheable = true;
    } elsif (beresp.ttl == 0s) {
        if (bereq.http.Cookie && bereq.http.Cookie ~ "auth_tkt") {
            set beresp.http.X-Cacheable = "NO - TTL = 0";
            set beresp.uncacheable = true;
        } else {
            set beresp.http.X-Cacheable = "YES, 3600s";
            set beresp.ttl = 3600s;
            set beresp.grace = 36000s;
        }

    # Varnish determined the object was cacheable
    } else {
        set beresp.http.X-Cacheable = "YES";
    }

# https://www.varnish-cache.org/trac/wiki/VCLExampleLongerCaching
    if (beresp.ttl == 31536000s) {  # for deform.js, jquery.form-3.09.js from deformstatic
        /* Remove Expires from backend, it's too long */
        unset beresp.http.expires;

        /* Set the clients TTL on this object */
        set beresp.http.cache-control = "max-age=86400";

        /* Set how long Varnish will keep it */
        set beresp.ttl = 86400s;
    }

    # Do not cache 5xx errors
    if (beresp.status >= 500 && beresp.status < 600) {
        unset beresp.http.Cache-Control;
        set beresp.http.X-Cache = "NOCACHE";
        set beresp.http.Cache-Control = "no-cache, max-age=0, must-revalidate";
        set beresp.ttl = 0s;
        set beresp.http.Pragma = "no-cache";
        set beresp.uncacheable = true;
        return(deliver);
    }

    return (deliver);
}

sub vcl_deliver {
    set resp.http.grace = req.http.grace;
    if (obj.hits > 0) {
         set resp.http.X-Cache = "HIT";
    } else {
        set resp.http.X-Cache = "MISS";
    }
    /* Rewrite s-maxage to exclude from intermediary proxies
      (to cache *everywhere*, just use 'max-age' token in the response to avoid
      this override) */
    if (resp.http.Cache-Control ~ "s-maxage") {
        set resp.http.Cache-Control = regsub(resp.http.Cache-Control, "s-maxage=[0-9]+", "s-maxage=0");
    }
    /* Remove proxy-revalidate for intermediary proxies */
    if (resp.http.Cache-Control ~ ", proxy-revalidate") {
        set resp.http.Cache-Control = regsub(resp.http.Cache-Control, ", proxy-revalidate", "");
    }

    unset resp.http.Server;

}

/*
 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( {"
        <?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>Varnish cache server: "} + resp.status + " " + resp.reason + {" </title>

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

"} + resp.reason + {"

Guru Meditation:

XID: "} + req.xid + {"

Varnish cache server

    "} );

    return (deliver);
}

# This a configuration file for varnish.
# It was generated by plone.recipe.varnish.
# See the vcl(7) man page for details on VCL syntax and semantics.
#
vcl 4.0;

import std;

# configure all backends
backend backend_000 {
   .host = "127.0.0.1";
   .port = "5001";
   .connect_timeout = 0.4s;
   .first_byte_timeout = 300s;
   .between_bytes_timeout  = 60s;
}

sub vcl_init {

}

acl purge {
    "localhost";
    "127.0.0.1";
}

sub vcl_recv {
    set req.backend_hint = backend_000;
    set req.http.grace = "none";

    if (req.method == "PURGE") {
        # Not from an allowed IP? Then die with an error.
        if (!client.ip ~ purge) {
            return (synth(405, "This IP is not allowed to send PURGE requests."));
        }
        return(purge);
    }

    if (req.method == "BAN") {
            # Same ACL check as above:
            if (!client.ip ~ purge) {
            return(synth(403, "Not allowed."));
            }
            #ban("req.url ~ " + req.url);
        ban("req.http.host == " + req.http.host +
            " && req.url == " + req.url);
            # Throw a synthetic page so the
            # request won't go to the backend.
            return(synth(200, "Ban added"));
    }

    if (req.method == "USERINFO" ||
        req.method == "CONNECT" ||
        req.method == "QUIT") {
        return (synth(501, "Not implemented"));
    }

    # Only deal with "normal" types
    if (req.method != "GET" &&
           req.method != "HEAD" &&
           req.method != "PUT" &&
           req.method != "TRACE" &&
           req.method != "OPTIONS" &&
           req.method != "DELETE") {
        /* Non-RFC2616 or CONNECT which is weird. */
        return(pipe);
    }

    # Only cache GET or HEAD requests. This makes sure the POST requests are always passed.
    if (req.method != "GET" && req.method != "HEAD") {
        return(pass);
    }

    if (req.http.Expect) {
        return(pipe);
    }

    if (req.http.If-None-Match && !req.http.If-Modified-Since) {
        return(pass);
    }

    /* Do not cache other authorized content by default */
    if (req.http.Authenticate || req.http.Authorization) {
        return(pass);
    }

    if (req.url ~ "@@registration|@@login|@@resetpassword") {
        return(pass);
    }

    if (req.url ~ "^[^?]*\.(svg|css|js|png|gif|jpeg|jpg|woff|woff2)(\?.*)?$") {
       unset req.http.Cookie;
    }

    if (req.http.Cookie && req.http.Cookie ~ "auth_tkt") {
        return(pass);
    }

    unset req.http.Accept-Encoding;

    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);
    }
    if (req.http.X-Forwarded-Proto) {
        hash_data(req.http.X-Forwarded-Proto);
    }
    return (lookup);
}

sub vcl_purge {
    return (synth(200, "Purged"));
}

sub vcl_hit {
    if (obj.ttl >= 0s) {
        // A pure unadultered hit, deliver it
        # normal hit
        return (deliver);
    }

    // Object is in grace, deliver it
    // Automatically triggers a background fetch
    if (obj.ttl + obj.grace > 0s) {
        set req.http.grace = "normal(limited)";
        return (deliver);
    }

    if (req.method == "PURGE") {
        set req.method = "GET";
        set req.http.X-purger = "Purged";
        return(synth(200, "Purged. in hit " + req.url));
    }

    // fetch & deliver once we get the result
    return (fetch);
}

sub vcl_miss {
    if (req.method == "PURGE") {
        set req.method = "GET";
        set req.http.X-purger = "Purged-possibly";
        return(synth(200, "Purged. in miss " + req.url));
    }

    // fetch & deliver once we get the result
    return (fetch);
}

sub vcl_backend_fetch{
    return (fetch);
}

sub vcl_backend_response {
    # The object is not cacheable
    if (beresp.http.Set-Cookie) {
        set beresp.http.X-Cacheable = "NO - Set Cookie";
        set beresp.ttl = 0s;
        set beresp.uncacheable = true;
    } elsif (beresp.http.Cache-Control ~ "private") {
        set beresp.http.X-Cacheable = "NO - Cache-Control=private";
        set beresp.uncacheable = true;
        set beresp.ttl = 120s;
    } elsif (beresp.http.Surrogate-control ~ "no-store") {
        set beresp.http.X-Cacheable = "NO - Surrogate-control=no-store";
        set beresp.uncacheable = true;
        set beresp.ttl = 120s;
    } elsif (!beresp.http.Surrogate-Control && beresp.http.Cache-Control ~ "no-cache|no-store") {
        set beresp.http.X-Cacheable = "NO - Cache-Control=no-cache|no-store";
        set beresp.uncacheable = true;
        set beresp.ttl = 120s;
    } elsif (beresp.http.Vary == "*") {
        set beresp.http.X-Cacheable = "NO - Vary=*";
        set beresp.uncacheable = true;
        set beresp.ttl = 120s;

    # ttl handling
    } elsif (beresp.ttl < 0s) {
        set beresp.http.X-Cacheable = "NO - TTL < 0";
        set beresp.uncacheable = true;
    } elsif (beresp.ttl == 0s) {
        if (bereq.http.Cookie && bereq.http.Cookie ~ "auth_tkt") {
            set beresp.http.X-Cacheable = "NO - TTL = 0";
            set beresp.uncacheable = true;
        } else {
            set beresp.http.X-Cacheable = "YES, 3600s";
            set beresp.ttl = 3600s;
            set beresp.grace = 36000s;
        }

    # Varnish determined the object was cacheable
    } else {
        set beresp.http.X-Cacheable = "YES";
    }

# https://www.varnish-cache.org/trac/wiki/VCLExampleLongerCaching
    if (beresp.ttl == 31536000s) {  # for deform.js, jquery.form-3.09.js from deformstatic
        /* Remove Expires from backend, it's too long */
        unset beresp.http.expires;

        /* Set the clients TTL on this object */
        set beresp.http.cache-control = "max-age=86400";

        /* Set how long Varnish will keep it */
        set beresp.ttl = 86400s;
    }

    # Do not cache 5xx errors
    if (beresp.status >= 500 && beresp.status < 600) {
        unset beresp.http.Cache-Control;
        set beresp.http.X-Cache = "NOCACHE";
        set beresp.http.Cache-Control = "no-cache, max-age=0, must-revalidate";
        set beresp.ttl = 0s;
        set beresp.http.Pragma = "no-cache";
        set beresp.uncacheable = true;
        return(deliver);
    }

    return (deliver);
}

sub vcl_deliver {
    set resp.http.grace = req.http.grace;
    if (obj.hits > 0) {
         set resp.http.X-Cache = "HIT";
    } else {
        set resp.http.X-Cache = "MISS";
    }
    /* Rewrite s-maxage to exclude from intermediary proxies
      (to cache *everywhere*, just use 'max-age' token in the response to avoid
      this override) */
    if (resp.http.Cache-Control ~ "s-maxage") {
        set resp.http.Cache-Control = regsub(resp.http.Cache-Control, "s-maxage=[0-9]+", "s-maxage=0");
    }
    /* Remove proxy-revalidate for intermediary proxies */
    if (resp.http.Cache-Control ~ ", proxy-revalidate") {
        set resp.http.Cache-Control = regsub(resp.http.Cache-Control, ", proxy-revalidate", "");
    }

    unset resp.http.Server;

}

/*
 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( {"
        <?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>Varnish cache server: "} + resp.status + " " + resp.reason + {" </title>

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

"} + resp.reason + {"

Guru Meditation:

XID: "} + req.xid + {"

Varnish cache server

    "} );

    return (deliver);
}