Clearer diagnostics.

Clearer diagnostics.

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) {
    set req.http.X-Passthrough-Reason = "No Caching Authenticated Content";
    return (pass);
  }

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

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

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

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

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

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

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

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

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

{% endif %}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  return (fetch);
{% endif %}
}

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

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

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

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

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

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

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

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) {
    set req.http.X-Passthrough-Reason = "No Caching Authenticated Content";
    return (pass);
  }

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

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

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

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

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

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

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

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

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

{% endif %}

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

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

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

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

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

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

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

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

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

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

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

{% if varnish_cookie_sanitization_discard_from_client %}
    set req.http.X-Cookie-Discarded = "YES";
    unset req.http.Cookie;
{% endif %}
}

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

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

  return (fetch);
{% endif %}
}

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

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

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

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

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

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

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


Example VCL: Cleanup and update for varnish version >= 1.2

git-svn-id: 2c9807fa3ff65b17195bd55dc8a6c4261e10127b@2634 d4fa192b-c00b-0410-8231-f00ffab90ce4

#
# This is an example VCL configuration file for varnish, meant for the
# Plone CMS running within Zope.  It defines a "default" backend for
# serving static content from a normal web server, and a "zope"
# backend for requests to the Zope CMS
#
# See the vcl(7) man page for details on VCL syntax and semantics.
#
# $Id$
#

# Default backend definition.  Set this to point to your content
# server.

# Default backend is the Zope CMS
backend default {
	set backend.host = "127.0.0.1";
	set backend.port = "9673";
}

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

sub vcl_recv {

	# Normalize host headers, and do rewriting for the zope sites.  Reject
	# requests for unknown hosts
        if (req.http.host ~ "(www.)?example.com") {
                set req.http.host = "example.com";
                set req.url = "/VirtualHostBase/http/example.com:80/example.com/VirtualHostRoot" req.url;
        } elsif (req.http.host ~ "(www.)?example.org") {
                set req.http.host = "example.org";
                set req.url = "/VirtualHostBase/http/example.org:80/example.org/VirtualHostRoot" req.url;
        } else {
                error 404 "Unknown virtual host.";
        }

        # Handle special requests
        if (req.request != "GET" && req.request != "HEAD") {

                # POST - Logins and edits
                if (req.request == "POST") {
                        pass;
                }

                # PURGE - The CacheFu product can invalidate updated URLs
                if (req.request == "PURGE") {
                        if (!client.ip ~ purge) {
                                error 405 "Not allowed.";
                        }
                        lookup;
                }
        }

        # Don't cache authenticated requests
        if (req.http.Cookie && req.http.Cookie ~ "__ac(|_(name|password|persistent))=") {

		# Force lookup of specific urls unlikely to need protection
		if (req.url ~ "\.(js|css)") {
                        remove req.http.cookie;
                        lookup;
                }
                pass;
        }

        # The default vcl_recv is used from here.
 }

# Do the PURGE thing
sub vcl_hit {
        if (req.request == "PURGE") {
                set obj.ttl = 0s;
                error 200 "Purged";
        }
}
sub vcl_miss {
        if (req.request == "PURGE") {
                error 404 "Not in cache";
        }
}

# Enforce a minimum TTL, since we can PURGE changed objects actively
# from Zope by using the CacheFu product

sub vcl_fetch {
        if (obj.ttl < 3600s) {
                set obj.ttl = 3600s;
        }
}

#
# This is a basic VCL configuration file for varnish.  See the vcl(7)
# man page for details on VCL syntax and semantics.
#
# $Id: default.vcl 1424 2007-05-15 19:38:56Z des $
#

# Default backend definition.  Set this to point to your content
# server.

backend default {
	set backend.host = "127.0.0.1";
	set backend.port = "9673";
}

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

sub vcl_recv {
	if (req.request != "GET" && req.request != "HEAD") {
        	# PURGE request if zope asks nicely
        	if (req.request == "PURGE") {
        	        if (!client.ip ~ purge) {
        	                error 405 "Not allowed.";
        	        }
        	        lookup;
        	}
		pipe;
	}
	if (req.http.Expect) {
		pipe;
	}
	if (req.http.Authenticate || req.http.Authorization) {
		pass;
	}
	# We only care about the "__ac.*" cookies, used for authentication
	if (req.http.Cookie && req.http.Cookie ~ "__ac(|_(name|password|persistent))=") {
		pass;
	}
	lookup;
}

# Do the PURGE thing
sub vcl_hit {
        if (req.request == "PURGE") {
                set obj.ttl = 0s;
                error 200 "Purged";
        }
}
sub vcl_miss {
        if (req.request == "PURGE") {
                error 404 "Not in cache";
        }
}

# Enforce a minimum TTL, since we PURGE changed objects actively from Zope.
sub vcl_fetch {
        if (obj.ttl < 3600s) {
                set obj.ttl = 3600s;
        }
}

# 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.

## Called when a client request is received
#
#sub vcl_recv {
#	if (req.request != "GET" && req.request != "HEAD") {
#		pipe;
#	}
#	if (req.http.Expect) {
#		pipe;
#	}
#	if (req.http.Authenticate || req.http.Cookie) {
#		pass;
#	}
#	lookup;
#}
#
## Called when entering pipe mode
#
#sub vcl_pipe {
#	pipe;
#}
#
## Called when entering pass mode
#
#sub vcl_pass {
#	pass;
#}
#
## Called when entering an object into the cache
#
#sub vcl_hash {
#	hash;
#}
#
## Called when the requested object was found in the cache
#
#sub vcl_hit {
#	if (!obj.cacheable) {
#		pass;
#	}
#	deliver;
#}
#
## Called when the requested object was not found in the cache
#
#sub vcl_miss {
#	fetch;
#}
#
## Called when the requested object has been retrieved from the
## backend, or the request to the backend has failed
#
#sub vcl_fetch {
#	if (!obj.valid) {
#		error;
#	}
#	if (!obj.cacheable) {
#		pass;
#	}
#	if (resp.http.Set-Cookie) {
#		pass;
#	}
#	insert;
#}
#
## Called when an object nears its expiry time
#
#sub vcl_timeout {
#	discard;
#}


Revert c79f16a..5ff1ccc

Revert c79f16a..5ff1ccc

This rolls back to commit c79f16ac55170b1cc95a7e62259406f4f1aa6424.

# This is a basic VCL configuration file for varnish.  See the vcl(7)
# man page for details on VCL syntax and semantics.

import std;

# static
backend default {
  .host = "127.0.0.1";
  .port = "4000";
}
backend examples {
  .host = "127.0.0.1";
  .port = "8080";
}
backend components {
  .host = "127.0.0.1";
  .port = "3330";
}

sub vcl_recv {
  if (!req.http.Host) {
    error 404 "Need a host header";
  }

  set req.http.Host = regsub(req.http.Host, "^www\.", "");
  set req.http.Host = regsub(req.http.Host, ":80$", "");

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

  if (
    req.http.Host ~ "^chat\." ||
    req.http.Host ~ "^charts\." ||
    req.http.Host ~ "^directory\." ||
    req.http.Host ~ "^codemirror\." ||
    req.http.Host ~ "^hello\." ||
    req.http.Host ~ "^sink\." ||
    req.http.Host ~ "^todos\." ||
    req.http.Host ~ "^widgets\."
  ) {
    set req.backend = examples;
  } else if (req.http.Host ~ "^components\.") {
    set req.backend = components;
  }

  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.http.Upgrade ~ "(?i)websocket") {
    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_hash {
  hash_data(req.url);
  if (req.http.host) {
    hash_data(req.http.host);
  } else {
    hash_data(server.ip);
  }
  if (req.http.Origin) {
    hash_data(req.http.Origin);
  }
  return (hash);
}

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.

  # Websockets
  if (req.http.upgrade) {
    set bereq.http.upgrade = req.http.upgrade;
  }

  return (pipe);
}

sub vcl_fetch {
  # Compress responses
  if (beresp.http.content-type ~ "text"
      || beresp.http.content-type ~ "json"
      || beresp.http.content-type ~ "javascript") {
    set beresp.do_gzip = true;
  }
}

sub vcl_error {
  if (obj.status == 750) {
    # moved permanently
    set obj.http.Location = req.http.Location;
    set obj.status = 301;
  } else if (obj.status == 752) {
    # moved temporarily
    set obj.http.Location = req.http.Location;
    set obj.status = 302;
  } else {
    set obj.http.Content-Type = "text/html; charset=utf-8";
    set obj.http.Retry-After = "5";
    synthetic std.fileread("/etc/varnish/503.html");
  }
  return (deliver);
}

# This is a basic VCL configuration file for varnish.  See the vcl(7)
# man page for details on VCL syntax and semantics.

import std;

# static
backend default {
  .host = "127.0.0.1";
  .port = "4000";
}
backend examples {
  .host = "127.0.0.1";
  .port = "8080";
}
backend components {
  .host = "127.0.0.1";
  .port = "3330";
}

sub vcl_recv {
  if (!req.http.Host) {
    error 404 "Need a host header";
  }

  set req.http.Host = regsub(req.http.Host, "^www\.", "");
  set req.http.Host = regsub(req.http.Host, ":80$", "");

  if (
    req.http.Host ~ "^chat\." ||
    req.http.Host ~ "^charts\." ||
    req.http.Host ~ "^directory\." ||
    req.http.Host ~ "^codemirror\." ||
    req.http.Host ~ "^hello\." ||
    req.http.Host ~ "^sink\." ||
    req.http.Host ~ "^todos\." ||
    req.http.Host ~ "^widgets\."
  ) {
    set req.backend = examples;
  } else if (req.http.Host ~ "^components\.") {
    set req.backend = components;
  }

  if (req.http.upgrade ~ "(?i)websocket" || req.url ~ "^/channel") {
    return (pipe);
  }

  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 {
  # Websockets
  if (req.http.upgrade) {
    set bereq.http.upgrade = req.http.upgrade;
  }
  return (pipe);
}


Remove Set-Cookie on all cached objects, not just text

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

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

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

    # Varnish sets default TTL if none of these are present
    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 (beresp.http.Content-Type ~ "^text/(html|xml)") {
            # marker for vcl_deliver to reset Age:
            set beresp.http.magentomarker = "1";

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

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

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

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

    # Varnish sets default TTL if none of these are present
    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 ) {
        if (beresp.http.Content-Type ~ "^text/(html|xml)") {
            # marker for vcl_deliver to reset Age:
            set beresp.http.magentomarker = "1";

            # Don't cache cookies - this is here because Magento sets cookies even for anonymous users
            # which busts cache
            unset beresp.http.set-cookie;

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


Fix varnish invalidators config

Fix varnish invalidators config

vcl 4.0;

import std;

backend default {
  .host = "api";
  .port = "80";
  # Health check
  #.probe = {
  #  .url = "/";
  #  .timeout = 5s;
  #  .interval = 10s;
  #  .window = 5;
  #  .threshold = 3;
  #}
}

# Hosts allowed to send BAN requests
acl invalidators {
  "localhost";
  "php";
  # local Kubernetes network
  "10.0.0.0"/8;
  "172.16.0.0"/12;
  "192.168.0.0"/16;
}

sub vcl_backend_response {
  # Ban lurker friendly header
  set beresp.http.url = bereq.url;

  # Add a grace in case the backend is down
  set beresp.grace = 1h;
}

sub vcl_deliver {
  # Don't send cache tags related headers to the client
  unset resp.http.url;
  # Uncomment the following line to NOT send the "Cache-Tags" header to the client (prevent using CloudFlare cache tags)
  #unset resp.http.Cache-Tags;
}

sub vcl_recv {
  # Remove the "Forwarded" HTTP header if exists (security)
  unset req.http.forwarded;

  # To allow API Platform to ban by cache tags
  if (req.method == "BAN") {
    if (client.ip !~ invalidators) {
      return(synth(405, "Not allowed"));
    }

    if (req.http.ApiPlatform-Ban-Regex) {
      ban("obj.http.Cache-Tags ~ " + req.http.ApiPlatform-Ban-Regex);

      return(synth(200, "Ban added"));
    }

    return(synth(400, "ApiPlatform-Ban-Regex HTTP header must be set."));
  }
}

sub vcl_hit {
  if (obj.ttl >= 0s) {
    # A pure unadulterated hit, deliver it
    return (deliver);
  }
  if (std.healthy(req.backend_hint)) {
    # The backend is healthy
    # Fetch the object from the backend
    return (miss);
  }
  # No fresh object and the backend is not healthy
  if (obj.ttl + obj.grace > 0s) {
    # Deliver graced object
    # Automatically triggers a background fetch
    return (deliver);
  }
  # No valid object to deliver
  # No healthy backend to handle request
  # Return error
  return (synth(503, "API is down"));
}

vcl 4.0;

import std;

backend default {
  .host = "api";
  .port = "80";
  # Health check
  #.probe = {
  #  .url = "/";
  #  .timeout = 5s;
  #  .interval = 10s;
  #  .window = 5;
  #  .threshold = 3;
  #}
}

# Hosts allowed to send BAN requests
acl invalidators {
  "localhost";
  "php";
}

sub vcl_backend_response {
  # Ban lurker friendly header
  set beresp.http.url = bereq.url;

  # Add a grace in case the backend is down
  set beresp.grace = 1h;
}

sub vcl_deliver {
  # Don't send cache tags related headers to the client
  unset resp.http.url;
  # Uncomment the following line to NOT send the "Cache-Tags" header to the client (prevent using CloudFlare cache tags)
  #unset resp.http.Cache-Tags;
}

sub vcl_recv {
  # Remove the "Forwarded" HTTP header if exists (security)
  unset req.http.forwarded;

  # To allow API Platform to ban by cache tags
  if (req.method == "BAN") {
    if (client.ip !~ invalidators) {
      return(synth(405, "Not allowed"));
    }

    if (req.http.ApiPlatform-Ban-Regex) {
      ban("obj.http.Cache-Tags ~ " + req.http.ApiPlatform-Ban-Regex);

      return(synth(200, "Ban added"));
    }

    return(synth(400, "ApiPlatform-Ban-Regex HTTP header must be set."));
  }
}

sub vcl_hit {
  if (obj.ttl >= 0s) {
    # A pure unadulterated hit, deliver it
    return (deliver);
  }
  if (std.healthy(req.backend_hint)) {
    # The backend is healthy
    # Fetch the object from the backend
    return (miss);
  }
  # No fresh object and the backend is not healthy
  if (obj.ttl + obj.grace > 0s) {
    # Deliver graced object
    # Automatically triggers a background fetch
    return (deliver);
  }
  # No valid object to deliver
  # No healthy backend to handle request
  # Return error
  return (synth(503, "API is down"));
}


cleanup + fix regex so it workish. fails miserably if the clients send an empty override cookie, but enough is enough

#
# detectdevice.vcl - regex based device detection for Varnish
# http://github.com/lkarsten/varnish-devicedetect/
#
# Author: Lasse Karstensen <lasse@varnish-software.com>

sub devicedetect {
    unset req.http.X-hash-input;
    unset req.http.X-UA-Device;
    set req.http.X-UA-Device = "pc";

    if    (req.http.User-Agent ~ "(?i)ip(hone|od)") { set req.http.X-UA-Device = "mobile-iphone"; }
    elsif (req.http.User-Agent ~ "(?i)ipad")        { set req.http.X-UA-Device = "tablet-ipad"; }
    # how do we differ between an android phone and an android tablet?
    # http://stackoverflow.com/questions/5341637/how-do-detect-android-tablets-in-general-useragent
    elsif (req.http.User-Agent ~ "(?i)android.*(mobile|mini)") { set req.http.X-UA-Device = "mobile-android"; }
    # may very well give false positives towards android tablets. Suggestions welcome.
    elsif (req.http.User-Agent ~ "(?i)android")         { set req.http.X-UA-Device = "tablet-android"; }

    elsif (req.http.User-Agent ~ "^HTC" ||
        req.http.User-Agent ~ "Fennec" ||
        req.http.User-Agent ~ "IEMobile" ||
        req.http.User-Agent ~ "BlackBerry" ||
        req.http.User-Agent ~ "SymbianOS.*AppleWebKit" ||
        req.http.User-Agent ~ "Opera Mobi") {
        set req.http.X-VG-Device = "mobile-smartphone";
    }
    elsif (req.http.User-Agent ~ "(?i)symbian" ||
        req.http.User-Agent ~ "(?i)^sonyericsson" ||
        req.http.User-Agent ~ "(?i)^nokia" ||
        req.http.User-Agent ~ "(?i)^samsung" ||
        req.http.User-Agent ~ "(?i)^lg" ||
	req.http.User-Agent ~ "(?i)bada" ||
	req.http.User-Agent ~ "(?i)blazer" ||
	req.http.User-Agent ~ "(?i)cellphone" ||
	req.http.User-Agent ~ "(?i)iemobile" ||
	req.http.User-Agent ~ "(?i)midp-2.0" ||
	req.http.User-Agent ~ "(?i)u990" ||
	req.http.User-Agent ~ "(?i)netfront" ||
	req.http.User-Agent ~ "(?i)opera mini" ||
	req.http.User-Agent ~ "(?i)palm" ||
	req.http.User-Agent ~ "(?i)nintendo wii" ||
	req.http.User-Agent ~ "(?i)playstation portable" ||
	req.http.User-Agent ~ "(?i)portalmmm" ||
	req.http.User-Agent ~ "(?i)proxinet" ||
	req.http.User-Agent ~ "(?i)sonyericsson" ||
	req.http.User-Agent ~ "(?i)symbian" ||
	req.http.User-Agent ~ "(?i)windows\ ?ce" ||
	req.http.User-Agent ~ "(?i)winwap" ||
	req.http.User-Agent ~ "(?i)eudoraweb" ||
	req.http.User-Agent ~ "(?i)htc" ||
	req.http.User-Agent ~ "(?i)240x320" ||
	req.http.User-Agent ~ "(?i)avantgo") {
        set req.http.X-UA-Device = "mobile-generic";
    }

    # handle overrides
    if (req.http.Cookie ~ "(i?)X-UA-Device-force") {
        # ;?? means zero or one ;, non-greedy to match the first.
        set req.http.X-UA-Device = regsub(req.http.Cookie, "(?i).*X-UA-Device-force=([^;]+);??.*", "\1");
    }
    set req.http.X-hash-input = req.http.X-UA-Device;
}

# vim: sw=4:tw=120 # meh

#
# detectdevice.vcl - regex based device detection for Varnish
# http://github.com/lkarsten/varnish-devicedetect/
#
# Author: Lasse Karstensen <lasse@varnish-software.com>

sub devicedetect {
    unset req.http.X-hash-input;
    set req.http.X-UA-Device = "pc";

    if    (req.http.User-Agent ~ "(?i)ip(hone|od)") { set req.http.X-UA-Device = "mobile-iphone"; }
    elsif (req.http.User-Agent ~ "(?i)ipad")        { set req.http.X-UA-Device = "tablet-ipad"; }
    # how do we differ between an android phone and an android tablet?
    # http://stackoverflow.com/questions/5341637/how-do-detect-android-tablets-in-general-useragent
    elsif (req.http.User-Agent ~ "(?i)android.*(mobile|mini)") { set req.http.X-UA-Device = "mobile-android"; }
    # may very well give false positives towards android tablets. Suggestions welcome.
    elsif (req.http.User-Agent ~ "(?i)android")         { set req.http.X-UA-Device = "tablet-android"; }

    elsif (req.http.User-Agent ~ "^HTC" ||
        req.http.User-Agent ~ "Fennec" ||
        req.http.User-Agent ~ "IEMobile" ||
        req.http.User-Agent ~ "BlackBerry" ||
        req.http.User-Agent ~ "SymbianOS.*AppleWebKit" ||
        req.http.User-Agent ~ "Opera Mobi") {
        set req.http.X-VG-Device = "mobile-smartphone";
    }
    elsif (req.http.User-Agent ~ "(?i)symbian" ||
        req.http.User-Agent ~ "(?i)^sonyericsson" ||
        req.http.User-Agent ~ "(?i)^nokia" ||
        req.http.User-Agent ~ "(?i)^samsung" ||
        req.http.User-Agent ~ "(?i)^lg" ||
	req.http.User-Agent ~ "(?i)bada" ||
	req.http.User-Agent ~ "(?i)blazer" ||
	req.http.User-Agent ~ "(?i)cellphone" ||
	req.http.User-Agent ~ "(?i)iemobile" ||
	req.http.User-Agent ~ "(?i)midp-2.0" ||
	req.http.User-Agent ~ "(?i)u990" ||
	req.http.User-Agent ~ "(?i)netfront" ||
	req.http.User-Agent ~ "(?i)opera mini" ||
	req.http.User-Agent ~ "(?i)palm" ||
	req.http.User-Agent ~ "(?i)nintendo wii" ||
	req.http.User-Agent ~ "(?i)playstation portable" ||
	req.http.User-Agent ~ "(?i)portalmmm" ||
	req.http.User-Agent ~ "(?i)proxinet" ||
	req.http.User-Agent ~ "(?i)sonyericsson" ||
	req.http.User-Agent ~ "(?i)symbian" ||
	req.http.User-Agent ~ "(?i)windows\ ?ce" ||
	req.http.User-Agent ~ "(?i)winwap" ||
	req.http.User-Agent ~ "(?i)eudoraweb" ||
	req.http.User-Agent ~ "(?i)htc" ||
	req.http.User-Agent ~ "(?i)240x320" ||
	req.http.User-Agent ~ "(?i)avantgo") {
        set req.http.X-UA-Device = "mobile-generic";
    }

    # handle overrides
    if (req.http.Cookie ~ "X-force-UA") {
        set req.http.X-UA-Device = regsub(req.http.Cookie, "X-force-UA=(.+);", "\1");
    }
    #(req.http.Cookie ~ "X-force-UA=mobile-generic") set req.http.X-UA-Device = "mobile-generic";
    #if (req.url ~ "force-ua-device=mobile-android$") { set req.http.X-UA-Device = "mobile-android"; }
    #elsif (req.url ~ "force-ua-device=mobile-ipad$") { set req.http.X-UA-Device = "mobile-ipad"; }
    #elsif (req.url ~ "force-ua-device=mobile-iphone$") { set req.http.X-UA-Device = "mobile-iphone"; }
    #elsif (req.url ~ "force-ua-device=mobile-generic$") { set req.http.X-UA-Device = "mobile-generic"; }
    #elsif (req.url ~ "force-ua-device=pc-forced$") { set req.http.X-UA-Device = "pc-forced"; }

    # XXX: why?
    #if (req.http.X-UA-Device != "pc" && req.http.X-UA-Device != "pc-forced") {
    set req.http.X-hash-input = req.http.X-UA-Device;
    #}
}

# vim: sw=4:tw=120 # meh


vanish cache user esi based on cookie

vanish cache user esi based on cookie

vcl 4.0;

# Disable esi xml check on varnish Startup
# Example:
#   DAEMON_OPTS="-a :6081 \
#     -T localhost:6082 \
#     -p feature=+esi_disable_xml_check
#     -p feature=+esi_ignore_https
#

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

import std;

sub vcl_recv {
        if (req.method != "GET")
        {
                return (pass);
        }
        if (req.http.Cookie ~ "\bvarnish-force=pass")
        {
                return (pass);
        }

        if (req.url ~ "/esi/header")
        {
                set req.http.Cookie = ";" + req.http.Cookie;
                set req.http.Cookie = regsuball(req.http.Cookie, "; +", ";");
                set req.http.Cookie = regsuball(req.http.Cookie, ";(PHPSESSID)=", "; \1=");
                set req.http.Cookie = regsuball(req.http.Cookie, ";[^ ][^;]*", "");
                set req.http.Cookie = regsuball(req.http.Cookie, "^[; ]+|[; ]+$", "");
                if (req.http.Cookie == "")
                {
                        unset req.http.Cookie;
                }
        }

        return (hash);
}

sub vcl_hash {
        hash_data(req.http.host);
        hash_data(req.url);
        if (req.url ~ "/esi/header/")
        {
                hash_data(req.http.Cookie);
        }
        return(lookup);
}

sub vcl_backend_response {
        if (bereq.url ~ "/esi/header/")
        {
                unset beresp.http.Pragma;
                unset beresp.http.Cache-control;
                set beresp.ttl = 30m;
        }
        else if (beresp.http.Cache-Control != "public")
        {
                set beresp.uncacheable = true;
                set beresp.ttl = 3m;
                return (deliver);
        }

        set beresp.do_esi = true;
        set beresp.ttl = std.duration(beresp.http.Varnish-TTL, 15m);

        unset beresp.http.Set-Cookie;
}

vcl 4.0;

# Disable esi xml check on varnish Startup
# Example:
#   DAEMON_OPTS="-a :6081 \
#     -T localhost:6082 \
#     -p feature=+esi_disable_xml_check
#     -p feature=+esi_ignore_https
#

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

import std;

sub vcl_recv {
        if (req.method != "GET") {
                return (pass);
        }
        if (req.http.Cookie ~ "\bvarnish-force=pass") {
                return (pass);
        }

        return (hash);
}

sub vcl_hash {
        hash_data(req.http.host);
        hash_data(req.url);
        return(lookup);
}

sub vcl_backend_response {
        if (beresp.http.Cache-Control != "public") {
                set beresp.uncacheable = true;
                set beresp.ttl = 3m;
                return (deliver);
        }

        set beresp.do_esi = true;
        set beresp.ttl = std.duration(beresp.http.Varnish-TTL, 15m);

        unset beresp.http.Set-Cookie;
}


Exclude /rest and /oauth from basic auth

  # Check Basic auth against a table. /admin URLs are not basic auth protected to avoid the possibility of people
  # locking themselves out. /oauth and /rest have their own auth so we can skip Basic Auth on them as well
  if ( table.lookup(magentomodule_basic_auth, regsub(req.http.Authorization, "^Basic ", ""), "NOTFOUND") == "NOTFOUND" &&
      !req.url ~ "^/(index\.php/)?admin(_.*)?/" &&
      !req.url ~ "^/(index\.php/)?(rest|oauth)/" &&
      !req.url ~ "^/pub/static/" ) {
      error 971;
  }

  # Check Basic auth against a table. /admin URLs are not basic auth protected to avoid the possibility of people
  # locking themselves out
  if ( table.lookup(magentomodule_basic_auth, regsub(req.http.Authorization, "^Basic ", ""), "NOTFOUND") == "NOTFOUND" &&
      !req.url ~ "^/(index\.php/)?admin(_.*)?/" &&
      !req.url ~ "^/pub/static/" ) {
      error 971;
  }


fix redirects

fix redirects

sub vcl_recv {
	if (req.http.Host ~ "polyfills.io") {
		# Fastly recommend using the 900-999 range of status codes for custom errors within an application's VCL.
		# Do the canonicalise check before the HTTPS check to avoid a double redirect
		error 901 "Canonicalise";
	}
	if (!req.http.Fastly-SSL) {
		# 801 is a special error code that Fastly uses to Force HTTPS on the request
		error 801 "Redirect to HTTPS";
	}
	if (req.http.Orig-URL ~ "^/v1") {
		error 902 "Redirect to V2";
	}

	if (req.url.path == "/" || req.url.path == "/v2" || req.url.path == "/v2/docs/") {
		error 908;
	}
}

sub vcl_error {
  # Redirect to canonical prod/qa origins
	if (obj.status == 901) {
		set obj.status = 301;
		set obj.response = "Moved Permanently";
		set obj.http.Location = "https://polyfill.io" req.url;
		synthetic {""};
		return (deliver);
	}
	# Redirect to v2
	if (obj.status == 902) {
		set obj.status = 301;
		set obj.response = "Moved Permanently";
		# Remove libVersion and gated query parameters if they exist.
		set req.url = querystring.regfilter(req.url, "^\b(libVersion|gated)\b.*");
		set obj.http.Location = if(req.url.path == "/v1", "/v3/", regsub(req.url, "^/v1", "/v2"));
		set obj.http.Deprecation-Notice = "API version 1 has been decommissioned - see the body of this response for more information.";
		synthetic {"API version 1 has been decommissioned. Your request is being redirected to v2.  The `libVersion` and `gated` query string parameters are no longer supported and if present have been removed from your request.\n\nA deprecation period for v1 existed between August and December 2015, during which time v1 requests were honoured but a deprecation warning was added to output."};
		return (deliver);
	}

	# Redirect to v3
	if (obj.status == 908) {
		set obj.status = 301;
		set obj.http.Location = "/v3/";
		return (deliver);
	}
}

sub vcl_recv {
	if (req.http.Host ~ "polyfills.io") {
		# Fastly recommend using the 900-999 range of status codes for custom errors within an application's VCL.
		# Do the canonicalise check before the HTTPS check to avoid a double redirect
		error 901 "Canonicalise";
	}
	if (!req.http.Fastly-SSL) {
		# 801 is a special error code that Fastly uses to Force HTTPS on the request
		error 801 "Redirect to HTTPS";
	}
	if (req.http.Orig-URL ~ "^/v1") {
		error 902 "Redirect to V2";
	}

	if (req.url.path == "/" || req.url.path == "/v2") {
		error 908;
	}
}

sub vcl_error {
  # Redirect to canonical prod/qa origins
	if (obj.status == 901) {
		set obj.status = 301;
		set obj.response = "Moved Permanently";
		set obj.http.Location = "https://polyfill.io" req.url;
		synthetic {""};
		return (deliver);
	}
	# Redirect to v2
	if (obj.status == 902) {
		set obj.status = 301;
		set obj.response = "Moved Permanently";
		# Remove libVersion and gated query parameters if they exist.
		set req.url = querystring.regfilter(req.url, "^\b(libVersion|gated)\b.*");
		set obj.http.Location = if(req.url.path == "/v1", "/v3/", regsub(req.url, "^/v1", "/v2"));
		set obj.http.Deprecation-Notice = "API version 1 has been decommissioned - see the body of this response for more information.";
		synthetic {"API version 1 has been decommissioned. Your request is being redirected to v2.  The `libVersion` and `gated` query string parameters are no longer supported and if present have been removed from your request.\n\nA deprecation period for v1 existed between August and December 2015, during which time v1 requests were honoured but a deprecation warning was added to output."};
		return (deliver);
	}

	# Redirect to v3
	if (obj.status == 908) {
		set obj.status = 301;
		set obj.http.Location = "/v3/";
		return (deliver);
	}
}


Remove message/compose blacklist.

Remove message/compose blacklist.

backend mweb {
    .host = "mweb";
    .port = "4444";
}
/* backend www {
    .host = "www.reddit.com";
    .port = "80";
} */

sub vcl_recv {
  call device_detect;

  # Check mweb override cookie.
  if (req.http.Cookie ~ "(^|;\s*)(mweb-no-redirect=1)(;|$)") {
    call set_www_backend;
  } elsif (
    (req.http.X-UA-Device ~ "^mobile-" || req.http.X-UA-Device ~ "^tablet-")
    # We don't host endpoints with extensions in mweb.
    # The Fastly equivalent is: && !req.url.ext
    && !reg.url ~ "\/.+\.[a-z]{3,4}(\?.*|$)"
    && (
      # Whitelisted 2X endpoints from router/index.js
      req.url == "/"
      || req.url ~ "^/actions/"
      || req.url ~ "^/comments/?"
      || req.url ~ "^/login/?$"
      || req.url ~ "^/message/"
      || req.url ~ "^/r/"
      || req.url ~ "^/register/?$"
      || req.url ~ "^/search/?$"
      || req.url ~ "^/submit/?$"
      || req.url ~ "^/submit_to_community/?$"
      || req.url ~ "^/u/"
      || req.url ~ "^/user/"
      || req.url ~ "^/(help|w|wiki)/?"
      # Whitelisted 2X Ajax endpoints
      || req.url ~ "^/csp-report$"
      || req.url == "/error"
      || req.url ~ "^/(login|refresh|register)proxy$"
      || req.url ~ "^/logout"
      || req.url == "/routes"
      || req.url ~ "^/timings$"
      || req.url ~ "^/(u/)?XXX\?.*"
      # Whitelisted dev endpoints (these will be hosted on www in production)
      || req.url == "/apple-app-site-association"
      || req.url ~ "^/favicon/"
      || req.url ~ "^/favicon\.ico"
      || req.url ~ "^/fonts/"
      || req.url == "/health"
      || req.url ~ "^/img/"
      || req.url ~ "^/ProductionClient\..*\.(css|js)$"
      || req.url == "/robots.txt"
  )) {
    # If it's a mobile device and a whitelisted endpoint, use mweb.
    set req.backend = mweb;
  } else {
    call set_www_backend;
  }
}

sub set_www_backend {
  # NOTE(wting|2016-09-26): Normally we would redirect the user to a different
  # backend, but instead we're using a 400 status code for easy programmatic testing.
  error 400;
  # set req.backend = www;
}

# Vendorized from https://github.com/varnish/varnish-devicedetect/blob/master/devicedetect.vcl
# BSD-2 License
sub device_detect {
  unset req.http.X-UA-Device;
  set req.http.X-UA-Device = "pc";

  # Handle that a cookie may override the detection alltogether.
  if (req.http.Cookie ~ "(?i)X-UA-Device-force") {
    /* ;?? means zero or one ;, non-greedy to match the first. */
    set req.http.X-UA-Device = regsub(req.http.Cookie, "(?i).*X-UA-Device-force=([^;]+);??.*", "\1");
    /* Clean up our mess in the cookie header */
    set req.http.Cookie = regsuball(req.http.Cookie, "(^|; ) *X-UA-Device-force=[^;]+;? *", "\1");
    /* If the cookie header is now empty, or just whitespace, unset it. */
    if (req.http.Cookie ~ "^ *$") {
      unset req.http.Cookie;
    }
  } else {
    if (req.http.User-Agent ~ "\(compatible; Googlebot-Mobile/2.1; \+http://www.google.com/bot.html\)"
        || (req.http.User-Agent ~ "(Android|iPhone)"
            && req.http.User-Agent ~ "\(compatible.?; Googlebot/2.1.?; \+http://www.google.com/bot.html")
        || (req.http.User-Agent ~ "(iPhone|Windows Phone)"
            && req.http.User-Agent ~ "\(compatible; bingbot/2.0; \+http://www.bing.com/bingbot.htm")
    ) {
      set req.http.X-UA-Device = "mobile-bot";
    } elsif (req.http.User-Agent ~ "(?i)(ads|google|bing|msn|yandex|baidu|ro|career|seznam|)bot"
             || req.http.User-Agent ~ "(?i)(baidu|jike|symantec)spider"
             || req.http.User-Agent ~ "(?i)scanner"
             || req.http.User-Agent ~ "(?i)(web)crawler"
    ) {
      set req.http.X-UA-Device = "bot";
    } elsif (req.http.User-Agent ~ "(?i)ipad") {
      set req.http.X-UA-Device = "tablet-ipad";
    } elsif (req.http.User-Agent ~ "(?i)ip(hone|od)") {
      set req.http.X-UA-Device = "mobile-iphone";
    } elsif (req.http.User-Agent ~ "(?i)android.*(mobile|mini)") {
      /* how do we differ between an android phone and an android tablet? */
      /* http://stackoverflow.com/questions/5341637/how-do-detect-android-tablets-in-general-useragent */
      set req.http.X-UA-Device = "mobile-android";
    }
    elsif (req.http.User-Agent ~ "(?i)android 3") {
      /* https://github.com/varnish/varnish-devicedetect/blob/master/devicedetect.vcl */
      set req.http.X-UA-Device = "tablet-android";
    } elsif (req.http.User-Agent ~ "Opera Mobi") {
      /* Opera Mobile */
      set req.http.X-UA-Device = "mobile-smartphone";
    }
    elsif (req.http.User-Agent ~ "(?i)android") {
      /* May very well give false positives towards android tablets. Suggestions welcome. */
      set req.http.X-UA-Device = "tablet-android";
    } elsif (req.http.User-Agent ~ "PlayBook; U; RIM Tablet") {
      set req.http.X-UA-Device = "tablet-rim";
    } elsif (req.http.User-Agent ~ "hp-tablet.*TouchPad") {
      set req.http.X-UA-Device = "tablet-hp";
    } elsif (req.http.User-Agent ~ "Kindle/3") {
      set req.http.X-UA-Device = "tablet-kindle";
    } elsif (req.http.User-Agent ~ "Touch.+Tablet PC" || req.http.User-Agent ~ "Windows NT [0-9.]+; ARM;" ) {
      set req.http.X-UA-Device = "tablet-microsoft";
    } elsif (req.http.User-Agent ~ "Mobile.+Firefox") {
      set req.http.X-UA-Device = "mobile-firefoxos";
    } elsif (req.http.User-Agent ~ "^HTC"
             || req.http.User-Agent ~ "Fennec"
             || req.http.User-Agent ~ "IEMobile"
             || req.http.User-Agent ~ "BlackBerry"
             || req.http.User-Agent ~ "BB10.*Mobile"
             || req.http.User-Agent ~ "GT-.*Build/GINGERBREAD"
             || req.http.User-Agent ~ "SymbianOS.*AppleWebKit") {
      set req.http.X-UA-Device = "mobile-smartphone";
    }
    elsif (req.http.User-Agent ~ "(?i)symbian"
           || req.http.User-Agent ~ "(?i)^sonyericsson"
           || req.http.User-Agent ~ "(?i)^nokia"
           || req.http.User-Agent ~ "(?i)^samsung"
           || req.http.User-Agent ~ "(?i)^lg"
           || req.http.User-Agent ~ "(?i)bada"
           || req.http.User-Agent ~ "(?i)blazer"
           || req.http.User-Agent ~ "(?i)cellphone"
           || req.http.User-Agent ~ "(?i)iemobile"
           || req.http.User-Agent ~ "(?i)midp-2.0"
           || req.http.User-Agent ~ "(?i)u990"
           || req.http.User-Agent ~ "(?i)netfront"
           || req.http.User-Agent ~ "(?i)opera mini"
           || req.http.User-Agent ~ "(?i)palm"
           || req.http.User-Agent ~ "(?i)nintendo wii"
           || req.http.User-Agent ~ "(?i)playstation portable"
           || req.http.User-Agent ~ "(?i)portalmmm"
           || req.http.User-Agent ~ "(?i)proxinet"
           || req.http.User-Agent ~ "(?i)sonyericsson"
           || req.http.User-Agent ~ "(?i)symbian"
           || req.http.User-Agent ~ "(?i)windows\ ?ce"
           || req.http.User-Agent ~ "(?i)winwap"
           || req.http.User-Agent ~ "(?i)eudoraweb"
           || req.http.User-Agent ~ "(?i)htc"
           || req.http.User-Agent ~ "(?i)240x320"
           || req.http.User-Agent ~ "(?i)avantgo") {
      set req.http.X-UA-Device = "mobile-generic";
    }
  }
}

backend mweb {
    .host = "mweb";
    .port = "4444";
}
/* backend www {
    .host = "www.reddit.com";
    .port = "80";
} */

sub vcl_recv {
  call device_detect;

  # Check mweb override cookie.
  if (req.http.Cookie ~ "(^|;\s*)(mweb-no-redirect=1)(;|$)") {
    call set_www_backend;
  } elsif (
    (req.http.X-UA-Device ~ "^mobile-" || req.http.X-UA-Device ~ "^tablet-")
    # We don't host endpoints with extensions in mweb.
    # The Fastly equivalent is: && !req.url.ext
    && !reg.url ~ "\/.+\.[a-z]{3,4}(\?.*|$)"
    && (
      # Blacklisted endpoints
      # This endpoint 200's for mweb, but the current implementation has issues
      req.url !~ "^/message/compose/?$"
    )
    && (
      # Whitelisted 2X endpoints from router/index.js
      req.url == "/"
      || req.url ~ "^/actions/"
      || req.url ~ "^/comments/?"
      || req.url ~ "^/login/?$"
      || req.url ~ "^/message/"
      || req.url ~ "^/r/"
      || req.url ~ "^/register/?$"
      || req.url ~ "^/search/?$"
      || req.url ~ "^/submit/?$"
      || req.url ~ "^/submit_to_community/?$"
      || req.url ~ "^/u/"
      || req.url ~ "^/user/"
      || req.url ~ "^/(help|w|wiki)/?"
      # Whitelisted 2X Ajax endpoints
      || req.url ~ "^/csp-report$"
      || req.url == "/error"
      || req.url ~ "^/(login|refresh|register)proxy$"
      || req.url ~ "^/logout"
      || req.url == "/routes"
      || req.url ~ "^/timings$"
      || req.url ~ "^/(u/)?XXX\?.*"
      # Whitelisted dev endpoints (these will be hosted on www in production)
      || req.url == "/apple-app-site-association"
      || req.url ~ "^/favicon/"
      || req.url ~ "^/favicon\.ico"
      || req.url ~ "^/fonts/"
      || req.url == "/health"
      || req.url ~ "^/img/"
      || req.url ~ "^/ProductionClient\..*\.(css|js)$"
      || req.url == "/robots.txt"
  )) {
    # If it's a mobile device and a whitelisted endpoint, use mweb.
    set req.backend = mweb;
  } else {
    call set_www_backend;
  }
}

sub set_www_backend {
  # NOTE(wting|2016-09-26): Normally we would redirect the user to a different
  # backend, but instead we're using a 400 status code for easy programmatic testing.
  error 400;
  # set req.backend = www;
}

# Vendorized from https://github.com/varnish/varnish-devicedetect/blob/master/devicedetect.vcl
# BSD-2 License
sub device_detect {
  unset req.http.X-UA-Device;
  set req.http.X-UA-Device = "pc";

  # Handle that a cookie may override the detection alltogether.
  if (req.http.Cookie ~ "(?i)X-UA-Device-force") {
    /* ;?? means zero or one ;, non-greedy to match the first. */
    set req.http.X-UA-Device = regsub(req.http.Cookie, "(?i).*X-UA-Device-force=([^;]+);??.*", "\1");
    /* Clean up our mess in the cookie header */
    set req.http.Cookie = regsuball(req.http.Cookie, "(^|; ) *X-UA-Device-force=[^;]+;? *", "\1");
    /* If the cookie header is now empty, or just whitespace, unset it. */
    if (req.http.Cookie ~ "^ *$") {
      unset req.http.Cookie;
    }
  } else {
    if (req.http.User-Agent ~ "\(compatible; Googlebot-Mobile/2.1; \+http://www.google.com/bot.html\)"
        || (req.http.User-Agent ~ "(Android|iPhone)"
            && req.http.User-Agent ~ "\(compatible.?; Googlebot/2.1.?; \+http://www.google.com/bot.html")
        || (req.http.User-Agent ~ "(iPhone|Windows Phone)"
            && req.http.User-Agent ~ "\(compatible; bingbot/2.0; \+http://www.bing.com/bingbot.htm")
    ) {
      set req.http.X-UA-Device = "mobile-bot";
    } elsif (req.http.User-Agent ~ "(?i)(ads|google|bing|msn|yandex|baidu|ro|career|seznam|)bot"
             || req.http.User-Agent ~ "(?i)(baidu|jike|symantec)spider"
             || req.http.User-Agent ~ "(?i)scanner"
             || req.http.User-Agent ~ "(?i)(web)crawler"
    ) {
      set req.http.X-UA-Device = "bot";
    } elsif (req.http.User-Agent ~ "(?i)ipad") {
      set req.http.X-UA-Device = "tablet-ipad";
    } elsif (req.http.User-Agent ~ "(?i)ip(hone|od)") {
      set req.http.X-UA-Device = "mobile-iphone";
    } elsif (req.http.User-Agent ~ "(?i)android.*(mobile|mini)") {
      /* how do we differ between an android phone and an android tablet? */
      /* http://stackoverflow.com/questions/5341637/how-do-detect-android-tablets-in-general-useragent */
      set req.http.X-UA-Device = "mobile-android";
    }
    elsif (req.http.User-Agent ~ "(?i)android 3") {
      /* https://github.com/varnish/varnish-devicedetect/blob/master/devicedetect.vcl */
      set req.http.X-UA-Device = "tablet-android";
    } elsif (req.http.User-Agent ~ "Opera Mobi") {
      /* Opera Mobile */
      set req.http.X-UA-Device = "mobile-smartphone";
    }
    elsif (req.http.User-Agent ~ "(?i)android") {
      /* May very well give false positives towards android tablets. Suggestions welcome. */
      set req.http.X-UA-Device = "tablet-android";
    } elsif (req.http.User-Agent ~ "PlayBook; U; RIM Tablet") {
      set req.http.X-UA-Device = "tablet-rim";
    } elsif (req.http.User-Agent ~ "hp-tablet.*TouchPad") {
      set req.http.X-UA-Device = "tablet-hp";
    } elsif (req.http.User-Agent ~ "Kindle/3") {
      set req.http.X-UA-Device = "tablet-kindle";
    } elsif (req.http.User-Agent ~ "Touch.+Tablet PC" || req.http.User-Agent ~ "Windows NT [0-9.]+; ARM;" ) {
      set req.http.X-UA-Device = "tablet-microsoft";
    } elsif (req.http.User-Agent ~ "Mobile.+Firefox") {
      set req.http.X-UA-Device = "mobile-firefoxos";
    } elsif (req.http.User-Agent ~ "^HTC"
             || req.http.User-Agent ~ "Fennec"
             || req.http.User-Agent ~ "IEMobile"
             || req.http.User-Agent ~ "BlackBerry"
             || req.http.User-Agent ~ "BB10.*Mobile"
             || req.http.User-Agent ~ "GT-.*Build/GINGERBREAD"
             || req.http.User-Agent ~ "SymbianOS.*AppleWebKit") {
      set req.http.X-UA-Device = "mobile-smartphone";
    }
    elsif (req.http.User-Agent ~ "(?i)symbian"
           || req.http.User-Agent ~ "(?i)^sonyericsson"
           || req.http.User-Agent ~ "(?i)^nokia"
           || req.http.User-Agent ~ "(?i)^samsung"
           || req.http.User-Agent ~ "(?i)^lg"
           || req.http.User-Agent ~ "(?i)bada"
           || req.http.User-Agent ~ "(?i)blazer"
           || req.http.User-Agent ~ "(?i)cellphone"
           || req.http.User-Agent ~ "(?i)iemobile"
           || req.http.User-Agent ~ "(?i)midp-2.0"
           || req.http.User-Agent ~ "(?i)u990"
           || req.http.User-Agent ~ "(?i)netfront"
           || req.http.User-Agent ~ "(?i)opera mini"
           || req.http.User-Agent ~ "(?i)palm"
           || req.http.User-Agent ~ "(?i)nintendo wii"
           || req.http.User-Agent ~ "(?i)playstation portable"
           || req.http.User-Agent ~ "(?i)portalmmm"
           || req.http.User-Agent ~ "(?i)proxinet"
           || req.http.User-Agent ~ "(?i)sonyericsson"
           || req.http.User-Agent ~ "(?i)symbian"
           || req.http.User-Agent ~ "(?i)windows\ ?ce"
           || req.http.User-Agent ~ "(?i)winwap"
           || req.http.User-Agent ~ "(?i)eudoraweb"
           || req.http.User-Agent ~ "(?i)htc"
           || req.http.User-Agent ~ "(?i)240x320"
           || req.http.User-Agent ~ "(?i)avantgo") {
      set req.http.X-UA-Device = "mobile-generic";
    }
  }
}


Fix VCL regexes to support parameters and/or subreddit searches.

backend mweb {
    .host = "mweb";
    .port = "4444";
}
/* backend www {
    .host = "www.reddit.com";
    .port = "80";
} */

sub vcl_recv {
  call device_detect;

  # Check mweb override cookie.
  if (req.http.Cookie ~ "(^|;\s*)(mweb-no-redirect=1)(;|$)") {
    call set_www_backend;
  } elsif (
    (req.http.X-UA-Device ~ "^mobile-" || req.http.X-UA-Device ~ "^tablet-")
    # We don't host endpoints with extensions in mweb.
    # The Fastly equivalent is: && !req.url.ext
    && !reg.url ~ "\/.+\.[a-z]{3,4}(\?.*|$)"
    && (
      # Blacklisted endpoints
      req.url !~ "\.json$"
    && (
      # Whitelisted 2X endpoints from router/index.js
      req.url == "/"
      || req.url ~ "^/actions/"
      || req.url ~ "^/comments/?"
      || req.url ~ "^/login/?$"
      || req.url ~ "^/message/"
      || req.url ~ "^/r/"
      || req.url ~ "^/register/?$"
      || req.url ~ "/search/?$"
      || req.url ~ "/submit/?$"
      || req.url ~ "^/submit/to_community/?$"
      || req.url ~ "^/u/"
      || req.url ~ "^/user/"
      || req.url ~ "^/(help|w|wiki)/?"
      # Whitelisted 2X Ajax endpoints
      || req.url ~ "^/csp-report$"
      || req.url == "/error"
      || req.url ~ "^/(login|refresh|register)proxy$"
      || req.url ~ "^/logout"
      || req.url == "/routes"
      || req.url ~ "^/timings"
      || req.url ~ "^/(u/)?XXX\?.*"
      # Whitelisted dev endpoints (these will be hosted on www in production)
      || req.url == "/apple-app-site-association"
      || req.url ~ "^/favicon/"
      || req.url ~ "^/favicon\.ico"
      || req.url ~ "^/fonts/"
      || req.url == "/health"
      || req.url ~ "^/img/"
      || req.url ~ "^/ProductionClient\..*\.(css|js)$"
      || req.url == "/robots.txt"
  )) {
    # If it's a mobile device and a whitelisted endpoint, use mweb.
    set req.backend = mweb;
  } else {
    call set_www_backend;
  }
}

sub set_www_backend {
  # NOTE(wting|2016-09-26): Normally we would redirect the user to a different
  # backend, but instead we're using a 400 status code for easy programmatic testing.
  error 400;
  # set req.backend = www;
}

# Vendorized from https://github.com/varnish/varnish-devicedetect/blob/master/devicedetect.vcl
# BSD-2 License
sub device_detect {
  unset req.http.X-UA-Device;
  set req.http.X-UA-Device = "pc";

  # Handle that a cookie may override the detection alltogether.
  if (req.http.Cookie ~ "(?i)X-UA-Device-force") {
    /* ;?? means zero or one ;, non-greedy to match the first. */
    set req.http.X-UA-Device = regsub(req.http.Cookie, "(?i).*X-UA-Device-force=([^;]+);??.*", "\1");
    /* Clean up our mess in the cookie header */
    set req.http.Cookie = regsuball(req.http.Cookie, "(^|; ) *X-UA-Device-force=[^;]+;? *", "\1");
    /* If the cookie header is now empty, or just whitespace, unset it. */
    if (req.http.Cookie ~ "^ *$") {
      unset req.http.Cookie;
    }
  } else {
    if (req.http.User-Agent ~ "\(compatible; Googlebot-Mobile/2.1; \+http://www.google.com/bot.html\)"
        || (req.http.User-Agent ~ "(Android|iPhone)"
            && req.http.User-Agent ~ "\(compatible.?; Googlebot/2.1.?; \+http://www.google.com/bot.html")
        || (req.http.User-Agent ~ "(iPhone|Windows Phone)"
            && req.http.User-Agent ~ "\(compatible; bingbot/2.0; \+http://www.bing.com/bingbot.htm")
    ) {
      set req.http.X-UA-Device = "mobile-bot";
    } elsif (req.http.User-Agent ~ "(?i)(ads|google|bing|msn|yandex|baidu|ro|career|seznam|)bot"
             || req.http.User-Agent ~ "(?i)(baidu|jike|symantec)spider"
             || req.http.User-Agent ~ "(?i)scanner"
             || req.http.User-Agent ~ "(?i)(web)crawler"
    ) {
      set req.http.X-UA-Device = "bot";
    } elsif (req.http.User-Agent ~ "(?i)ipad") {
      set req.http.X-UA-Device = "tablet-ipad";
    } elsif (req.http.User-Agent ~ "(?i)ip(hone|od)") {
      set req.http.X-UA-Device = "mobile-iphone";
    } elsif (req.http.User-Agent ~ "(?i)android.*(mobile|mini)") {
      /* how do we differ between an android phone and an android tablet? */
      /* http://stackoverflow.com/questions/5341637/how-do-detect-android-tablets-in-general-useragent */
      set req.http.X-UA-Device = "mobile-android";
    }
    elsif (req.http.User-Agent ~ "(?i)android 3") {
      /* https://github.com/varnish/varnish-devicedetect/blob/master/devicedetect.vcl */
      set req.http.X-UA-Device = "tablet-android";
    } elsif (req.http.User-Agent ~ "Opera Mobi") {
      /* Opera Mobile */
      set req.http.X-UA-Device = "mobile-smartphone";
    }
    elsif (req.http.User-Agent ~ "(?i)android") {
      /* May very well give false positives towards android tablets. Suggestions welcome. */
      set req.http.X-UA-Device = "tablet-android";
    } elsif (req.http.User-Agent ~ "PlayBook; U; RIM Tablet") {
      set req.http.X-UA-Device = "tablet-rim";
    } elsif (req.http.User-Agent ~ "hp-tablet.*TouchPad") {
      set req.http.X-UA-Device = "tablet-hp";
    } elsif (req.http.User-Agent ~ "Kindle/3") {
      set req.http.X-UA-Device = "tablet-kindle";
    } elsif (req.http.User-Agent ~ "Touch.+Tablet PC" || req.http.User-Agent ~ "Windows NT [0-9.]+; ARM;" ) {
      set req.http.X-UA-Device = "tablet-microsoft";
    } elsif (req.http.User-Agent ~ "Mobile.+Firefox") {
      set req.http.X-UA-Device = "mobile-firefoxos";
    } elsif (req.http.User-Agent ~ "^HTC"
             || req.http.User-Agent ~ "Fennec"
             || req.http.User-Agent ~ "IEMobile"
             || req.http.User-Agent ~ "BlackBerry"
             || req.http.User-Agent ~ "BB10.*Mobile"
             || req.http.User-Agent ~ "GT-.*Build/GINGERBREAD"
             || req.http.User-Agent ~ "SymbianOS.*AppleWebKit") {
      set req.http.X-UA-Device = "mobile-smartphone";
    }
    elsif (req.http.User-Agent ~ "(?i)symbian"
           || req.http.User-Agent ~ "(?i)^sonyericsson"
           || req.http.User-Agent ~ "(?i)^nokia"
           || req.http.User-Agent ~ "(?i)^samsung"
           || req.http.User-Agent ~ "(?i)^lg"
           || req.http.User-Agent ~ "(?i)bada"
           || req.http.User-Agent ~ "(?i)blazer"
           || req.http.User-Agent ~ "(?i)cellphone"
           || req.http.User-Agent ~ "(?i)iemobile"
           || req.http.User-Agent ~ "(?i)midp-2.0"
           || req.http.User-Agent ~ "(?i)u990"
           || req.http.User-Agent ~ "(?i)netfront"
           || req.http.User-Agent ~ "(?i)opera mini"
           || req.http.User-Agent ~ "(?i)palm"
           || req.http.User-Agent ~ "(?i)nintendo wii"
           || req.http.User-Agent ~ "(?i)playstation portable"
           || req.http.User-Agent ~ "(?i)portalmmm"
           || req.http.User-Agent ~ "(?i)proxinet"
           || req.http.User-Agent ~ "(?i)sonyericsson"
           || req.http.User-Agent ~ "(?i)symbian"
           || req.http.User-Agent ~ "(?i)windows\ ?ce"
           || req.http.User-Agent ~ "(?i)winwap"
           || req.http.User-Agent ~ "(?i)eudoraweb"
           || req.http.User-Agent ~ "(?i)htc"
           || req.http.User-Agent ~ "(?i)240x320"
           || req.http.User-Agent ~ "(?i)avantgo") {
      set req.http.X-UA-Device = "mobile-generic";
    }
  }
}

backend mweb {
    .host = "mweb";
    .port = "4444";
}
/* backend www {
    .host = "www.reddit.com";
    .port = "80";
} */

sub vcl_recv {
  call device_detect;

  # Check mweb override cookie.
  if (req.http.Cookie ~ "(^|;\s*)(mweb-no-redirect=1)(;|$)") {
    call set_www_backend;
  } elsif (
    (req.http.X-UA-Device ~ "^mobile-" || req.http.X-UA-Device ~ "^tablet-")
    # We don't host endpoints with extensions in mweb.
    # The Fastly equivalent is: && !req.url.ext
    && !reg.url ~ "\/.+\.[a-z]{3,4}(\?.*|$)"
    && (
      # Blacklisted endpoints
      req.url !~ "\.json$"
    && (
      # Whitelisted 2X endpoints from router/index.js
      req.url == "/"
      || req.url ~ "^/actions/"
      || req.url ~ "^/comments/?"
      || req.url ~ "^/login/?$"
      || req.url ~ "^/message/"
      || req.url ~ "^/r/"
      || req.url ~ "^/register/?$"
      || req.url ~ "^/search/?$"
      || req.url ~ "^/submit/?$"
      || req.url ~ "^/submit_to_community/?$"
      || req.url ~ "^/u/"
      || req.url ~ "^/user/"
      || req.url ~ "^/(help|w|wiki)/?"
      # Whitelisted 2X Ajax endpoints
      || req.url ~ "^/csp-report$"
      || req.url == "/error"
      || req.url ~ "^/(login|refresh|register)proxy$"
      || req.url ~ "^/logout"
      || req.url == "/routes"
      || req.url ~ "^/timings$"
      || req.url ~ "^/(u/)?XXX\?.*"
      # Whitelisted dev endpoints (these will be hosted on www in production)
      || req.url == "/apple-app-site-association"
      || req.url ~ "^/favicon/"
      || req.url ~ "^/favicon\.ico"
      || req.url ~ "^/fonts/"
      || req.url == "/health"
      || req.url ~ "^/img/"
      || req.url ~ "^/ProductionClient\..*\.(css|js)$"
      || req.url == "/robots.txt"
  )) {
    # If it's a mobile device and a whitelisted endpoint, use mweb.
    set req.backend = mweb;
  } else {
    call set_www_backend;
  }
}

sub set_www_backend {
  # NOTE(wting|2016-09-26): Normally we would redirect the user to a different
  # backend, but instead we're using a 400 status code for easy programmatic testing.
  error 400;
  # set req.backend = www;
}

# Vendorized from https://github.com/varnish/varnish-devicedetect/blob/master/devicedetect.vcl
# BSD-2 License
sub device_detect {
  unset req.http.X-UA-Device;
  set req.http.X-UA-Device = "pc";

  # Handle that a cookie may override the detection alltogether.
  if (req.http.Cookie ~ "(?i)X-UA-Device-force") {
    /* ;?? means zero or one ;, non-greedy to match the first. */
    set req.http.X-UA-Device = regsub(req.http.Cookie, "(?i).*X-UA-Device-force=([^;]+);??.*", "\1");
    /* Clean up our mess in the cookie header */
    set req.http.Cookie = regsuball(req.http.Cookie, "(^|; ) *X-UA-Device-force=[^;]+;? *", "\1");
    /* If the cookie header is now empty, or just whitespace, unset it. */
    if (req.http.Cookie ~ "^ *$") {
      unset req.http.Cookie;
    }
  } else {
    if (req.http.User-Agent ~ "\(compatible; Googlebot-Mobile/2.1; \+http://www.google.com/bot.html\)"
        || (req.http.User-Agent ~ "(Android|iPhone)"
            && req.http.User-Agent ~ "\(compatible.?; Googlebot/2.1.?; \+http://www.google.com/bot.html")
        || (req.http.User-Agent ~ "(iPhone|Windows Phone)"
            && req.http.User-Agent ~ "\(compatible; bingbot/2.0; \+http://www.bing.com/bingbot.htm")
    ) {
      set req.http.X-UA-Device = "mobile-bot";
    } elsif (req.http.User-Agent ~ "(?i)(ads|google|bing|msn|yandex|baidu|ro|career|seznam|)bot"
             || req.http.User-Agent ~ "(?i)(baidu|jike|symantec)spider"
             || req.http.User-Agent ~ "(?i)scanner"
             || req.http.User-Agent ~ "(?i)(web)crawler"
    ) {
      set req.http.X-UA-Device = "bot";
    } elsif (req.http.User-Agent ~ "(?i)ipad") {
      set req.http.X-UA-Device = "tablet-ipad";
    } elsif (req.http.User-Agent ~ "(?i)ip(hone|od)") {
      set req.http.X-UA-Device = "mobile-iphone";
    } elsif (req.http.User-Agent ~ "(?i)android.*(mobile|mini)") {
      /* how do we differ between an android phone and an android tablet? */
      /* http://stackoverflow.com/questions/5341637/how-do-detect-android-tablets-in-general-useragent */
      set req.http.X-UA-Device = "mobile-android";
    }
    elsif (req.http.User-Agent ~ "(?i)android 3") {
      /* https://github.com/varnish/varnish-devicedetect/blob/master/devicedetect.vcl */
      set req.http.X-UA-Device = "tablet-android";
    } elsif (req.http.User-Agent ~ "Opera Mobi") {
      /* Opera Mobile */
      set req.http.X-UA-Device = "mobile-smartphone";
    }
    elsif (req.http.User-Agent ~ "(?i)android") {
      /* May very well give false positives towards android tablets. Suggestions welcome. */
      set req.http.X-UA-Device = "tablet-android";
    } elsif (req.http.User-Agent ~ "PlayBook; U; RIM Tablet") {
      set req.http.X-UA-Device = "tablet-rim";
    } elsif (req.http.User-Agent ~ "hp-tablet.*TouchPad") {
      set req.http.X-UA-Device = "tablet-hp";
    } elsif (req.http.User-Agent ~ "Kindle/3") {
      set req.http.X-UA-Device = "tablet-kindle";
    } elsif (req.http.User-Agent ~ "Touch.+Tablet PC" || req.http.User-Agent ~ "Windows NT [0-9.]+; ARM;" ) {
      set req.http.X-UA-Device = "tablet-microsoft";
    } elsif (req.http.User-Agent ~ "Mobile.+Firefox") {
      set req.http.X-UA-Device = "mobile-firefoxos";
    } elsif (req.http.User-Agent ~ "^HTC"
             || req.http.User-Agent ~ "Fennec"
             || req.http.User-Agent ~ "IEMobile"
             || req.http.User-Agent ~ "BlackBerry"
             || req.http.User-Agent ~ "BB10.*Mobile"
             || req.http.User-Agent ~ "GT-.*Build/GINGERBREAD"
             || req.http.User-Agent ~ "SymbianOS.*AppleWebKit") {
      set req.http.X-UA-Device = "mobile-smartphone";
    }
    elsif (req.http.User-Agent ~ "(?i)symbian"
           || req.http.User-Agent ~ "(?i)^sonyericsson"
           || req.http.User-Agent ~ "(?i)^nokia"
           || req.http.User-Agent ~ "(?i)^samsung"
           || req.http.User-Agent ~ "(?i)^lg"
           || req.http.User-Agent ~ "(?i)bada"
           || req.http.User-Agent ~ "(?i)blazer"
           || req.http.User-Agent ~ "(?i)cellphone"
           || req.http.User-Agent ~ "(?i)iemobile"
           || req.http.User-Agent ~ "(?i)midp-2.0"
           || req.http.User-Agent ~ "(?i)u990"
           || req.http.User-Agent ~ "(?i)netfront"
           || req.http.User-Agent ~ "(?i)opera mini"
           || req.http.User-Agent ~ "(?i)palm"
           || req.http.User-Agent ~ "(?i)nintendo wii"
           || req.http.User-Agent ~ "(?i)playstation portable"
           || req.http.User-Agent ~ "(?i)portalmmm"
           || req.http.User-Agent ~ "(?i)proxinet"
           || req.http.User-Agent ~ "(?i)sonyericsson"
           || req.http.User-Agent ~ "(?i)symbian"
           || req.http.User-Agent ~ "(?i)windows\ ?ce"
           || req.http.User-Agent ~ "(?i)winwap"
           || req.http.User-Agent ~ "(?i)eudoraweb"
           || req.http.User-Agent ~ "(?i)htc"
           || req.http.User-Agent ~ "(?i)240x320"
           || req.http.User-Agent ~ "(?i)avantgo") {
      set req.http.X-UA-Device = "mobile-generic";
    }
  }
}