Use Cookie accessors to simplify VCL

Use Cookie accessors to simplify VCL

###############################################################################
#
# Fastly CDN for Magento
#
# NOTICE OF LICENSE
#
# This source file is subject to the Fastly CDN for Magento End User License
# Agreement that is bundled with this package in the file LICENSE_FASTLY_CDN.txt.
#
# @copyright   Copyright (c) 2015 Fastly, Inc. (http://www.fastly.com)
# @license     BSD, see LICENSE_FASTLY_CDN.txt
#
###############################################################################

# This is a basic VCL configuration file for fastly CDN for Magento module.

sub vcl_recv {
#FASTLY recv
    # we only deal with GET and HEAD by default
    if (req.request != "GET" && req.request != "HEAD"  && req.request != "FASTLYPURGE") {
        return (pass);
    }

    # auth for purging
    if (req.request == "FASTLYPURGE") {
        # extract token signature and expiration
        set req.http.X-Sig = regsub(req.http.X-Purge-Token, "^[^_]+_(.*)", "\1");
        set req.http.X-Exp = regsub(req.http.X-Purge-Token, "^([^_]+)_.*", "\1");

        # validate signature
        if (req.http.X-Sig == regsub(digest.hmac_sha1(req.service_id, req.url.path req.http.X-Exp), "^0x", "")) {

            # use vcl time math to check expiration timestamp
            set req.http.X-Original-Grace = req.grace;
            set req.grace = std.atoi(strftime({"%s"}, now));
            set req.grace -= std.atoi(req.http.X-Exp);

            if (std.atoi(req.grace) > 0) {
                error 410;
            }

            # clean up grace since we used it for time math
            set req.grace = std.atoi(req.http.X-Original-Grace);
            unset req.http.X-Original-Grace;

        } else {
            error 403;
        }

        # cleanup variables
        unset req.http.X-Purge-Token;
        unset req.http.X-Sig;
        unset req.http.X-Exp;
    }

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

    # disable ESI processing on Origin Shield
    if (req.http.Fastly-FF) {
        set req.esi = false;
    }

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

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

    # geoip lookup
    if (req.url ~ "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 geoip.country_code;
        }
    }

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

    # check for ESI calls
     if (req.url ~ "esi_data=") {
         # check for valid cookie data
         if (req.http.Cookie ~ "FASTLY_CDN-") {
             # get the cookie name to look for
             set req.http.fastlyCDNEsiCookieName = regsub(
                 req.url,
                 "(.*)esi_data=([^&]*)(.*)",
                 "\2"
             );

             # get the cookie value for the cookie name
             # tmp string is NAMESPACE-COOKIENAME@@@@@COMPLETE_COOKIE
             # left of @@@@@ is back-referenced in regex to extract value from cookie
             set req.http.fastlyTmp = "FASTLY_CDN-" req.http.fastlyCDNEsiCookieName "@@@@@" req.http.Cookie;
             set req.http.fastlyCDNRequest = regsub(
                 req.http.fastlyTmp,
                 "^([A-Za-z0-9-_]+)@@@@@.*\1=([^;]*)",
                 "\2"
             );

             # do we have a value for the cookie name?
             if (!req.http.fastlyCDNRequest) {
                 set req.http.fastlyCDNRequest = "default";
             }

             # build backend url
             set req.url = regsub(
                   req.url,
                   "(.*)esi_data=([^&]*)[&]?(.*)",
                   "\1\3"
                 )
                 "&esi_data="
                 req.http.fastlyCDNRequest;

             # clean up temp variables
             remove req.http.fastlyCDNEsiCookieName;
             remove req.http.fastlyCDNRequest;

             return (lookup);
         }
     }

    return(lookup);
}

sub vcl_pass {
#FASTLY pass
}

sub vcl_hash {
    set req.hash += req.http.Https;
    set req.hash += req.http.host;
    set req.hash += req.url;

    if (req.http.cookie:FASTLY_CDN_ENV) {
        set req.hash += req.http.cookie:FASTLY_CDN_ENV;
    }

    set req.hash += "#####GENERATION#####";

    if (!(req.url ~ "^/(media|js|skin)/.*\.(png|jpg|jpeg|gif|css|js|swf|ico)$")) {
        call design_exception;
    }
    return (hash);
}

sub vcl_hit {
#FASTLY hit

    if (!obj.cacheable) {
        return(pass);
    }
    return(deliver);
}

sub vcl_miss {
    # Deactivate gzip on origin
    unset bereq.http.Accept-Encoding;

#FASTLY miss

    return(fetch);
}

sub vcl_fetch {
#FASTLY fetch

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

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

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

    if (beresp.http.Content-Type ~ "text/html" || beresp.http.Content-Type ~ "text/xml") {
        # enable ESI feature for Magento response by default
        esi;
    } else {
        # enable gzip for all static content
        if ((beresp.status == 200 || beresp.status == 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 ~ "\.(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.http.Cache-Control ~ "private") {
        set req.http.Fastly-Cachetype = "PRIVATE";
        return (pass);
    }

    if (beresp.status == 500 || beresp.status == 503) {
        set req.http.Fastly-Cachetype = "ERROR";
        set beresp.ttl = 1s;
        set beresp.grace = 5s;
        return (deliver);
    }

    if (beresp.status == 200 || beresp.status == 301 || beresp.status == 404) {
        if (beresp.http.Content-Type ~ "text/html" || beresp.http.Content-Type ~ "text/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";
        }

        set beresp.http.X-Surrogate-Key = beresp.http.Surrogate-Key;
    }

    return (deliver);
}

sub vcl_deliver {
    # debug info
    if (resp.http.X-Cache-Debug) {
        if (obj.hits > 0) {
            set resp.http.X-Cache      = "HIT";
            set resp.http.X-Cache-Hits = obj.hits;
        } else {
            set resp.http.X-Cache      = "MISS";
        }
        set resp.http.X-Cache-Expires  = resp.http.Expires;
    } else {
        # remove Varnish/proxy header
        remove resp.http.X-Varnish;
        remove resp.http.Via;
        remove resp.http.Age;
        remove resp.http.X-Purge-URL;
        remove resp.http.X-Purge-Host;
        remove resp.http.X-Surrogate-Key;
    }

    if (resp.http.magentomarker) {
        # Remove the magic marker
        unset resp.http.magentomarker;

        set resp.http.Cache-Control = "no-store, no-cache, must-revalidate, post-check=0, pre-check=0";
        set resp.http.Pragma        = "no-cache";
        set resp.http.Expires       = "Mon, 31 Mar 2008 10:00:00 GMT";
        set resp.http.Age           = "0";
    }

#FASTLY deliver

    return (deliver);
}

sub vcl_error {
    # workaround for possible security issue
    if (req.url ~ "^\s") {
        set obj.status = 400;
        set obj.response = "Malformed request";
        synthetic "";
        return (deliver);
    }

    # geo ip request
    if (obj.status == 750) {
        set req.url = regsub(req.url, "(/fastlycdn/esi/getcountry/.*)", "/fastlycdn/esi/getcountryaction/?country_code=") obj.response;
        return (restart);
    }

    # geo ip country code
    if (obj.status == 755) {
        set obj.status = 200;
	    synthetic obj.response;
        return(deliver);
    }

    # formkey request
    if (obj.status == 760) {
        set obj.status = 200;
	    synthetic obj.response;
        return (deliver);
    }

    # error 200
    if (obj.status == 200) {
        return (deliver);
    }

     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 {"

Fastly CDN server

"};

#FASTLY error
}

sub design_exception {
}

###############################################################################
#
# Fastly CDN for Magento
#
# NOTICE OF LICENSE
#
# This source file is subject to the Fastly CDN for Magento End User License
# Agreement that is bundled with this package in the file LICENSE_FASTLY_CDN.txt.
#
# @copyright   Copyright (c) 2015 Fastly, Inc. (http://www.fastly.com)
# @license     BSD, see LICENSE_FASTLY_CDN.txt
#
###############################################################################

# This is a basic VCL configuration file for fastly CDN for Magento module.

sub vcl_recv {
#FASTLY recv
    # we only deal with GET and HEAD by default
    if (req.request != "GET" && req.request != "HEAD"  && req.request != "FASTLYPURGE") {
        return (pass);
    }

    # auth for purging
    if (req.request == "FASTLYPURGE") {
        # extract token signature and expiration
        set req.http.X-Sig = regsub(req.http.X-Purge-Token, "^[^_]+_(.*)", "\1");
        set req.http.X-Exp = regsub(req.http.X-Purge-Token, "^([^_]+)_.*", "\1");

        # validate signature
        if (req.http.X-Sig == regsub(digest.hmac_sha1(req.service_id, req.url.path req.http.X-Exp), "^0x", "")) {

            # use vcl time math to check expiration timestamp
            set req.http.X-Original-Grace = req.grace;
            set req.grace = std.atoi(strftime({"%s"}, now));
            set req.grace -= std.atoi(req.http.X-Exp);

            if (std.atoi(req.grace) > 0) {
                error 410;
            }

            # clean up grace since we used it for time math
            set req.grace = std.atoi(req.http.X-Original-Grace);
            unset req.http.X-Original-Grace;

        } else {
            error 403;
        }

        # cleanup variables
        unset req.http.X-Purge-Token;
        unset req.http.X-Sig;
        unset req.http.X-Exp;
    }

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

    # disable ESI processing on Origin Shield
    if (req.http.Fastly-FF) {
        set req.esi = false;
    }

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

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

    # geoip lookup
    if (req.url ~ "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 geoip.country_code;
        }
    }

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

    # check for ESI calls
     if (req.url ~ "esi_data=") {
         # check for valid cookie data
         if (req.http.Cookie ~ "FASTLY_CDN-") {
             # get the cookie name to look for
             set req.http.fastlyCDNEsiCookieName = regsub(
                 req.url,
                 "(.*)esi_data=([^&]*)(.*)",
                 "\2"
             );

             # get the cookie value for the cookie name
             # tmp string is NAMESPACE-COOKIENAME@@@@@COMPLETE_COOKIE
             # left of @@@@@ is back-referenced in regex to extract value from cookie
             set req.http.fastlyTmp = "FASTLY_CDN-" req.http.fastlyCDNEsiCookieName "@@@@@" req.http.Cookie;
             set req.http.fastlyCDNRequest = regsub(
                 req.http.fastlyTmp,
                 "^([A-Za-z0-9-_]+)@@@@@.*\1=([^;]*)",
                 "\2"
             );

             # do we have a value for the cookie name?
             if (!req.http.fastlyCDNRequest) {
                 set req.http.fastlyCDNRequest = "default";
             }

             # build backend url
             set req.url = regsub(
                   req.url,
                   "(.*)esi_data=([^&]*)[&]?(.*)",
                   "\1\3"
                 )
                 "&esi_data="
                 req.http.fastlyCDNRequest;

             # clean up temp variables
             remove req.http.fastlyCDNEsiCookieName;
             remove req.http.fastlyCDNRequest;

             return (lookup);
         }
     }

    return(lookup);
}

sub vcl_pass {
#FASTLY pass
}

sub vcl_hash {
    set req.hash += req.http.Https;
    set req.hash += req.http.host;
    set req.hash += req.url;

    if (req.http.cookie ~ "FASTLY_CDN_ENV=") {
        set req.http.fastlyCDNEnv = regsub(
            req.http.cookie,
            "(.*)FASTLY_CDN_ENV=([^;]*)(.*)",
            "\2"
        );
        set req.hash += req.http.fastlyCDNEnv;
        unset req.http.fastlyCDNEnv;
    }

    set req.hash += "#####GENERATION#####";

    if (!(req.url ~ "^/(media|js|skin)/.*\.(png|jpg|jpeg|gif|css|js|swf|ico)$")) {
        call design_exception;
    }
    return (hash);
}

sub vcl_hit {
#FASTLY hit

    if (!obj.cacheable) {
        return(pass);
    }
    return(deliver);
}

sub vcl_miss {
    # Deactivate gzip on origin
    unset bereq.http.Accept-Encoding;

#FASTLY miss

    return(fetch);
}

sub vcl_fetch {
#FASTLY fetch

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

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

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

    if (beresp.http.Content-Type ~ "text/html" || beresp.http.Content-Type ~ "text/xml") {
        # enable ESI feature for Magento response by default
        esi;
    } else {
        # enable gzip for all static content
        if ((beresp.status == 200 || beresp.status == 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 ~ "\.(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.http.Cache-Control ~ "private") {
        set req.http.Fastly-Cachetype = "PRIVATE";
        return (pass);
    }

    if (beresp.status == 500 || beresp.status == 503) {
        set req.http.Fastly-Cachetype = "ERROR";
        set beresp.ttl = 1s;
        set beresp.grace = 5s;
        return (deliver);
    }

    if (beresp.status == 200 || beresp.status == 301 || beresp.status == 404) {
        if (beresp.http.Content-Type ~ "text/html" || beresp.http.Content-Type ~ "text/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";
        }

        set beresp.http.X-Surrogate-Key = beresp.http.Surrogate-Key;
    }

    return (deliver);
}

sub vcl_deliver {
    # debug info
    if (resp.http.X-Cache-Debug) {
        if (obj.hits > 0) {
            set resp.http.X-Cache      = "HIT";
            set resp.http.X-Cache-Hits = obj.hits;
        } else {
            set resp.http.X-Cache      = "MISS";
        }
        set resp.http.X-Cache-Expires  = resp.http.Expires;
    } else {
        # remove Varnish/proxy header
        remove resp.http.X-Varnish;
        remove resp.http.Via;
        remove resp.http.Age;
        remove resp.http.X-Purge-URL;
        remove resp.http.X-Purge-Host;
        remove resp.http.X-Surrogate-Key;
    }

    if (resp.http.magentomarker) {
        # Remove the magic marker
        unset resp.http.magentomarker;

        set resp.http.Cache-Control = "no-store, no-cache, must-revalidate, post-check=0, pre-check=0";
        set resp.http.Pragma        = "no-cache";
        set resp.http.Expires       = "Mon, 31 Mar 2008 10:00:00 GMT";
        set resp.http.Age           = "0";
    }

#FASTLY deliver

    return (deliver);
}

sub vcl_error {
    # workaround for possible security issue
    if (req.url ~ "^\s") {
        set obj.status = 400;
        set obj.response = "Malformed request";
        synthetic "";
        return (deliver);
    }

    # geo ip request
    if (obj.status == 750) {
        set req.url = regsub(req.url, "(/fastlycdn/esi/getcountry/.*)", "/fastlycdn/esi/getcountryaction/?country_code=") obj.response;
        return (restart);
    }

    # geo ip country code
    if (obj.status == 755) {
        set obj.status = 200;
	    synthetic obj.response;
        return(deliver);
    }

    # formkey request
    if (obj.status == 760) {
        set obj.status = 200;
	    synthetic obj.response;
        return (deliver);
    }

    # error 200
    if (obj.status == 200) {
        return (deliver);
    }

     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 {"

Fastly CDN server

"};

#FASTLY error
}

sub design_exception {
}


Make varnish forward the x forwarded for header.

vcl 4.0;

backend default {
  .host = "nginx";
  .port = "5003";
}

acl app {
  "app";
  "worker";
}

#sub vcl_fetch {
#    set obj.ttl = 1m;
#}

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

  if (req.method == "PURGE") {
    if (!client.ip ~ app) {
      return (synth(405, "Not allowed."));
    }

    return (purge);
  }

  # IP forwarding.
  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;
    }
  }

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

  # The endpoints change their content based on ip, etc. Never cache them.
  if (req.url ~ "^/banned/" || req.url ~ "^/verify/" || req.url ~ "^/post/" || req.url ~ "^/post_manage/") {
    return (pass);
  }

  # Remove verification cookie from client
  # The verification cookie has been designed to not interfere with any requests,
  # except for /verify/ and POST requests, which are never cached.
  set req.http.Cookie = regsuball(req.http.Cookie, "verification=[^;]+(; )?", "");

  # Remove a ";" prefix in the cookie if present
  set req.http.Cookie = regsuball(req.http.Cookie, "^;\s*", "");

  # Are there cookies left with only spaces or that are empty?
  if (req.http.cookie ~ "^\s*$") {
    unset req.http.cookie;
  }
}

sub vcl_backend_response {
  set beresp.grace = 10m;
}

vcl 4.0;

backend default {
  .host = "nginx";
  .port = "5003";
}

acl app {
  "app";
  "worker";
}

#sub vcl_fetch {
#    set obj.ttl = 1m;
#}

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

  if (req.method == "PURGE") {
    if (!client.ip ~ app) {
      return (synth(405, "Not allowed."));
    }

    return (purge);
  }

  set req.http.X-Forwarded-For = client.ip;

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

  # The endpoints change their content based on ip, etc. Never cache them.
  if (req.url ~ "^/banned/" || req.url ~ "^/verify/" || req.url ~ "^/post/" || req.url ~ "^/post_manage/") {
    return (pass);
  }

  # Remove verification cookie from client
  # The verification cookie has been designed to not interfere with any requests,
  # except for /verify/ and POST requests, which are never cached.
  set req.http.Cookie = regsuball(req.http.Cookie, "verification=[^;]+(; )?", "");

  # Remove a ";" prefix in the cookie if present
  set req.http.Cookie = regsuball(req.http.Cookie, "^;\s*", "");

  # Are there cookies left with only spaces or that are empty?
  if (req.http.cookie ~ "^\s*$") {
    unset req.http.cookie;
  }
}

sub vcl_backend_response {
  set beresp.grace = 10m;
}


re #444 - Set the secret to the vagrant box default

vcl 4.0;
# Based on: https://github.com/mattiasgeniar/varnish-4.0-configuration-templates/blob/master/default.vcl

import std;
import directors;

probe health
{
    #.url = "/varnish-enabled";
    # We prefer to only do a HEAD /
    .request =
        "HEAD /varnish-enabled HTTP/1.1"
        "Host: localhost"
        "Connection: close";
    .interval = 5s;   # check the health of each backend every 5 seconds
    .timeout = 1s;    # timing out after 1 second.
    # If 3 out of the last 5 polls succeeded the backend is considered healthy, otherwise it will be marked as sick
    .window = 5;
    .threshold = 3;
    .expected_response = 200;
}

backend default
{
    .host = "127.0.0.1";        # IP or Hostname of backend
    .port = "8080";             # Port Apache or whatever is listening
    .max_connections = 300;     # That's it
    .probe = health;
    .connect_timeout            = 600s;  # How long to wait before we receive a first byte from our backend?
    .first_byte_timeout         = 600s;  # How long to wait for a backend connection?
    .between_bytes_timeout      = 600s;  # How long to wait between bytes received from our backend?
}

backend passthrough
{
    .host = "127.0.0.1";        # IP or Hostname of backend
    .port = "8080";             # Port Apache or whatever is listening
}

acl localhost
{
    "localhost";
    "33.33.33.63";
    "::1";
}

sub vcl_init
{
    # Called when VCL is loaded, before any requests pass through it.
    # Typically used to initialize VMODs.

    new vdir = directors.round_robin();
    vdir.add_backend(default);
    # vdir.add_backend(server...);
    # vdir.add_backend(servern);
}

sub 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 and if needed to modify the request.

    # Send all traffic to the vdir director
    set req.backend_hint = vdir.backend();

    if (req.restarts == 0)
    {
        # Set or append the client.ip to X-Forwarded-For header
        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;
        }

        set req.http.X-Forwarded-By = server.ip;
        set req.http.X-Forwarded-Port = std.port(client.ip);
    }

    # Normalize the header, remove the port (in case you're testing this on various TCP ports)
    set req.http.Host = regsub(req.http.Host, ":[0-9]+", "");

    # Normalize the query arguments
    set req.url = std.querysort(req.url);

    # Allow purging
    if (req.method == "PURGE")
    {
        # purge is the ACL defined at the begining
        if (!client.ip ~ localhost) {
            return (synth(405, "This IP is not allowed to send PURGE requests."));
        }

        # If you got this stage (and didn't error out above), purge the cached result
        return (purge);
    }

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

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

    # Implementing websocket support (https://www.varnish-cache.org/docs/4.0/users-guide/vcl-example-websockets.html)
    if (req.http.Upgrade ~ "(?i)websocket") {
        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);
    }

    # Specific Nooku Platform rules

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

    # Specific Nooku Server rules

        # Do not cache webgrind.nooku.dev
        if (req.http.host == "webgrind.nooku.dev") {
            return (pass);
        }

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

        # Do not cache /apc and /phpinfo
        if (req.url == "/apc" || req.url == "/phpinfo") {
            return (pass);
        }

    # Generic URL manipulation, useful for all templates that follow

        # Remove the Google Analytics added parameters, useless for our backend
        if (req.url ~ "(\?|&)(utm_source|utm_medium|utm_campaign|utm_content|gclid|cx|ie|cof|siteurl)=") {
            set req.url = regsuball(req.url, "&(utm_source|utm_medium|utm_campaign|utm_content|gclid|cx|ie|cof|siteurl)=([A-z0-9_\-\.%25]+)", "");
            set req.url = regsuball(req.url, "\?(utm_source|utm_medium|utm_campaign|utm_content|gclid|cx|ie|cof|siteurl)=([A-z0-9_\-\.%25]+)", "?");
            set req.url = regsub(req.url, "\?&", "?");
            set req.url = regsub(req.url, "\?$", "");
        }

        # Strip hash, server doesn't need it.
        if (req.url ~ "\#") {
            set req.url = regsub(req.url, "\#.*$", "");
        }

        # Strip a trailing ? if it exists
        if (req.url ~ "\?$") {
            set req.url = regsub(req.url, "\?$", "");
        }

    # Cookie manipulation

        # Remove the "has_js" cookie
        set req.http.Cookie = regsuball(req.http.Cookie, "has_js=[^;]+(; )?", "");

        # Remove any Google Analytics based cookies
        set req.http.Cookie = regsuball(req.http.Cookie, "__utm.=[^;]+(; )?", "");
        set req.http.Cookie = regsuball(req.http.Cookie, "_ga=[^;]+(; )?", "");
        set req.http.Cookie = regsuball(req.http.Cookie, "utmctr=[^;]+(; )?", "");
        set req.http.Cookie = regsuball(req.http.Cookie, "utmcmd.=[^;]+(; )?", "");
        set req.http.Cookie = regsuball(req.http.Cookie, "utmccn.=[^;]+(; )?", "");

        # Remove DoubleClick offensive cookies
        set req.http.Cookie = regsuball(req.http.Cookie, "__gads=[^;]+(; )?", "");

        # Remove the Quant Capital cookies (added by some plugin, all __qca)
        set req.http.Cookie = regsuball(req.http.Cookie, "__qc.=[^;]+(; )?", "");

        # Remove the AddThis cookies
        set req.http.Cookie = regsuball(req.http.Cookie, "__atuv.=[^;]+(; )?", "");

        # Remove a ";" prefix in the cookie if present
        set req.http.Cookie = regsuball(req.http.Cookie, "^;\s*", "");

        # Remove the csrf_token cookie
        set req.http.Cookie = regsuball(req.http.Cookie, "csrf_token=[^;]+(; )?", "");

        # Are there cookies left with only spaces or that are empty?
        if (req.http.cookie ~ "^\s*$") {
            unset req.http.cookie;
        }

        # Large static files are delivered directly to the end-user without waiting for Varnish to fully read the file first.
        # Varnish 4 supports Streaming, so set do_stream in vcl_backend_response()
        if (req.url ~ "^[^?]*\.(mp[34]|rar|tar|tgz|gz|wav|zip|bz2|xz|7z|avi|mov|og[gvaxm]|mpe?g|mk[av])(\?.*)?$") {
            unset req.http.Cookie;
            return (hash);
        }

        # Remove all cookies for static files
        # Before you blindly enable this read: https://ma.ttias.be/stop-caching-static-files/
        #if (req.method == "GET")
        #{
        #    # Cache files with these extensions and remove cookie
        #    if (req.url ~ "^[^?]*\.(bmp|bz2|css|doc|eot|flv|gif|gz|ico|jpeg|jpg|js|less|pdf|png|rtf|swf|txt|woff|xml)(\?.*)?$") {
        #        unset req.http.Cookie;
        #        return (hash);
        #    }
        #}

    # Normalize Accept-Encoding header
    # straight from the manual: https://www.varnish-cache.org/docs/3.0/tutorial/vary.html
    # TODO: Test if it's still needed, Varnish 4 now does this by itself if http_gzip_support = on
    # https://www.varnish-cache.org/docs/trunk/users-guide/compression.html
    # https://www.varnish-cache.org/docs/trunk/phk/gzip.html
    if (req.http.Accept-Encoding)
    {
        if (req.url ~ "\.(jpg|png|gif|gz|tgz|bz2|tbz|mp3|ogg)$") {
            # No point in compressing these
            unset req.http.Accept-Encoding;
        } elsif (req.http.Accept-Encoding ~ "gzip") {
            set req.http.Accept-Encoding = "gzip";
        } elsif (req.http.Accept-Encoding ~ "deflate") {
            set req.http.Accept-Encoding = "deflate";
        } else {
            # unkown algorithm
            unset req.http.Accept-Encoding;
        }
    }

    if (req.http.Cache-Control ~ "(?i)no-cache" && client.ip ~ localhost)
    {
        # http://varnish.projects.linpro.no/wiki/VCLExampleEnableForceRefresh
        # Ignore requests via proxy caches and badly behaved crawlers
        # like msnbot that send no-cache with every request.
        if (! (req.http.Via || req.http.User-Agent ~ "(?i)bot" || req.http.X-Purge))
        {
            # Doesn't seems to refresh the object in the cache
            set req.hash_always_miss = true;

            # Couple this with restart in vcl_purge and X-Purge header to avoid loops
            return(purge);
        }
    }

    # Send Surrogate-Capability headers to announce ESI support to backend
    set req.http.Surrogate-Capability = "varnish=ESI/1.0";

    # Requests that require authorization are not cacheable by default
    if (req.http.Authorization) {
        return (pass);
    }

    # We do not support SPDY or HTTP/2.0
    if (req.method == "PRI") {
        return (synth(405));
    }

    return (hash);
}

# Called upon entering pipe mode. In this mode, the request is passed on to the backend, and any further data from both
# the client and backend is passed on unaltered until either end closes the connection. Basically, Varnish will degrade
# into a simple TCP proxy, shuffling bytes back and forth. For a connection in pipe mode, no other VCL subroutine will
# ever get called after vcl_pipe.
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";
    # It is not set by default as it might break some broken web applications, like IIS with NTLM authentication.
    # set bereq.http.Connection = "Close";

    # Implementing websocket support (https://www.varnish-cache.org/docs/4.0/users-guide/vcl-example-websockets.html)
    if (req.http.upgrade) {
        set bereq.http.upgrade = req.http.upgrade;
    }

    return (pipe);
}

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

# Called after vcl_recv to create a hash value for the request. This is used as a key to look up the object in Varnish.
sub vcl_hash
{
    hash_data(req.url);
    if (req.http.host) {
        hash_data(req.http.host);
    } else {
        hash_data(server.ip);
    }

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

# Called when a cache lookup is successful.
sub vcl_hit
{
    # A pure unadultered hit, deliver it
    if (obj.ttl >= 0s) {
        return (deliver);
    }

    # https://www.varnish-cache.org/docs/trunk/users-guide/vcl-grace.html
    # When several clients are requesting the same page Varnish will send one request to the backend and place the
    # others on hold while fetching one copy from the backend. In some products this is called request coalescing and
    # Varnish does this automatically.

    # If you are serving thousands of hits per second the queue of waiting requests can get huge. There are two
    # potential problems - one is a thundering herd problem - suddenly releasing a thousand threads to serve content
    # might send the load sky high. Secondly - nobody likes to wait. To deal with this we can instruct Varnish to keep
    # the objects in cache beyond their TTL and to serve the waiting requests somewhat stale content.

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

    # We have no fresh fish. Lets look at the stale ones.
    if (std.healthy(req.backend_hint))
    {
        # Backend is healthy. Limit age to 10s.
        if (obj.ttl + 10s > 0s)
        {
            #set req.http.grace = "normal(limited)";
            return (deliver);
        }
        else
        {
            # No candidate for grace. Fetch a fresh object.
            return(fetch);
        }
    }
    else
    {
        # backend is sick - use full grace
        if (obj.ttl + obj.grace > 0s)
        {
            #set req.http.grace = "full";
            return (deliver);
        }
        else
        {
            # no graced object.
            return (fetch);
        }
    }

    # fetch & deliver once we get the result
    return (fetch); # Dead code, keep as a safeguard
}

# Called after a cache lookup if the requested document was not found in the cache. Its purpose is to decide whether
# or not to attempt to retrieve the document from the backend, and which backend to use.
sub vcl_miss
{
    return (fetch);
}

 # Called after the response headers has been successfully retrieved from the backend.
sub vcl_backend_response
{
    # Enable ESI handling
    if (beresp.http.Surrogate-Control ~ "ESI/1.0" ) {
        set beresp.do_esi = true;
    }

    # Store the csrf_token cookie temporarily if the response is cacheabale
    if (beresp.http.Set-Cookie && !beresp.uncacheable)
    {
        set beresp.http.X-Varnish-Cookie = beresp.http.Set-Cookie;
        unset beresp.http.Set-Cookie;
    }

    # Cache handling all static files
    if (bereq.method == "GET")
    {
        # Before you blindly enable this, read: https://ma.ttias.be/stop-caching-static-files/
        #if (bereq.url ~ "^[^?]*\.(bmp|bz2|css|doc|eot|flv|gif|gz|ico|jpeg|jpg|js|less|mp[34]|pdf|png|rar|rtf|swf|tar|tgz|txt|wav|woff|xml|zip)(\?.*)?$") {
        #    unset beresp.http.Set-Cookie;
        #}

        # Large static files are delivered directly to the end-user without waiting for Varnish to fully read the
        # file first. Check memory usage it'll grow in fetch_chunksize blocks (128k by default) if the backend
        # doesn't send a Content-Length header. Only enable it for large files
        if (bereq.url ~ "^[^?]*\.(mp[34]|rar|tar|tgz|gz|wav|zip|bz2|xz|7z|avi|mov|og[gvaxm]|mpe?g|mk[av])(\?.*)?$")
        {
            unset beresp.http.Set-Cookie;

            # Use streaming to avoid locking
            set beresp.do_stream = true;

            # Don't try to compress it for storage
            set beresp.do_gzip = false;

            # Disable Transfer-encoding chunked when serving HTML5 video
            # See : http://stackoverflow.com/questions/23643233/how-do-i-disable-transfer-encoding-chunked-encoding-in-varnish
            # See : https://www.varnish-cache.org/trac/ticket/1506
            if(beresp.http.Content-Type ~ "video") {
                set beresp.do_stream = true;
                set beresp.do_esi    = true;
            }
        }

        # Set 2min cache if unset for static files
        if (beresp.ttl <= 0s || beresp.http.Set-Cookie || beresp.http.Vary == "*")
        {
            set beresp.ttl         = 120s; # Important, you shouldn't rely on this, SET YOUR HEADERS in the backend
            set beresp.uncacheable = true;

            return (deliver);
        }
    }

    # Sometimes, a 301 or 302 redirect formed via Apache's mod_rewrite can mess with the HTTP port that is being passed along.
    # This often happens with simple rewrite rules in a scenario where Varnish runs on :80 and Apache on :8080 on the same box.
    # A redirect can then often redirect the end-user to a URL on :8080, where it should be :80.
    # This may need finetuning on your setup.
    #
    # To prevent accidental replace, we only filter the 301/302 redirects for now.
    if (beresp.status == 301 || beresp.status == 302) {
        set beresp.http.Location = regsub(beresp.http.Location, ":[0-9]+", "");
    }

    # Do not cache errors from the backend
    if (beresp.status >= 400)
    {
        set beresp.uncacheable = true;
        set beresp.ttl = 0s;

        return (deliver);
    }

    # Allow stale content, in case the backend goes down. Make Varnish keep all objects for 6 hours beyond their TTL
    set beresp.grace = 6h;

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

    return (deliver);
}

# Called when the backend returns an error
sub vcl_backend_error
{
    if (beresp.status == 503 && bereq.retries < 5)
    {
        set beresp.http.X-Varnish-Retries = bereq.retries;
        return(retry);
    }

    return(deliver);
}

# Called before a cached object is delivered to the client.
sub vcl_deliver
{
    # Send the Cookie header again if a temporay header was stored
    if (req.http.X-Varnish-Cookie) {
        set resp.http.Set-Cookie = req.http.X-Varnish-Cookie;
    }

    # Add extra headers if debugging is enabled
    if (resp.http.x-varnish-debug && server.ip ~ localhost)
    {
        set resp.http.X-Served-By = server.hostname;

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

        # Please note that obj.hits behaviour changed in 4.0, now it counts per objecthead, not per object
        # and obj.hits may not be reset in some cases where bans are in use. See bug 1492 for details.
        # So take hits with a grain of salt
        set resp.http.X-Varnish-Hits = obj.hits;
    }
    else
    {
        # Remove ban-lurker friendly custom headers when delivering to client
        unset resp.http.X-Url;
        unset resp.http.X-Host;

        # Remove some headers: PHP version
        unset resp.http.X-Powered-By;

        # Remove some headers: Apache version & OS
        unset resp.http.Server;
        unset resp.http.Via;
        unset resp.http.Link;
        unset resp.http.X-Varnish;
        unset resp.http.X-Varnish-Tag;
        unset resp.http.X-Varnish-Debug;
        unset resp.http.X-Varnish-Host;
        unset resp.http.X-Varnish-Url;
        unset resp.http.X-Varnish-Retries;
        unset resp.http.X-Varnish-Cookie;
        unset resp.http.Surrogate-Control;
    }

    return (deliver);
}

sub vcl_purge
{
    # Only handle actual PURGE HTTP methods, everything else is discarded
    if (req.method != "PURGE" && server.ip ~ localhost)
    {
        set req.http.X-Varnish-Purge = "Yes";
        return (restart);
    }
}

sub vcl_synth
{
    if (resp.status == 720)
    {
        # We use this special error status 720 to force redirects with 301 (permanent) redirects
        # To use this, call the following from anywhere in vcl_recv: return (synth(720, "http://host/new.html"));
        set resp.http.Location = resp.reason;
        set resp.status = 301;

        return (deliver);
    }
    elseif (resp.status == 721)
    {
        # And we use error status 721 to force redirects with a 302 (temporary) redirect
        # To use this, call the following from anywhere in vcl_recv: return (synth(720, "http://host/new.html"));

        set resp.http.Location = resp.reason;
         set resp.status = 302;

        return (deliver);
    }

    return (deliver);
}

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

vcl 4.0;

# Based on: https://github.com/mattiasgeniar/varnish-4.0-configuration-templates/blob/master/default.vcl
# Corrected & improved for 4.0.2 by jnerin@gmail.com

import std;
import directors;

# Backend definitions
backend default
{
    .host = "127.0.0.1";        # IP or Hostname of backend
    .port = "8080";             # Port Apache or whatever is listening
    .max_connections = 300;     # That's it
    .probe = {
        #.url = "/varnish-enabled";
        # We prefer to only do a HEAD /
        .request =
            "HEAD /varnish-enabled HTTP/1.1"
            "Host: localhost"
            "Connection: close";
      .interval = 5s;   # check the health of each backend every 5 seconds
      .timeout = 1s;    # timing out after 1 second.
       # If 3 out of the last 5 polls succeeded the backend is considered healthy, otherwise it will be marked as sick
      .window = 5;
      .threshold = 3;
    }

    .connect_timeout = 600s;        # How long to wait before we receive a first byte from our backend?
    .first_byte_timeout = 600s;     # How long to wait for a backend connection?
    .between_bytes_timeout = 600s;  # How long to wait between bytes received from our backend?
}

# Acl definitions
acl localhost
{
    "localhost";
    "33.33.33.63";
    "::1";
}

# Called when VCL is loaded, before any requests pass through it. Typically used to initialize VMODs.
sub vcl_init
{
    new vdir = directors.round_robin();
    vdir.add_backend(default);
    # vdir.add_backend(server...);
    # vdir.add_backend(servern);
}

# 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 also used to
# modify the request
sub vcl_recv
{
    # Add a Surrogate-Capability header to announce ESI support.
    set req.http.Surrogate-Capability = "varnish=ESI/1.0";

    # Send all traffic to the vdir director
    set req.backend_hint = vdir.backend(); # send all traffic to the vdir director

    if (req.restarts == 0)
    {
        # Set or append the client.ip to X-Forwarded-For header
        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;
        }

        set req.http.X-Forwarded-By = server.ip;
        set req.http.X-Forwarded-Port = std.port(client.ip);
    }

    # Normalize the header, remove the port (in case you're testing this on various TCP ports)
    set req.http.Host = regsub(req.http.Host, ":[0-9]+", "");

    # Normalize the query arguments
    set req.url = std.querysort(req.url);

    # Allow purging
    if (req.method == "PURGE")
    {
        # purge is the ACL defined at the begining
        if (!client.ip ~ localhost) {
          return (synth(405, "This IP is not allowed to send PURGE requests."));
        }

        return (purge);
    }

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

    # Implementing websocket support (https://www.varnish-cache.org/docs/4.0/users-guide/vcl-example-websockets.html)
    if (req.http.Upgrade ~ "(?i)websocket") {
        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);
    }

    # Send Surrogate-Capability headers to announce ESI support to backend
    set req.http.Surrogate-Capability = "key=ESI/1.0";
    if (req.http.Authorization)
    {
        # Not cacheable by default
        return (pass);
    }

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

    # Specific Nooku Platform rules

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

    # Specific Nooku Server rules

        # Do not cache webgrind.nooku.dev
        if (req.http.host == "webgrind.nooku.dev") {
            return (pass);
        }

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

        # Do not cache /apc and /phpinfo
        if (req.url == "/apc" || req.url == "/phpinfo") {
            return (pass);
        }

    # Generic URL manipulation, useful for all templates that follow

        # Remove the Google Analytics added parameters, useless for our backend
        if (req.url ~ "(\?|&)(utm_source|utm_medium|utm_campaign|utm_content|gclid|cx|ie|cof|siteurl)=") {
            set req.url = regsuball(req.url, "&(utm_source|utm_medium|utm_campaign|utm_content|gclid|cx|ie|cof|siteurl)=([A-z0-9_\-\.%25]+)", "");
            set req.url = regsuball(req.url, "\?(utm_source|utm_medium|utm_campaign|utm_content|gclid|cx|ie|cof|siteurl)=([A-z0-9_\-\.%25]+)", "?");
            set req.url = regsub(req.url, "\?&", "?");
            set req.url = regsub(req.url, "\?$", "");
        }

        # Strip hash, server doesn't need it.
        if (req.url ~ "\#") {
            set req.url = regsub(req.url, "\#.*$", "");
        }

        # Strip a trailing ? if it exists
        if (req.url ~ "\?$") {
            set req.url = regsub(req.url, "\?$", "");
        }

    # Cookie manipulation

        # Remove the "has_js" cookie
        set req.http.Cookie = regsuball(req.http.Cookie, "has_js=[^;]+(; )?", "");

        # Remove any Google Analytics based cookies
        set req.http.Cookie = regsuball(req.http.Cookie, "__utm.=[^;]+(; )?", "");
        set req.http.Cookie = regsuball(req.http.Cookie, "_ga=[^;]+(; )?", "");
        set req.http.Cookie = regsuball(req.http.Cookie, "utmctr=[^;]+(; )?", "");
        set req.http.Cookie = regsuball(req.http.Cookie, "utmcmd.=[^;]+(; )?", "");
        set req.http.Cookie = regsuball(req.http.Cookie, "utmccn.=[^;]+(; )?", "");

        # Remove DoubleClick offensive cookies
        set req.http.Cookie = regsuball(req.http.Cookie, "__gads=[^;]+(; )?", "");

        # Remove the Quant Capital cookies (added by some plugin, all __qca)
        set req.http.Cookie = regsuball(req.http.Cookie, "__qc.=[^;]+(; )?", "");

        # Remove the AddThis cookies
        set req.http.Cookie = regsuball(req.http.Cookie, "__atuv.=[^;]+(; )?", "");

        # Remove a ";" prefix in the cookie if present
        set req.http.Cookie = regsuball(req.http.Cookie, "^;\s*", "");

        # Remove the csrf_token cookie
        set req.http.Cookie = regsuball(req.http.Cookie, "csrf_token=[^;]+(; )?", "");

        # Are there cookies left with only spaces or that are empty?
        if (req.http.cookie ~ "^\s*$") {
            unset req.http.cookie;
        }

    # We do not support SPDY or HTTP/2.0
    if (req.method == "PRI") {
        return (synth(405));
    }

    # Normalize Accept-Encoding header
    # straight from the manual: https://www.varnish-cache.org/docs/3.0/tutorial/vary.html
    # TODO: Test if it's still needed, Varnish 4 now does this by itself if http_gzip_support = on
    # https://www.varnish-cache.org/docs/trunk/users-guide/compression.html
    # https://www.varnish-cache.org/docs/trunk/phk/gzip.html
    if (req.http.Accept-Encoding)
    {
        if (req.url ~ "\.(jpg|png|gif|gz|tgz|bz2|tbz|mp3|ogg)$") {
          # No point in compressing these
          unset req.http.Accept-Encoding;
        } elsif (req.http.Accept-Encoding ~ "gzip") {
          set req.http.Accept-Encoding = "gzip";
        } elsif (req.http.Accept-Encoding ~ "deflate") {
          set req.http.Accept-Encoding = "deflate";
        } else {
          # unkown algorithm
          unset req.http.Accept-Encoding;
        }
    }

    if (req.http.Cache-Control ~ "(?i)no-cache" && client.ip ~ localhost)
    {
        # http://varnish.projects.linpro.no/wiki/VCLExampleEnableForceRefresh
        # Ignore requests via proxy caches and badly behaved crawlers
        # like msnbot that send no-cache with every request.
        if (! (req.http.Via || req.http.User-Agent ~ "(?i)bot" || req.http.X-Purge))
        {
             # Doesn't seems to refresh the object in the cache
             set req.hash_always_miss = true;

             # Couple this with restart in vcl_purge and X-Purge header to avoid loops
             return(purge);
        }
    }

    # Large static files are delivered directly to the end-user without waiting for Varnish to fully read the file first.
    # Varnish 4 fully supports Streaming, so set do_stream in vcl_backend_response()
    if (req.url ~ "^[^?]*\.(mp[34]|rar|tar|tgz|gz|wav|zip|bz2|xz|7z|avi|mov|ogm|mpe?g|mk[av])(\?.*)?$") {
        unset req.http.Cookie;
        return (hash);
    }

    # Remove all cookies for static files
    # Before you blindly enable this, have a read here: https://ma.ttias.be/stop-caching-static-files/
    if (req.method == "GET")
    {
        # Cache files with these extensions and remove cookie
        if (req.url ~ "^[^?]*\.(bmp|bz2|css|doc|eot|flv|gif|gz|ico|jpeg|jpg|js|less|pdf|png|rtf|swf|txt|woff|xml)(\?.*)?$") {
            unset req.http.Cookie;
            return (hash);
        }
    }

    return (hash);
}

# Called upon entering pipe mode. In this mode, the request is passed on to the backend, and any further data from both
# the client and backend is passed on unaltered until either end closes the connection. Basically, Varnish will degrade
# into a simple TCP proxy, shuffling bytes back and forth. For a connection in pipe mode, no other VCL subroutine will
# ever get called after vcl_pipe.
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";
    # It is not set by default as it might break some broken web applications, like IIS with NTLM authentication.
    # set bereq.http.Connection = "Close";

    # Implementing websocket support (https://www.varnish-cache.org/docs/4.0/users-guide/vcl-example-websockets.html)
    if (req.http.upgrade) {
        set bereq.http.upgrade = req.http.upgrade;
    }

    return (pipe);
}

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

# Called after vcl_recv to create a hash value for the request. This is used as a key to look up the object in Varnish.
sub vcl_hash
{
    hash_data(req.url);
    if (req.http.host) {
        hash_data(req.http.host);
    } else {
        hash_data(server.ip);
    }

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

# Called when a cache lookup is successful.
sub vcl_hit
{
    # A pure unadultered hit, deliver it
    if (obj.ttl >= 0s) {
        return (deliver);
    }

    # https://www.varnish-cache.org/docs/trunk/users-guide/vcl-grace.html
    # When several clients are requesting the same page Varnish will send one request to the backend and place the
    # others on hold while fetching one copy from the backend. In some products this is called request coalescing and
    # Varnish does this automatically.

    # If you are serving thousands of hits per second the queue of waiting requests can get huge. There are two
    # potential problems - one is a thundering herd problem - suddenly releasing a thousand threads to serve content
    # might send the load sky high. Secondly - nobody likes to wait. To deal with this we can instruct Varnish to keep
    # the objects in cache beyond their TTL and to serve the waiting requests somewhat stale content.

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

    # We have no fresh fish. Lets look at the stale ones.
    if (std.healthy(req.backend_hint))
    {
        # Backend is healthy. Limit age to 10s.
        if (obj.ttl + 10s > 0s)
        {
            #set req.http.grace = "normal(limited)";
            return (deliver);
        }
        else
        {
            # No candidate for grace. Fetch a fresh object.
            return(fetch);
        }
    }
    else
    {
        # backend is sick - use full grace
        if (obj.ttl + obj.grace > 0s)
        {
            #set req.http.grace = "full";
            return (deliver);
        }
        else
        {
            # no graced object.
            return (fetch);
        }
    }

    # fetch & deliver once we get the result
    return (fetch); # Dead code, keep as a safeguard
}

# Called after a cache lookup if the requested document was not found in the cache. Its purpose is to decide whether
# or not to attempt to retrieve the document from the backend, and which backend to use.
sub vcl_miss
{
    return (fetch);
}

 # Called after the response headers has been successfully retrieved from the backend.
sub vcl_backend_response
{
    # Enable ESI handling
    if (beresp.http.Surrogate-Control ~ "ESI/1.0" ) {
        set beresp.do_esi = true;
    }

    # Store the csrf_token cookie temporarily if the response is cacheabale
    if (beresp.http.Set-Cookie && beresp.uncacheable )
    {
        set beresp.http.X-Varnish-Cookie = beresp.http.Set-Cookie;
        unset beresp.http.Set-Cookie;
    }

    # Enable cache for all static files
    # Before you blindly enable this, have a read here: https://ma.ttias.be/stop-caching-static-files/
    if (bereq.method == "GET")
    {
        if (bereq.url ~ "^[^?]*\.(bmp|bz2|css|doc|eot|flv|gif|gz|ico|jpeg|jpg|js|less|mp[34]|pdf|png|rar|rtf|swf|tar|tgz|txt|wav|woff|xml|zip)(\?.*)?$") {
            unset beresp.http.Set-Cookie;
        }
    }

    # Large static files are delivered directly to the end-user without waiting for Varnish to fully read the file first.
    # Varnish 4 fully supports Streaming, so use streaming here to avoid locking.
    if (bereq.url ~ "^[^?]*\.(mp[34]|rar|tar|tgz|gz|wav|zip|bz2|xz|7z|avi|mov|ogm|mpe?g|mk[av])(\?.*)?$")
    {
        unset beresp.http.Set-Cookie;
        set beresp.do_stream = true;
        # Check memory usage it'll grow in fetch_chunksize blocks (128k by default) if
        # the backend doesn't send a Content-Length header, so only enable it for big objects
        set beresp.do_gzip = false; # Don't try to compress it for storage
    }

    # Sometimes, a 301 or 302 redirect formed via Apache's mod_rewrite can mess with the HTTP port that is being passed along.
    # This often happens with simple rewrite rules in a scenario where Varnish runs on :80 and Apache on :8080 on the same box.
    # A redirect can then often redirect the end-user to a URL on :8080, where it should be :80.
    # This may need finetuning on your setup.
    #
    # To prevent accidental replace, we only filter the 301/302 redirects for now.
    if (beresp.status == 301 || beresp.status == 302) {
        set beresp.http.Location = regsub(beresp.http.Location, ":[0-9]+", "");
    }

    # Set 2min cache if unset for static files
    if (beresp.ttl <= 0s || beresp.http.Set-Cookie || beresp.http.Vary == "*")
    {
        set beresp.ttl = 120s; # Important, you shouldn't rely on this, SET YOUR HEADERS in the backend
        set beresp.uncacheable = true;

        return (deliver);
    }

    # Do not cache errors from the backend
    if (beresp.status >= 400)
    {
        set beresp.uncacheable = true;
        set beresp.ttl = 0s;

        return (deliver);
    }

    # Allow stale content, in case the backend goes down. Make Varnish keep all objects for 6 hours beyond their TTL
    set beresp.grace = 6h;

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

    return (deliver);
}

# Called when the backend returns an error
sub vcl_backend_error
{
    if (beresp.status == 503 && bereq.retries < 5)
    {
        set beresp.http.X-Varnish-Retries = bereq.retries;
        return(retry);
    }

    return(deliver);
}

# Called before a cached object is delivered to the client.
sub vcl_deliver
{
    # Send the Cookie header again if a temporay header was stored
    if (req.http.X-Varnish-Cookie) {
        set resp.http.Set-Cookie = req.http.X-Varnish-Cookie;
    }

    # Add extra headers if debugging is enabled
    if (resp.http.x-varnish-debug && server.ip ~ localhost)
    {
        set resp.http.X-Served-By = server.hostname;

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

        # Please note that obj.hits behaviour changed in 4.0, now it counts per objecthead, not per object
        # and obj.hits may not be reset in some cases where bans are in use. See bug 1492 for details.
        # So take hits with a grain of salt
        set resp.http.X-Varnish-Hits = obj.hits;
    }
    else
    {
        # Remove ban-lurker friendly custom headers when delivering to client
        unset resp.http.X-Url;
        unset resp.http.X-Host;

        # Remove some headers: PHP version
        unset resp.http.X-Powered-By;

        # Remove some headers: Apache version & OS
        unset resp.http.Server;
        unset resp.http.Via;
        unset resp.http.Link;
        unset resp.http.X-Varnish;
        unset resp.http.X-Varnish-Tag;
        unset resp.http.X-Varnish-Debug;
        unset resp.http.X-Varnish-Host;
        unset resp.http.X-Varnish-Url;
        unset resp.http.X-Varnish-Retries;
        unset resp.http.X-Varnish-Cookie;
        unset resp.http.Surrogate-Control;
    }

    return (deliver);
}

sub vcl_purge
{
    # Only handle actual PURGE HTTP methods, everything else is discarded
    if (req.method != "PURGE" && server.ip ~ localhost)
    {
        set req.http.X-Varnish-Purge = "Yes";
        return(restart);
    }
}

sub vcl_synth
{
    if (resp.status == 720)
    {
        # We use this special error status 720 to force redirects with 301 (permanent) redirects
        # To use this, call the following from anywhere in vcl_recv: return (synth(720, "http://host/new.html"));
        set resp.status = 301;
        set resp.http.Location = resp.reason;

        return (deliver);
    }
    elseif (resp.status == 721)
    {
        # And we use error status 721 to force redirects with a 302 (temporary) redirect
        # To use this, call the following from anywhere in vcl_recv: return (synth(720, "http://host/new.html"));

        set resp.status = 302;
        set resp.http.Location = resp.reason;

        return (deliver);
    }

    return (deliver);
}

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


remove /v2/docs redirects

remove /v2/docs 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 == "/") {
		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 != "/v2/polyfill.js" && req.url.path != "/v2/polyfill.min.js") {
		if (req.url.path ~ "^/v2($|/)") {
			error 908;
		}
	}
	if (req.url.path == "/") {
		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);
	}
}


Revert "fix redirects"

Revert "fix redirects"

This reverts commit c4a85eada332dbfb6867d8ec1c80b57f04d6c960.

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

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


Correct the S3 regex for packages URLs

Correct the S3 regex for packages URLs

sub vcl_recv {

    # I'm not 100% sure on what this is exactly for, it was taken from the
    # Fastly documentation, however, what I *believe* it does is just ensure
    # that we don't serve a stale copy of the page from the shield node when
    # an edge node is requesting content.
    if (req.http.Fastly-FF) {
        set req.max_stale_while_revalidate = 0s;
    }

    # Some (Older) clients will send a hash fragment as part of the URL even
    # though that is a local only modification. This breaks this badly for the
    # files in S3, and in general it's just not needed.
    set req.url = regsub(req.url, "#.*$", "");

    # Most of the URLs in Warehouse do not support or require any sort of query
    # parameter. If we strip these at the edge then we'll increase our cache
    # efficiency when they won't otherwise change the output of the pages.
    #
    # This will match any URL except those that start with:
    #
    #   * /search/
    #   * /account/login/
    #   * /account/logout/
    #   * /account/register/
    #   * /pypi
    if (req.url !~ "^/(search/|account/(login|logout|register)/|pypi)") {
        set req.url = req.url.path;
    }

    # Sort all of our query parameters, this will ensure that the same query
    # parameters in a different order will end up being represented as the same
    # thing, reducing cache misses due to ordering differences.
    set req.url = boltsort.sort(req.url);

#FASTLY recv

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

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

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

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

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

    # Canonicalize our domains by redirecting any domain that doesn't match our
    # primary domain to our primary domain. We do this *after* the HTTPS check
    # on purpose.
    if (std.tolower(req.http.host) != std.tolower(req.http.Primary-Domain)) {
        set req.http.Location = "https://" req.http.Primary-Domain req.url;
        error 750 "Redirect to Primary Domain";
    }

    # Requests to /packages/ get dispatched to Amazon instead of to our typical
    # Origin. This requires a a bit of setup to make it work.
    if (req.url ~ "^/packages/[a-f0-9]{2}/[a-f0-9]{2}/[a-f0-9]{60}/") {
        # Setup our environment to better match what S3 expects/needs
        set req.http.Host = req.http.AWS-Bucket-Name ".s3.amazonaws.com";
        set req.http.Date = now;
        set req.url = regsuball(req.url, "\+", urlencode("+"));

        # Compute the Authorization header that S3 requires to be able to
        # access the files stored there.
        set req.http.Authorization = "AWS " req.http.AWS-Access-Key-ID ":" digest.hmac_sha1_base64(req.http.AWS-Secret-Access-Key, "GET" LF LF LF req.http.Date LF "/" req.http.AWS-Bucket-Name req.url.path);

        # We don't want to send our Warehouse-Token to S3, so we'll go ahead
        # and remove it.
        unset req.http.Warehouse-Token;
    }

    # We no longer need any of these variables, which would exist only to
    # shuffle configuration from the Fastly UI into our VCL.
    unset req.http.Primary-Domain;
    unset req.http.AWS-Access-Key-ID;
    unset req.http.AWS-Secret-Access-Key;
    unset req.http.AWS-Bucket-Name;

    # We have a number of items that we'll pass back to the origin, but only
    # if we have a Warehouse-Token that will allow them to be accepted.
    if (req.http.Warehouse-Token) {
        # Set a header to tell the backend if we're using https or http.
        if (req.http.Fastly-SSL) {
            set req.http.Warehouse-Proto = "https";
        } else {
            set req.http.Warehouse-Proto = "http";
        }

        # Pass the client IP address back to the backend.
        if (req.http.Fastly-Client-IP) {
            set req.http.Warehouse-IP = req.http.Fastly-Client-IP;
        }

        # Pass the real host value back to the backend.
        if (req.http.Host) {
            set req.http.Warehouse-Host = req.http.Host;
        }
    }

    # On a POST, we want to skip the shielding and hit backends directly.
    if (req.request == "POST") {
        set req.backend = F_Heroku;
    }

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

    # We don't ever want to cache our health URL. Outside systems should be
    # able to use it to reach past Fastly and get an end to end health check.
    if (req.url == "/_health/") {
        return(pass);
    }

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

sub vcl_fetch {

    # For any 5xx status code we want to see if a stale object exists for it,
    # if so we'll go ahead and serve it.
    if (beresp.status >= 500 && beresp.status < 600) {
        if (stale.exists) {
            return(deliver_stale);
        }
    }

#FASTLY fetch

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

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

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

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

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

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

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

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

sub vcl_hit {
#FASTLY hit

    # If the object we have isn't cacheable, then just serve it directly
    # without going through any of the caching mechanisms.
    if (!obj.cacheable) {
        return(pass);
    }

    return(deliver);
}

sub vcl_deliver {
    # If this is an error and we have a stale response available, restart so
    # that we can pick it up and serve it.
    if (resp.status >= 500 && resp.status < 600) {
        if (stale.exists) {
            restart;
        }
    }

#FASTLY deliver

    # Unset headers that we don't need/want to send on to the client because
    # they are not generally useful.
    unset resp.http.Server;
    unset resp.http.Via;

    # Unset a few headers set by Amazon that we don't really have a need/desire
    # to send to clients.
    unset resp.http.X-AMZ-Replication-Status;
    unset resp.http.X-AMZ-Meta-Python-Version;
    unset resp.http.X-AMZ-Meta-Version;
    unset resp.http.X-AMZ-Meta-Package-Type;
    unset resp.http.X-AMZ-Meta-Project;

    # Set our standard security headers, we do this in VCL rather than in
    # Warehouse itself so that we always get these headers, regardless of the
    # origin server being used.
    set resp.http.Strict-Transport-Security = "max-age=31536000; includeSubDomains; preload";
    set resp.http.X-Frame-Options = "deny";
    set resp.http.X-XSS-Protection = "1; mode=block";
    set resp.http.X-Content-Type-Options = "nosniff";
    set resp.http.X-Permitted-Cross-Domain-Policies = "none";

    return(deliver);
}

sub vcl_error {
#FASTLY error

    # If we have a 5xx error and there is a stale object available, then we
    # will deliver that stale object.
    if (obj.status >= 500 && obj.status < 600) {
        if (stale.exists) {
            return(deliver_stale);
        }
    }

    if (obj.status == 803) {
        set obj.status = 403;
        set obj.response = "SSL is required";
        set obj.http.Content-Type = "text/plain; charset=UTF-8";
        synthetic {"SSL is required."};
        return (deliver);
    } else if (obj.status == 750) {
        set obj.status = 301;
        set obj.http.Location = req.http.Location;
        set obj.http.Content-Type = "text/html; charset=UTF-8";
        synthetic {"<title>301 Moved Permanently</title>
301 Moved Permanently
"};
        return(deliver);
    }

}

sub vcl_recv {

    # I'm not 100% sure on what this is exactly for, it was taken from the
    # Fastly documentation, however, what I *believe* it does is just ensure
    # that we don't serve a stale copy of the page from the shield node when
    # an edge node is requesting content.
    if (req.http.Fastly-FF) {
        set req.max_stale_while_revalidate = 0s;
    }

    # Some (Older) clients will send a hash fragment as part of the URL even
    # though that is a local only modification. This breaks this badly for the
    # files in S3, and in general it's just not needed.
    set req.url = regsub(req.url, "#.*$", "");

    # Most of the URLs in Warehouse do not support or require any sort of query
    # parameter. If we strip these at the edge then we'll increase our cache
    # efficiency when they won't otherwise change the output of the pages.
    #
    # This will match any URL except those that start with:
    #
    #   * /search/
    #   * /account/login/
    #   * /account/logout/
    #   * /account/register/
    #   * /pypi
    if (req.url !~ "^/(search/|account/(login|logout|register)/|pypi)") {
        set req.url = req.url.path;
    }

    # Sort all of our query parameters, this will ensure that the same query
    # parameters in a different order will end up being represented as the same
    # thing, reducing cache misses due to ordering differences.
    set req.url = boltsort.sort(req.url);

#FASTLY recv

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

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

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

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

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

    # Canonicalize our domains by redirecting any domain that doesn't match our
    # primary domain to our primary domain. We do this *after* the HTTPS check
    # on purpose.
    if (std.tolower(req.http.host) != std.tolower(req.http.Primary-Domain)) {
        set req.http.Location = "https://" req.http.Primary-Domain req.url;
        error 750 "Redirect to Primary Domain";
    }

    # Requests to /packages/ get dispatched to Amazon instead of to our typical
    # Origin. This requires a a bit of setup to make it work.
    if (req.url ~ "^/packages/") {
        # Setup our environment to better match what S3 expects/needs
        set req.http.Host = req.http.AWS-Bucket-Name ".s3.amazonaws.com";
        set req.http.Date = now;
        set req.url = regsuball(req.url, "\+", urlencode("+"));

        # Compute the Authorization header that S3 requires to be able to
        # access the files stored there.
        set req.http.Authorization = "AWS " req.http.AWS-Access-Key-ID ":" digest.hmac_sha1_base64(req.http.AWS-Secret-Access-Key, "GET" LF LF LF req.http.Date LF "/" req.http.AWS-Bucket-Name req.url.path);

        # We don't want to send our Warehouse-Token to S3, so we'll go ahead
        # and remove it.
        unset req.http.Warehouse-Token;
    }

    # We no longer need any of these variables, which would exist only to
    # shuffle configuration from the Fastly UI into our VCL.
    unset req.http.Primary-Domain;
    unset req.http.AWS-Access-Key-ID;
    unset req.http.AWS-Secret-Access-Key;
    unset req.http.AWS-Bucket-Name;

    # We have a number of items that we'll pass back to the origin, but only
    # if we have a Warehouse-Token that will allow them to be accepted.
    if (req.http.Warehouse-Token) {
        # Set a header to tell the backend if we're using https or http.
        if (req.http.Fastly-SSL) {
            set req.http.Warehouse-Proto = "https";
        } else {
            set req.http.Warehouse-Proto = "http";
        }

        # Pass the client IP address back to the backend.
        if (req.http.Fastly-Client-IP) {
            set req.http.Warehouse-IP = req.http.Fastly-Client-IP;
        }

        # Pass the real host value back to the backend.
        if (req.http.Host) {
            set req.http.Warehouse-Host = req.http.Host;
        }
    }

    # On a POST, we want to skip the shielding and hit backends directly.
    if (req.request == "POST") {
        set req.backend = F_Heroku;
    }

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

    # We don't ever want to cache our health URL. Outside systems should be
    # able to use it to reach past Fastly and get an end to end health check.
    if (req.url == "/_health/") {
        return(pass);
    }

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

sub vcl_fetch {

    # For any 5xx status code we want to see if a stale object exists for it,
    # if so we'll go ahead and serve it.
    if (beresp.status >= 500 && beresp.status < 600) {
        if (stale.exists) {
            return(deliver_stale);
        }
    }

#FASTLY fetch

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

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

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

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

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

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

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

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

sub vcl_hit {
#FASTLY hit

    # If the object we have isn't cacheable, then just serve it directly
    # without going through any of the caching mechanisms.
    if (!obj.cacheable) {
        return(pass);
    }

    return(deliver);
}

sub vcl_deliver {
    # If this is an error and we have a stale response available, restart so
    # that we can pick it up and serve it.
    if (resp.status >= 500 && resp.status < 600) {
        if (stale.exists) {
            restart;
        }
    }

#FASTLY deliver

    # Unset headers that we don't need/want to send on to the client because
    # they are not generally useful.
    unset resp.http.Server;
    unset resp.http.Via;

    # Unset a few headers set by Amazon that we don't really have a need/desire
    # to send to clients.
    unset resp.http.X-AMZ-Replication-Status;
    unset resp.http.X-AMZ-Meta-Python-Version;
    unset resp.http.X-AMZ-Meta-Version;
    unset resp.http.X-AMZ-Meta-Package-Type;
    unset resp.http.X-AMZ-Meta-Project;

    # Set our standard security headers, we do this in VCL rather than in
    # Warehouse itself so that we always get these headers, regardless of the
    # origin server being used.
    set resp.http.Strict-Transport-Security = "max-age=31536000; includeSubDomains; preload";
    set resp.http.X-Frame-Options = "deny";
    set resp.http.X-XSS-Protection = "1; mode=block";
    set resp.http.X-Content-Type-Options = "nosniff";
    set resp.http.X-Permitted-Cross-Domain-Policies = "none";

    return(deliver);
}

sub vcl_error {
#FASTLY error

    # If we have a 5xx error and there is a stale object available, then we
    # will deliver that stale object.
    if (obj.status >= 500 && obj.status < 600) {
        if (stale.exists) {
            return(deliver_stale);
        }
    }

    if (obj.status == 803) {
        set obj.status = 403;
        set obj.response = "SSL is required";
        set obj.http.Content-Type = "text/plain; charset=UTF-8";
        synthetic {"SSL is required."};
        return (deliver);
    } else if (obj.status == 750) {
        set obj.status = 301;
        set obj.http.Location = req.http.Location;
        set obj.http.Content-Type = "text/html; charset=UTF-8";
        synthetic {"<title>301 Moved Permanently</title>
301 Moved Permanently
"};
        return(deliver);
    }

}