Added caching 404s for 1 minute

Added caching 404s for 1 minute

###############################################################################
#
# Fastly CDN for Magento 2
#
# NOTICE OF LICENSE
#
# This source file is subject to the Fastly CDN for Magento 2 End User License
# Agreement that is bundled with this package in the file LICENSE_FASTLY_CDN.txt.
#
# @copyright   Copyright (c) 2016 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 2 module.

sub vcl_recv {
#FASTLY recv

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

    # Deactivate gzip
    set req.http.X-Orig-Accept-Encoding = req.http.Accept-Encoding;
    unset req.http.Accept-Encoding;

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

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

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

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

    # geoip lookup
    if (req.url ~ "fastlyCdn/geoip/getaction/") {
        # check if GeoIP has been already processed by client. this normally happens before essential cookies are set.
        if (req.http.cookie ~ "(X-Magento-Vary|form_key)=") {
            error 200 "";
        } else {
            # append parameter with country code
            set req.url = req.url "?country_code=" geoip.country_code;
        }
    }

    return(lookup);
}

sub vcl_fetch {

    if (req.url ~ "^/(pub/)?(media|static)/.*") {
        unset beresp.http.set-cookie;
    }

#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 ~ "^(text\/html|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|text\/xml)\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.X-Orig-Accept-Encoding == "gzip") {
                set beresp.gzip = true;
            }
        }
    }

    # cache only successfully responses and 404s
    if (beresp.status != 200 && beresp.status != 301 && beresp.status != 404) {
        set req.http.Fastly-Cachetype = "ERROR";
        set beresp.ttl = 1s;
        set beresp.grace = 5s;
        return (deliver);
    } elsif (beresp.http.Cache-Control ~ "private") {
        set req.http.Fastly-Cachetype = "PRIVATE";
        return (pass);
    }

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

    # validate if we need to cache it and prevent from setting cookie
    # images, css and js are cacheable by default so we have to remove cookie also
    if (beresp.ttl > 0s && (req.request == "GET" || req.request == "HEAD")) {
        unset beresp.http.set-cookie;
        if (req.url !~ "^/(pub/)?(media|static)/.*") {
            set beresp.http.Pragma = "no-cache";
            set beresp.http.Expires = "-1";
            set beresp.http.Cache-Control = "no-store, no-cache, must-revalidate, max-age=0";
            set beresp.grace = 1m;
        }

        # init surrogate keys
        if (beresp.http.X-Magento-Tags) {
            set beresp.http.Surrogate-Key = beresp.http.X-Magento-Tags " 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);
    }

    # Cache 404's for 1 minute.
    if (beresp.status == 404 && req.url ~ "^/(pub/)?(media|static)/.*") {
        set beresp.ttl = 60s;
    }

    return (deliver);
}

sub vcl_hit {
#FASTLY hit

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

sub vcl_miss {
#FASTLY miss

    return(fetch);
}

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

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

    # 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 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 ~ "X-Magento-Vary=") {
        set req.http.X-Magento-Vary = regsub(req.http.cookie, "^.*?X-Magento-Vary=([^;]+);*.*$", "\1");
        set req.hash += req.http.X-Magento-Vary;
        unset req.http.X-Magento-Vary;
    }

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

    ### {{ design_exceptions_code }} ###

    return (hash);
}

###############################################################################
#
# Fastly CDN for Magento 2
#
# NOTICE OF LICENSE
#
# This source file is subject to the Fastly CDN for Magento 2 End User License
# Agreement that is bundled with this package in the file LICENSE_FASTLY_CDN.txt.
#
# @copyright   Copyright (c) 2016 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 2 module.

sub vcl_recv {
#FASTLY recv

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

    # Deactivate gzip
    set req.http.X-Orig-Accept-Encoding = req.http.Accept-Encoding;
    unset req.http.Accept-Encoding;

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

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

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

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

    # geoip lookup
    if (req.url ~ "fastlyCdn/geoip/getaction/") {
        # check if GeoIP has been already processed by client. this normally happens before essential cookies are set.
        if (req.http.cookie ~ "(X-Magento-Vary|form_key)=") {
            error 200 "";
        } else {
            # append parameter with country code
            set req.url = req.url "?country_code=" geoip.country_code;
        }
    }

    return(lookup);
}

sub vcl_fetch {

    if (req.url ~ "^/(pub/)?(media|static)/.*") {
        unset beresp.http.set-cookie;
    }

#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 ~ "^(text\/html|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|text\/xml)\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.X-Orig-Accept-Encoding == "gzip") {
                set beresp.gzip = true;
            }
        }
    }

    # cache only successfully responses and 404s
    if (beresp.status != 200 && beresp.status != 301 && beresp.status != 404) {
        set req.http.Fastly-Cachetype = "ERROR";
        set beresp.ttl = 1s;
        set beresp.grace = 5s;
        return (deliver);
    } elsif (beresp.http.Cache-Control ~ "private") {
        set req.http.Fastly-Cachetype = "PRIVATE";
        return (pass);
    }

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

    # validate if we need to cache it and prevent from setting cookie
    # images, css and js are cacheable by default so we have to remove cookie also
    if (beresp.ttl > 0s && (req.request == "GET" || req.request == "HEAD")) {
        unset beresp.http.set-cookie;
        if (req.url !~ "^/(pub/)?(media|static)/.*") {
            set beresp.http.Pragma = "no-cache";
            set beresp.http.Expires = "-1";
            set beresp.http.Cache-Control = "no-store, no-cache, must-revalidate, max-age=0";
            set beresp.grace = 1m;
        }

        # init surrogate keys
        if (beresp.http.X-Magento-Tags) {
            set beresp.http.Surrogate-Key = beresp.http.X-Magento-Tags " 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);
    }

    return (deliver);
}

sub vcl_hit {
#FASTLY hit

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

sub vcl_miss {
#FASTLY miss

    return(fetch);
}

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

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

    # 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 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 ~ "X-Magento-Vary=") {
        set req.http.X-Magento-Vary = regsub(req.http.cookie, "^.*?X-Magento-Vary=([^;]+);*.*$", "\1");
        set req.hash += req.http.X-Magento-Vary;
        unset req.http.X-Magento-Vary;
    }

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

    ### {{ design_exceptions_code }} ###

    return (hash);
}


Put comments in the default VCL to make people stop frobbing stuff
we don't support yet.

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

/*-
 * Copyright (c) 2006 Verdens Gang AS
 * Copyright (c) 2006-2008 Linpro AS
 * All rights reserved.
 *
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $Id$
 *
 * The default VCL code.
 *
 * NB! You do NOT need to copy & paste all of this into your won vcl code,
 * if you do not handle one of the functions, the compiler will automaticall
 * fall back to the default code.
 *
 * This code will be prefixed with a backend declaration built from the
 * -b argument.
 */

sub vcl_recv {
    if (req.request != "GET" &&
      req.request != "HEAD" &&
      req.request != "PUT" &&
      req.request != "POST" &&
      req.request != "TRACE" &&
      req.request != "OPTIONS" &&
      req.request != "DELETE") {
        /* Non-RFC2616 or CONNECT which is weird. */
        pipe;
    }
    if (req.http.Expect) {
        /* Expect is just too hard at present. */
        pipe;
    }
    if (req.request != "GET" && req.request != "HEAD") {
        /* We only deal with GET and HEAD by default */
        pass;
    }
    if (req.http.Authorization || req.http.Cookie) {
        /* Not cacheable by default */
        pass;
    }
    lookup;
}

sub vcl_pipe {
    pipe;
}

sub vcl_pass {
    pass;
}

sub vcl_hash {
    set req.hash += req.url;
    if (req.http.host) {
        set req.hash += req.http.host;
    } else {
        set req.hash += server.ip;
    }
    hash;
}

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

sub vcl_miss {
    fetch;
}

sub vcl_fetch {
    if (!obj.cacheable) {
        pass;
    }
    if (obj.http.Set-Cookie) {
        pass;
    }
    set obj.prefetch =  -30s;
    deliver;
}

sub vcl_deliver {
    deliver;
}

sub vcl_discard {
    /* XXX: Do not redefine vcl_discard{}, it is not yet supported */
    discard;
}

sub vcl_prefetch {
    /* XXX: Do not redefine vcl_prefetch{}, it is not yet supported */
    fetch;
}

sub vcl_timeout {
    /* XXX: Do not redefine vcl_timeout{}, it is not yet supported */
    discard;
}

sub vcl_error {
    set obj.http.Content-Type = "text/html; charset=utf-8";
    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 {"

    <address>Varnish</address>

"};
    deliver;
}

/*-
 * Copyright (c) 2006 Verdens Gang AS
 * Copyright (c) 2006-2008 Linpro AS
 * All rights reserved.
 *
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $Id$
 *
 * The default VCL code.
 *
 * NB! You do NOT need to copy & paste all of this into your won vcl code,
 * if you do not handle one of the functions, the compiler will automaticall
 * fall back to the default code.
 *
 * This code will be prefixed with a backend declaration built from the
 * -b argument.
 */

sub vcl_recv {
    if (req.request != "GET" &&
      req.request != "HEAD" &&
      req.request != "PUT" &&
      req.request != "POST" &&
      req.request != "TRACE" &&
      req.request != "OPTIONS" &&
      req.request != "DELETE") {
        /* Non-RFC2616 or CONNECT which is weird. */
        pipe;
    }
    if (req.http.Expect) {
        /* Expect is just too hard at present. */
        pipe;
    }
    if (req.request != "GET" && req.request != "HEAD") {
        /* We only deal with GET and HEAD by default */
        pass;
    }
    if (req.http.Authorization || req.http.Cookie) {
        /* Not cacheable by default */
        pass;
    }
    lookup;
}

sub vcl_pipe {
    pipe;
}

sub vcl_pass {
    pass;
}

sub vcl_hash {
    set req.hash += req.url;
    if (req.http.host) {
        set req.hash += req.http.host;
    } else {
        set req.hash += server.ip;
    }
    hash;
}

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

sub vcl_miss {
    fetch;
}

sub vcl_fetch {
    if (!obj.cacheable) {
        pass;
    }
    if (obj.http.Set-Cookie) {
        pass;
    }
    set obj.prefetch =  -30s;
    deliver;
}

sub vcl_deliver {
    deliver;
}

sub vcl_discard {
    discard;
}

sub vcl_prefetch {
    fetch;
}

sub vcl_timeout {
    discard;
}

sub vcl_error {
    set obj.http.Content-Type = "text/html; charset=utf-8";
    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 {"

    <address>Varnish</address>

"};
    deliver;
}


Added logic to set a TTL for 404s.

Added logic to set a TTL for 404s.

###############################################################################
#
# Fastly CDN for Magento 2
#
# NOTICE OF LICENSE
#
# This source file is subject to the Fastly CDN for Magento 2 End User License
# Agreement that is bundled with this package in the file LICENSE_FASTLY_CDN.txt.
#
# @copyright   Copyright (c) 2016 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 2 module.

sub vcl_recv {
#FASTLY recv

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

    # Deactivate gzip
    set req.http.X-Orig-Accept-Encoding = req.http.Accept-Encoding;
    unset req.http.Accept-Encoding;

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

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

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

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

    # geoip lookup
    if (req.url ~ "fastlyCdn/geoip/getaction/") {
        # check if GeoIP has been already processed by client. this normally happens before essential cookies are set.
        if (req.http.cookie ~ "(X-Magento-Vary|form_key)=") {
            error 200 "";
        } else {
            # append parameter with country code
            set req.url = req.url "?country_code=" geoip.country_code;
        }
    }

    return(lookup);
}

sub vcl_fetch {

    # Remove Set-Cookies from responses for static content
    # to match the cookie removal in recv.
    if (req.url ~ "^/(pub/)?(media|static)/.*") {
        unset beresp.http.set-cookie;

        # Set a short TTL for 404's
        if (beresp.status == 404) {
            set beresp.ttl = 300s;
        }

    }

#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 ~ "^(text\/html|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|text\/xml)\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.X-Orig-Accept-Encoding == "gzip") {
                set beresp.gzip = true;
            }
        }
    }

    # cache only successfully responses and 404s
    if (beresp.status != 200 && beresp.status != 301 && beresp.status != 404) {
        set req.http.Fastly-Cachetype = "ERROR";
        set beresp.ttl = 1s;
        set beresp.grace = 5s;
        return (deliver);
    } elsif (beresp.http.Cache-Control ~ "private") {
        set req.http.Fastly-Cachetype = "PRIVATE";
        return (pass);
    }

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

    # validate if we need to cache it and prevent from setting cookie
    # images, css and js are cacheable by default so we have to remove cookie also
    if (beresp.ttl > 0s && (req.request == "GET" || req.request == "HEAD")) {
        unset beresp.http.set-cookie;
        if (req.url !~ "^/(pub/)?(media|static)/.*") {
            set beresp.http.Pragma = "no-cache";
            set beresp.http.Expires = "-1";
            set beresp.http.Cache-Control = "no-store, no-cache, must-revalidate, max-age=0";
            set beresp.grace = 1m;
        }

        # init surrogate keys
        if (beresp.http.X-Magento-Tags) {
            set beresp.http.Surrogate-Key = beresp.http.X-Magento-Tags " 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);
    }

    return (deliver);
}

sub vcl_hit {
#FASTLY hit

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

sub vcl_miss {
#FASTLY miss

    return(fetch);
}

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

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

    # 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 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 ~ "X-Magento-Vary=") {
        set req.http.X-Magento-Vary = regsub(req.http.cookie, "^.*?X-Magento-Vary=([^;]+);*.*$", "\1");
        set req.hash += req.http.X-Magento-Vary;
        unset req.http.X-Magento-Vary;
    }

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

    ### {{ design_exceptions_code }} ###

    return (hash);
}

###############################################################################
#
# Fastly CDN for Magento 2
#
# NOTICE OF LICENSE
#
# This source file is subject to the Fastly CDN for Magento 2 End User License
# Agreement that is bundled with this package in the file LICENSE_FASTLY_CDN.txt.
#
# @copyright   Copyright (c) 2016 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 2 module.

sub vcl_recv {
#FASTLY recv

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

    # Deactivate gzip
    set req.http.X-Orig-Accept-Encoding = req.http.Accept-Encoding;
    unset req.http.Accept-Encoding;

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

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

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

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

    # geoip lookup
    if (req.url ~ "fastlyCdn/geoip/getaction/") {
        # check if GeoIP has been already processed by client. this normally happens before essential cookies are set.
        if (req.http.cookie ~ "(X-Magento-Vary|form_key)=") {
            error 200 "";
        } else {
            # append parameter with country code
            set req.url = req.url "?country_code=" geoip.country_code;
        }
    }

    return(lookup);
}

sub vcl_fetch {

    if (req.url ~ "^/(pub/)?(media|static)/.*") {
        unset beresp.http.set-cookie;
    }

#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 ~ "^(text\/html|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|text\/xml)\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.X-Orig-Accept-Encoding == "gzip") {
                set beresp.gzip = true;
            }
        }
    }

    # cache only successfully responses and 404s
    if (beresp.status != 200 && beresp.status != 301 && beresp.status != 404) {
        set req.http.Fastly-Cachetype = "ERROR";
        set beresp.ttl = 1s;
        set beresp.grace = 5s;
        return (deliver);
    } elsif (beresp.http.Cache-Control ~ "private") {
        set req.http.Fastly-Cachetype = "PRIVATE";
        return (pass);
    }

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

    # validate if we need to cache it and prevent from setting cookie
    # images, css and js are cacheable by default so we have to remove cookie also
    if (beresp.ttl > 0s && (req.request == "GET" || req.request == "HEAD")) {
        unset beresp.http.set-cookie;
        if (req.url !~ "^/(pub/)?(media|static)/.*") {
            set beresp.http.Pragma = "no-cache";
            set beresp.http.Expires = "-1";
            set beresp.http.Cache-Control = "no-store, no-cache, must-revalidate, max-age=0";
            set beresp.grace = 1m;
        }

        # init surrogate keys
        if (beresp.http.X-Magento-Tags) {
            set beresp.http.Surrogate-Key = beresp.http.X-Magento-Tags " 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);
    }

    # Cache 404's for 1 minute.
    if (beresp.status == 404 && req.url ~ "^/(pub/)?(media|static)/.*") {
        set beresp.ttl = 60s;
    }

    return (deliver);
}

sub vcl_hit {
#FASTLY hit

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

sub vcl_miss {
#FASTLY miss

    return(fetch);
}

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

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

    # 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 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 ~ "X-Magento-Vary=") {
        set req.http.X-Magento-Vary = regsub(req.http.cookie, "^.*?X-Magento-Vary=([^;]+);*.*$", "\1");
        set req.hash += req.http.X-Magento-Vary;
        unset req.http.X-Magento-Vary;
    }

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

    ### {{ design_exceptions_code }} ###

    return (hash);
}


Do not trow away existing headers listed in Vary:

C{

/* ------------------------------------------------------------------ */
/* THIS FILE IS AUTOMATICALLY GENERATED BY ./gen_vcl.pl. DO NOT EDIT. */

/*
 * Accept-language header normalization
 *
 * Cosimo, 21/01/2010
 *
 */

#include <ctype.h>  /* isupper */
#include <stdio.h>
#include <stdlib.h> /* qsort */
#include <string.h>

#define DEFAULT_LANGUAGE "en"
#define SUPPORTED_LANGUAGES ":de:fr:es:ru:pt:hy:en:"

#define vcl_string char
#define LANG_LIST_SIZE 16
#define LANG_MAXLEN 16
#define RETURN_LANG(x) { \
    strncpy(lang, x, LANG_MAXLEN); \
    return; \
}
#define RETURN_DEFAULT_LANG RETURN_LANG(DEFAULT_LANGUAGE)
#define PUSH_LANG(x,y) { \
    /* fprintf(stderr, "Pushing lang [%d] %s %.4f\n", curr_lang, x, y); */ \
    /* We have to copy, otherwise root_lang will be the same every time */ \
    strncpy(pl[curr_lang].lang, x, LANG_MAXLEN); \
    pl[curr_lang].q = y;       \
    curr_lang++;               \
}

struct lang_list {
    vcl_string lang[LANG_MAXLEN];
    float q;
};

/* In-place lowercase of a string */
static void strtolower(char *s) {
    register char *c;
    for (c=s; *c; c++) {
        if (isupper(*c)) {
            *c = tolower(*c);
        }
    }
    return;
}

/* Checks if a given language is in the static list of the ones we support */
int is_supported(vcl_string *lang) {
    vcl_string *supported_languages = SUPPORTED_LANGUAGES;
    vcl_string match_str[LANG_MAXLEN + 3] = "";  /* :, :, \0 = 3 */
    int is_supported = 0;

    /* We want to match 'zh-cn' and 'zh-CN' too */
    strtolower(lang);

    /* Search ":<lang>:" in supported languages string */
    strncpy(match_str, ":", 1);
    strncat(match_str, lang, LANG_MAXLEN);
    strncat(match_str, ":\0", 2);

    if (strstr(supported_languages, match_str)) {
        is_supported = 1;
    }

    return is_supported;
}

/* Used by qsort() below */
int sort_by_q(const void *x, const void *y) {
    struct lang_list *a = (struct lang_list *)x;
    struct lang_list *b = (struct lang_list *)y;
    if (a->q > b->q) return -1;
    if (a->q < b->q) return 1;
    return 0;
}

/* Reads Accept-Language, parses it, and finds the first match
   among the supported languages. In case of no match,
   returns the default language.
*/
void select_language(const vcl_string *incoming_header, char *lang) {

    struct lang_list pl[LANG_LIST_SIZE];
    vcl_string *lang_tok = NULL;
    vcl_string root_lang[3];
    vcl_string *header;
    vcl_string *pos = NULL;
    vcl_string *q_spec = NULL;
    unsigned int curr_lang = 0, i = 0;
    float q;

    /* Empty or default string, return default language immediately */
    if (
        !incoming_header
        || (0 == strcmp(incoming_header, "en-US"))
        || (0 == strcmp(incoming_header, "en-GB"))
        || (0 == strcmp(incoming_header, DEFAULT_LANGUAGE))
        || (0 == strcmp(incoming_header, ""))
    )
        RETURN_DEFAULT_LANG;

    /* Tokenize Accept-Language */
    header = (vcl_string *) incoming_header;

    while ((lang_tok = strtok_r(header, " ,", &pos))) {

        q = 1.0;

        if ((q_spec = strstr(lang_tok, ";q="))) {
            /* Truncate language name before ';' */
            *q_spec = '\0';
            /* Get q value */
            sscanf(q_spec + 3, "%f", &q);
        }

        /* Wildcard language '*' should be last in list */
        if ((*lang_tok) == '*') q = 0.0;

        /* Push in the prioritized list */
        PUSH_LANG(lang_tok, q);

        /* For cases like 'en-GB', we also want the root language in the final list */
        if ('-' == lang_tok[2]) {
            root_lang[0] = lang_tok[0];
            root_lang[1] = lang_tok[1];
            root_lang[2] = '\0';
            PUSH_LANG(root_lang, q - 0.001);
        }

        /* For strtok_r() to proceed from where it left off */
        header = NULL;

        /* Break out if stored max no. of languages */
        if (curr_lang >= LANG_MAXLEN) break;
    }

    /* Sort by priority */
    qsort(pl, curr_lang, sizeof(struct lang_list), &sort_by_q);

    /* Match with supported languages */
    for (i = 0; i < curr_lang; i++) {
        if (is_supported(pl[i].lang))
            RETURN_LANG(pl[i].lang);
    }

    RETURN_DEFAULT_LANG;
}

/* Reads req.http.Accept-Language and writes X-Varnish-Accept-Language */
void vcl_rewrite_accept_language(const struct sess *sp) {
    vcl_string *in_hdr;
    vcl_string lang[LANG_MAXLEN];

    memset(lang, 0, LANG_MAXLEN);

    /* Get Accept-Language header from client */
    in_hdr = VRT_GetHdr(sp, HDR_REQ, "\020Accept-Language:");

    /* Normalize and filter out by list of supported languages */
    select_language(in_hdr, lang);

    /* By default, use a different header name: don't mess with backend logic */
    VRT_SetHdr(sp, HDR_REQ, "\032X-Varnish-Accept-Language:", lang, vrt_magic_string_end);

    return;
}

/* vim: syn=c ts=4 et sts=4 sw=4 tw=0
*/

/* THIS FILE IS AUTOMATICALLY GENERATED BY ./gen_vcl.pl. DO NOT EDIT. */
/* ------------------------------------------------------------------ */
}C

sub vcl_recv {
  C{
    vcl_rewrite_accept_language(sp);
  }C
}

sub vcl_fetch {
  if (beresp.http.Vary) {
    set beresp.http.Vary = beresp.http.Vary ", X-Varnish-Accept-Language";
  } else {
    set beresp.http.Vary = "X-Varnish-Accept-Language";
  }
}

C{

/* ------------------------------------------------------------------ */
/* THIS FILE IS AUTOMATICALLY GENERATED BY ./gen_vcl.pl. DO NOT EDIT. */

/*
 * Accept-language header normalization
 *
 * Cosimo, 21/01/2010
 *
 */

#include <ctype.h>  /* isupper */
#include <stdio.h>
#include <stdlib.h> /* qsort */
#include <string.h>

#define DEFAULT_LANGUAGE "en"
#define SUPPORTED_LANGUAGES ":de:fr:es:ru:pt:hy:en:"

#define vcl_string char
#define LANG_LIST_SIZE 16
#define LANG_MAXLEN 16
#define RETURN_LANG(x) { \
    strncpy(lang, x, LANG_MAXLEN); \
    return; \
}
#define RETURN_DEFAULT_LANG RETURN_LANG(DEFAULT_LANGUAGE)
#define PUSH_LANG(x,y) { \
    /* fprintf(stderr, "Pushing lang [%d] %s %.4f\n", curr_lang, x, y); */ \
    /* We have to copy, otherwise root_lang will be the same every time */ \
    strncpy(pl[curr_lang].lang, x, LANG_MAXLEN); \
    pl[curr_lang].q = y;       \
    curr_lang++;               \
}

struct lang_list {
    vcl_string lang[LANG_MAXLEN];
    float q;
};

/* In-place lowercase of a string */
static void strtolower(char *s) {
    register char *c;
    for (c=s; *c; c++) {
        if (isupper(*c)) {
            *c = tolower(*c);
        }
    }
    return;
}

/* Checks if a given language is in the static list of the ones we support */
int is_supported(vcl_string *lang) {
    vcl_string *supported_languages = SUPPORTED_LANGUAGES;
    vcl_string match_str[LANG_MAXLEN + 3] = "";  /* :, :, \0 = 3 */
    int is_supported = 0;

    /* We want to match 'zh-cn' and 'zh-CN' too */
    strtolower(lang);

    /* Search ":<lang>:" in supported languages string */
    strncpy(match_str, ":", 1);
    strncat(match_str, lang, LANG_MAXLEN);
    strncat(match_str, ":\0", 2);

    if (strstr(supported_languages, match_str)) {
        is_supported = 1;
    }

    return is_supported;
}

/* Used by qsort() below */
int sort_by_q(const void *x, const void *y) {
    struct lang_list *a = (struct lang_list *)x;
    struct lang_list *b = (struct lang_list *)y;
    if (a->q > b->q) return -1;
    if (a->q < b->q) return 1;
    return 0;
}

/* Reads Accept-Language, parses it, and finds the first match
   among the supported languages. In case of no match,
   returns the default language.
*/
void select_language(const vcl_string *incoming_header, char *lang) {

    struct lang_list pl[LANG_LIST_SIZE];
    vcl_string *lang_tok = NULL;
    vcl_string root_lang[3];
    vcl_string *header;
    vcl_string *pos = NULL;
    vcl_string *q_spec = NULL;
    unsigned int curr_lang = 0, i = 0;
    float q;

    /* Empty or default string, return default language immediately */
    if (
        !incoming_header
        || (0 == strcmp(incoming_header, "en-US"))
        || (0 == strcmp(incoming_header, "en-GB"))
        || (0 == strcmp(incoming_header, DEFAULT_LANGUAGE))
        || (0 == strcmp(incoming_header, ""))
    )
        RETURN_DEFAULT_LANG;

    /* Tokenize Accept-Language */
    header = (vcl_string *) incoming_header;

    while ((lang_tok = strtok_r(header, " ,", &pos))) {

        q = 1.0;

        if ((q_spec = strstr(lang_tok, ";q="))) {
            /* Truncate language name before ';' */
            *q_spec = '\0';
            /* Get q value */
            sscanf(q_spec + 3, "%f", &q);
        }

        /* Wildcard language '*' should be last in list */
        if ((*lang_tok) == '*') q = 0.0;

        /* Push in the prioritized list */
        PUSH_LANG(lang_tok, q);

        /* For cases like 'en-GB', we also want the root language in the final list */
        if ('-' == lang_tok[2]) {
            root_lang[0] = lang_tok[0];
            root_lang[1] = lang_tok[1];
            root_lang[2] = '\0';
            PUSH_LANG(root_lang, q - 0.001);
        }

        /* For strtok_r() to proceed from where it left off */
        header = NULL;

        /* Break out if stored max no. of languages */
        if (curr_lang >= LANG_MAXLEN) break;
    }

    /* Sort by priority */
    qsort(pl, curr_lang, sizeof(struct lang_list), &sort_by_q);

    /* Match with supported languages */
    for (i = 0; i < curr_lang; i++) {
        if (is_supported(pl[i].lang))
            RETURN_LANG(pl[i].lang);
    }

    RETURN_DEFAULT_LANG;
}

/* Reads req.http.Accept-Language and writes X-Varnish-Accept-Language */
void vcl_rewrite_accept_language(const struct sess *sp) {
    vcl_string *in_hdr;
    vcl_string lang[LANG_MAXLEN];

    memset(lang, 0, LANG_MAXLEN);

    /* Get Accept-Language header from client */
    in_hdr = VRT_GetHdr(sp, HDR_REQ, "\020Accept-Language:");

    /* Normalize and filter out by list of supported languages */
    select_language(in_hdr, lang);

    /* By default, use a different header name: don't mess with backend logic */
    VRT_SetHdr(sp, HDR_REQ, "\032X-Varnish-Accept-Language:", lang, vrt_magic_string_end);

    return;
}

/* vim: syn=c ts=4 et sts=4 sw=4 tw=0
*/

/* THIS FILE IS AUTOMATICALLY GENERATED BY ./gen_vcl.pl. DO NOT EDIT. */
/* ------------------------------------------------------------------ */
}C

sub vcl_recv {
  C{
    vcl_rewrite_accept_language(sp);
  }C
}

sub vcl_fetch {
  set beresp.http.Vary = "X-Varnish-Accept-Language";
}


Our current VCL logic picks out the first value of FASTLY_CDN cookie and
replaces it in the esi_data query string. This is incorrect. We need to find out
what value of the esi_data is being asked for e.g. message, customer-quote find
the matching cookie (if it exists) then replace that value inside esi_data.

This patch does that ie.
1. Identifies the esi_data cookie being asked for
2. Parses the value out

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

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

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

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

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

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

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

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

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

    # formkey lookup
    if (req.url.path ~ "/fastlycdn/getformkey/") {
        # check if we have a formkey cookie
        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 client.geo.longitude client.geo.latitude client.geo.postal_code;
            set req.http.Formkey = regsub(digest.hash_md5(req.http.seed), "^0x", "");
        }
        error 760 req.http.Formkey;
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

    # formkey lookup
    if (req.url.path ~ "/fastlycdn/getformkey/") {
        # check if we have a formkey cookie
        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 client.geo.longitude client.geo.latitude client.geo.postal_code;
            set req.http.Formkey = regsub(digest.hash_md5(req.http.seed), "^0x", "");
        }
        error 760 req.http.Formkey;
    }

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

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

    # check for ESI calls
    if (req.url.qs ~ "esi_data=") {
        # check for valid cookie data
        if (req.http.Cookie ~ "FASTLY_CDN-([A-Za-z0-9-_]+)=([^;]*)") {
            set req.url = querystring.filter(req.url, "esi_data") + "&esi_data=" + re.group.2;
        }
    }

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


changed to correct hostname

changed to correct hostname

# This is a the VCL configuration file for 3scale's varnish plugin.
#
# Default backend definition.  Set this to point to the 3scale's
# backend.
#
# set the beresp.ttl in vcl_fetch to have a default global TTL
# you can define custom TTL via regular expressions
#
# Integration mode: API proxy.
# Traffic from API consumers will go through Varnish
# Varnish will check with 3scale backend for API call authorization and reporting
# (see https://support.3scale.net/howtos/api-configuration/varnish)

# Replace placeholders at lines 18, 19 and 42.

## the backend of your API
backend backend_api {
  .host = "YOUR_API_ENDPOINT";
  .port = "YOUR_API_ENDPOINT";
}

## real 3scale backend
backend backend_3scale {
  .host = "su1.3scale.net";
  .port = "80";
}

import std;
import threescale;

sub vcl_recv {

  unset req.http.cookie;

  if (!req.http.X-3scale-authorization) {
    ## this request was send by the end-user
    set req.backend = backend_api;

    ## need to generate the string for the 3scale authorization,
    set req.http.X-3scale-authrep = "/transactions/authrep.xml?provider_key=";
    ## set your 3scale provider_key
    ## set req.http.X-3scale-authrep = req.http.X-3scale-authrep + "YOUR_PROVIDER_KEY";

    ## extract the parameters sent by the user
    set req.http.X-3scale-app_id = regsub(req.url,".*[&?](app_id.[^&]*).*","\1");
    set req.http.X-3scale-app_key = regsub(req.url,".*[&?](app_key.[^&]*).*","\1");
    set req.http.X-3scale-user_key = regsub(req.url,".*[&?](user_key.[^&]*).*","\1");
    set req.http.X-3scale-user_id = regsub(req.url,".*[&?](user_id.[^&]*).*","\1");

    if (req.http.X-3scale-app_id != req.url) {
      set req.http.X-3scale-authrep = req.http.X-3scale-authrep + "&" + req.http.X-3scale-app_id;
    }

    if (req.http.X-3scale-app_key != req.url ) {
      set req.http.X-3scale-authrep = req.http.X-3scale-authrep + "&" + req.http.X-3scale-app_key;
    }

    if (req.http.X-3scale-user_id != req.url) {
      set req.http.X-3scale-authrep = req.http.X-3scale-authrep + "&" + req.http.X-3scale-user_id;
    }

    if (req.http.X-3scale-user_key != req.url) {
      set req.http.X-3scale-authrep = req.http.X-3scale-authrep + "&" + req.http.X-3scale-user_key;
    }

    unset req.http.X-3scale-app_id;
    unset req.http.X-3scale-app_key;
    unset req.http.X-3scale-user_id;
    unset req.http.X-3scale-user_key;

    ## set the appropriate usage in a similar why than above, the basic one is...
    set req.http.X-3scale-authrep = req.http.X-3scale-authrep + "&usage[hits]=1";

    ## now the additional paramters, for instance if you want to pass the url path
    set req.http.X-3scale-authrep = req.http.X-3scale-authrep + "&object=" + req.url;

    set req.http.X-3scale-authrep-result = threescale.send_get_request("su1.3scale.net","80",req.http.X-3scale-authrep,"X-3scale-authorization: true;");

    if (req.http.X-3scale-authrep-result == "200") {
      ## the request from the user has been authorized, proceed as normal

      if (req.request != "GET" && req.request != "HEAD") {
        return(pass);
      }
      else {
        return(lookup);
      }

    }
    else {
      ## the request from the users has not been authorized!! Returning an error
      ## FIXME: convert req.http.X-3scale-authrep-result; to int
      error 401;
    }

    unset req.http.X-3scale-authrep;
    unset req.http.X-3scale-authrep-result;

  }
  else {
    ## the request was not send by the end-user but by varnish itself
    set req.backend = backend_3scale;
    set req.http.host = "su1.3scale.net";

    if (req.request != "GET" && req.request != "HEAD") {
      std.syslog(0,"This should never happen!!");
    }
    else {
      return(lookup);
    }

  }

}

# the hash of the request needs to be customized to remove all fields that are not
# associated to the user

sub vcl_hash {

  if (req.http.X-3scale-authorization) {
    ## remove no_body
    set req.http.X-url-tmp = regsub(req.url,"[&?]no_body.[^&]*","");
    ## remove object_id
    set req.http.X-url-tmp = regsub(req.http.X-url-tmp,"[&?]object.[^&]*","");
    ## remove object
    set req.http.X-url-tmp = regsub(req.http.X-url-tmp,"[&?]object_id.[^&]*","");

    hash_data(req.http.X-url-tmp);
    unset req.http.X-url-tmp;
  }
  else {
    ## the default has of varnish for the end-user request, however we better remove the
    ## parameters specific to 3scale from the end-user request. If you want to remove other
    ## parameters from the original request so that they are not taken into account for the
    ## cache this is the place.

    set req.http.X-url-tmp = regsub(req.url,"[&?]user_key.[^&]*","");
    set req.http.X-url-tmp = regsub(req.url,"[&?]user_id.[^&]*","");
    set req.http.X-url-tmp = regsub(req.url,"[&?]app_key.[^&]*","");
    set req.http.X-url-tmp = regsub(req.url,"[&?]app_id.[^&]*","");

    hash_data(req.http.X-url-tmp);

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

	  unset req.http.X-url-tmp;

  }

  return (hash);

}

#A HIT means that the request to your local cache of 3scale is still fresh and the
#response will not have to fetch the data from the remote 3scale backend.
#vmod_3scale plugin will send a replica of the call asynchronously to 3scale's remote
#backend on its own

sub vcl_hit {

  if (req.http.X-3scale-authorization) {
    if (req.url ~ "^/transactions/authrep.xml\?") {
	    if (threescale.send_get_request_threaded("su1.3scale.net","80",req.url,"")==0) {}
    }
  }

}

sub vcl_fetch {

  if (req.http.X-3scale-authorization) {
    ## cache the authorization from 3scale
  	set beresp.ttl = 30s;
  }
  else {
    ## cache of the content of your API
	  set beresp.ttl = 60s;
  }

}

sub vcl_deliver {
  ## remove control headers
  unset req.http.X-3scale-authrep;
}

sub vcl_error {
  set obj.http.Content-Type = "text/html; charset=utf-8";
  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>

Authorization error trying to access this API

Did you forget to send your credentials?

Are you over the limits of your account?

Please go to your account at 3scale to find out

"};
  return (deliver);
}

# This is a the VCL configuration file for 3scale's varnish plugin.
#
# Default backend definition.  Set this to point to the 3scale's
# backend.
#
# set the beresp.ttl in vcl_fetch to have a default global TTL
# you can define custom TTL via regular expressions
#
# Integration mode: API proxy.
# Traffic from API consumers will go through Varnish
# Varnish will check with 3scale backend for API call authorization and reporting
# (see https://support.3scale.net/howtos/api-configuration/varnish)

# Replace placeholders at lines 18, 19 and 42.

## the backend of your API
backend backend_api {
  .host = "YOUR_API_ENDPOINT";
  .port = "YOUR_API_ENDPOINT";
}

## real 3scale backend
backend backend_3scale {
  .host = "su1.3scale.net";
  .port = "80";
}

import std;
import threescale;

sub vcl_recv {

  unset req.http.cookie;

  if (!req.http.X-3scale-authorization) {
    ## this request was send by the end-user
    set req.backend = backend_api;

    ## need to generate the string for the 3scale authorization,
    set req.http.X-3scale-authrep = "/transactions/authrep.xml?provider_key=";
    ## set your 3scale provider_key
    ## set req.http.X-3scale-authrep = req.http.X-3scale-authrep + "YOUR_PROVIDER_KEY";

    ## extract the parameters sent by the user
    set req.http.X-3scale-app_id = regsub(req.url,".*[&?](app_id.[^&]*).*","\1");
    set req.http.X-3scale-app_key = regsub(req.url,".*[&?](app_key.[^&]*).*","\1");
    set req.http.X-3scale-user_key = regsub(req.url,".*[&?](user_key.[^&]*).*","\1");
    set req.http.X-3scale-user_id = regsub(req.url,".*[&?](user_id.[^&]*).*","\1");

    if (req.http.X-3scale-app_id != req.url) {
      set req.http.X-3scale-authrep = req.http.X-3scale-authrep + "&" + req.http.X-3scale-app_id;
    }

    if (req.http.X-3scale-app_key != req.url ) {
      set req.http.X-3scale-authrep = req.http.X-3scale-authrep + "&" + req.http.X-3scale-app_key;
    }

    if (req.http.X-3scale-user_id != req.url) {
      set req.http.X-3scale-authrep = req.http.X-3scale-authrep + "&" + req.http.X-3scale-user_id;
    }

    if (req.http.X-3scale-user_key != req.url) {
      set req.http.X-3scale-authrep = req.http.X-3scale-authrep + "&" + req.http.X-3scale-user_key;
    }

    unset req.http.X-3scale-app_id;
    unset req.http.X-3scale-app_key;
    unset req.http.X-3scale-user_id;
    unset req.http.X-3scale-user_key;

    ## set the appropriate usage in a similar why than above, the basic one is...
    set req.http.X-3scale-authrep = req.http.X-3scale-authrep + "&usage[hits]=1";

    ## now the additional paramters, for instance if you want to pass the url path
    set req.http.X-3scale-authrep = req.http.X-3scale-authrep + "&object=" + req.url;

    set req.http.X-3scale-authrep-result = threescale.send_get_request("localhost","80",req.http.X-3scale-authrep,"X-3scale-authorization: true;");

    if (req.http.X-3scale-authrep-result == "200") {
      ## the request from the user has been authorized, proceed as normal

      if (req.request != "GET" && req.request != "HEAD") {
        return(pass);
      }
      else {
        return(lookup);
      }

    }
    else {
      ## the request from the users has not been authorized!! Returning an error
      ## FIXME: convert req.http.X-3scale-authrep-result; to int
      error 401;
    }

    unset req.http.X-3scale-authrep;
    unset req.http.X-3scale-authrep-result;

  }
  else {
    ## the request was not send by the end-user but by varnish itself
    set req.backend = backend_3scale;
    set req.http.host = "su1.3scale.net";

    if (req.request != "GET" && req.request != "HEAD") {
      std.syslog(0,"This should never happen!!");
    }
    else {
      return(lookup);
    }

  }

}

# the hash of the request needs to be customized to remove all fields that are not
# associated to the user

sub vcl_hash {

  if (req.http.X-3scale-authorization) {
    ## remove no_body
    set req.http.X-url-tmp = regsub(req.url,"[&?]no_body.[^&]*","");
    ## remove object_id
    set req.http.X-url-tmp = regsub(req.http.X-url-tmp,"[&?]object.[^&]*","");
    ## remove object
    set req.http.X-url-tmp = regsub(req.http.X-url-tmp,"[&?]object_id.[^&]*","");

    hash_data(req.http.X-url-tmp);
    unset req.http.X-url-tmp;
  }
  else {
    ## the default has of varnish for the end-user request, however we better remove the
    ## parameters specific to 3scale from the end-user request. If you want to remove other
    ## parameters from the original request so that they are not taken into account for the
    ## cache this is the place.

    set req.http.X-url-tmp = regsub(req.url,"[&?]user_key.[^&]*","");
    set req.http.X-url-tmp = regsub(req.url,"[&?]user_id.[^&]*","");
    set req.http.X-url-tmp = regsub(req.url,"[&?]app_key.[^&]*","");
    set req.http.X-url-tmp = regsub(req.url,"[&?]app_id.[^&]*","");

    hash_data(req.http.X-url-tmp);

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

	  unset req.http.X-url-tmp;

  }

  return (hash);

}

#A HIT means that the request to your local cache of 3scale is still fresh and the
#response will not have to fetch the data from the remote 3scale backend.
#vmod_3scale plugin will send a replica of the call asynchronously to 3scale's remote
#backend on its own

sub vcl_hit {

  if (req.http.X-3scale-authorization) {
    if (req.url ~ "^/transactions/authrep.xml\?") {
	    if (threescale.send_get_request_threaded("su1.3scale.net","80",req.url,"")==0) {}
    }
  }

}

sub vcl_fetch {

  if (req.http.X-3scale-authorization) {
    ## cache the authorization from 3scale
  	set beresp.ttl = 30s;
  }
  else {
    ## cache of the content of your API
	  set beresp.ttl = 60s;
  }

}

sub vcl_deliver {
  ## remove control headers
  unset req.http.X-3scale-authrep;
}

sub vcl_error {
  set obj.http.Content-Type = "text/html; charset=utf-8";
  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>

Authorization error trying to access this API

Did you forget to send your credentials?

Are you over the limits of your account?

Please go to your account at 3scale to find out

"};
  return (deliver);
}


update varnish config

update varnish config

# Marker to tell the VCL compiler that this VCL has been adapted to the
# new 4.0 format.
vcl 4.0;

# Default backend definition. Set this to point to your content server.
backend default {
    .host = "127.0.0.1";
    .port = "8080";
}

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.

    if (req.url ~ "^/(admin/|contact|awin-transaction)" || req.url ~ "/edit") {
        return (pass);
    }

    # special features on fleet lists for logged in users
    if (req.http.Cookie ~ "sessionid" && req.url ~ "operator" && req.url ~ "vehicles") {
        return (pass);
    }

    unset req.http.Cookie;
}

sub vcl_backend_response {
    # Happens after we have read the response headers from the backend.
    #
    # Here you clean the response headers, removing silly Set-Cookie headers
    # and other mistakes your backend does.

    if (bereq.url !~ "^/(admin/|contact|edit)") {
        unset beresp.http.set-cookie;

        if (beresp.status >= 200 && beresp.status < 400) {
            if (bereq.url ~ "^/stops/") {
                set beresp.ttl = 50s;
            } elif (bereq.url ~ "/(vehicles|journeys/)") {
                set beresp.ttl = 6s;
            } else {
                set beresp.ttl = 1h;
            }
        }
    }
}

vcl 4.0;

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

backend bustimesio {
    .host = "127.0.0.1";
    .port = "8082";
}

backend supermarket {
    .host = "127.0.0.1";
    .port = "8000";
}

backend basiltherat {
    .host = "127.0.0.1";
    .port = "8083";
}

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

sub vcl_recv {
    if (req.http.host == "bustimes.io") {
        set req.backend_hint = bustimesio;
    } elif (req.http.host == "www.supermarketmarket.co.uk") {
        set req.backend_hint = supermarket;
    } elif (req.http.host == "hygieneratings.co.uk") {
        set req.backend_hint = basiltherat;
    } elif (req.url ~ "^/styles/") {
        set req.backend_hint = tileserver;
    }

    if (req.url ~ "^/(admin/|contact|awin-transaction)") {
        return (pass);
    }

    unset req.http.Cookie;
}

sub vcl_backend_response {
    if (bereq.url !~ "^/(admin/|contact)") {
        unset beresp.http.set-cookie;

        if (beresp.status >= 200 && beresp.status < 400) {
            if (bereq.url ~ "^/stops/") {
                set beresp.ttl = 30s;
            } elif (bereq.url ~ "^/vehicles\.json") {
                set beresp.ttl = 5s;
            } elif (bereq.url ~ "^/styles/") {
                set beresp.ttl = 30d;
            } else {
                set beresp.ttl = 1h;
            }
        }

    }
}

sub vcl_deliver {
    if (req.url ~ "/stops/" && resp.status >= 200 && resp.status < 400) {
        set resp.http.X-Accel-Buffering = "no";
    }
}


Adjusting Varnish to cache most GET requests even when cookies are present

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

 sub vcl_recv {

    if (! (req.url ~ "^/openidm/") ) {
      set req.url = regsub(req.url, "^/", "/sqlfiddle/");
    }

    if ( req.url == "/sqlfiddle/") {
      set req.url = "/sqlfiddle/index.html";
    }

    if (req.request == "GET" && req.url != "/openidm/info/login") {
        unset req.http.cookie;
    }

 }

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

 sub vcl_recv {

    if (! (req.url ~ "^/openidm/") ) {
      set req.url = regsub(req.url, "^/", "/sqlfiddle/");
    }

    if ( req.url == "/sqlfiddle/") {
      set req.url = "/sqlfiddle/index.html";
    }
 }


update port for examples

update port for examples

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

import std;

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

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

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

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

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

  if (req.request != "GET" &&
      req.request != "HEAD" &&
      req.request != "PUT" &&
      req.request != "POST" &&
      req.request != "TRACE" &&
      req.request != "OPTIONS" &&
      req.request != "DELETE") {
    /* Non-RFC2616 or CONNECT which is weird. */
    return (pipe);
  }
  if (req.request != "GET" && req.request != "HEAD") {
    /* We only deal with GET and HEAD by default */
    return (pass);
  }
  if (req.http.Authorization || req.http.Cookie) {
    /* Not cacheable by default */
    return (pass);
  }
  return (lookup);
}

sub vcl_hash {
  hash_data(req.url);
  if (req.http.host) {
    hash_data(req.http.host);
  } else {
    hash_data(server.ip);
  }
  if (req.http.Origin) {
    hash_data(req.http.Origin);
  }
  return (hash);
}

sub vcl_pipe {
  # Note that only the first request to the backend will have
  # X-Forwarded-For set.  If you use X-Forwarded-For and want to
  # have it set for all requests, make sure to have:
  set bereq.http.connection = "close";
  # here.  It is not set by default as it might break some broken web
  # applications, like IIS with NTLM authentication.

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

  return (pipe);
}

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

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

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

import std;

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

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

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

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

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

  if (req.request != "GET" &&
      req.request != "HEAD" &&
      req.request != "PUT" &&
      req.request != "POST" &&
      req.request != "TRACE" &&
      req.request != "OPTIONS" &&
      req.request != "DELETE") {
    /* Non-RFC2616 or CONNECT which is weird. */
    return (pipe);
  }
  if (req.request != "GET" && req.request != "HEAD") {
    /* We only deal with GET and HEAD by default */
    return (pass);
  }
  if (req.http.Authorization || req.http.Cookie) {
    /* Not cacheable by default */
    return (pass);
  }
  return (lookup);
}

sub vcl_hash {
  hash_data(req.url);
  if (req.http.host) {
    hash_data(req.http.host);
  } else {
    hash_data(server.ip);
  }
  if (req.http.Origin) {
    hash_data(req.http.Origin);
  }
  return (hash);
}

sub vcl_pipe {
  # Note that only the first request to the backend will have
  # X-Forwarded-For set.  If you use X-Forwarded-For and want to
  # have it set for all requests, make sure to have:
  set bereq.http.connection = "close";
  # here.  It is not set by default as it might break some broken web
  # applications, like IIS with NTLM authentication.

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

  return (pipe);
}

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

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