Revert "fix cache of images"

Revert "fix cache of images"

This reverts commit 9cac58ae16a1e12a97ce54f220a84a61c90ebcd7.

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

import std;

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

sub vcl_init {

}

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

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

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

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

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

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

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

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

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

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

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

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

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

    unset req.http.Accept-Encoding;

    return(hash);
}

sub vcl_pipe {
    # By default Connection: close is set on all piped requests, to stop
    # connection reuse from sending future requests directly to the
    # (potentially) wrong backend. If you do want this to happen, you can undo
    # it here.
    # unset bereq.http.connection;

    return(pipe);
}

sub vcl_pass {
    return (fetch);
}

sub vcl_hash {
    hash_data(req.url);
    if (req.http.host) {
        hash_data(req.http.host);
    } else {
        hash_data(server.ip);
    }
    if (req.http.X-Forwarded-Proto) {
        hash_data(req.http.X-Forwarded-Proto);
    }
    return (lookup);
}

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

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

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

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

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

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

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

sub vcl_backend_fetch{
    return (fetch);
}

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

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

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

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

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

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

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

    return (deliver);
}

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

    unset resp.http.Server;

}

/*
 We can come here "invisibly" with the following errors: 413, 417 & 503
*/
sub vcl_synth {
    set resp.http.Content-Type = "text/html; charset=utf-8";
    set resp.http.Retry-After = "5";

    synthetic( {"
        <?xml version="1.0" encoding="utf-8"?>
        <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

            <title>Varnish cache server: "} + resp.status + " " + resp.reason + {" </title>

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

"} + resp.reason + {"

Guru Meditation:

XID: "} + req.xid + {"

Varnish cache server

    "} );

    return (deliver);
}

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

import std;

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

sub vcl_init {

}

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

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

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

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

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

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

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

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

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

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

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

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

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

    unset req.http.Accept-Encoding;

    return(hash);
}

sub vcl_pipe {
    # By default Connection: close is set on all piped requests, to stop
    # connection reuse from sending future requests directly to the
    # (potentially) wrong backend. If you do want this to happen, you can undo
    # it here.
    # unset bereq.http.connection;

    return(pipe);
}

sub vcl_pass {
    return (fetch);
}

sub vcl_hash {
    hash_data(req.url);
    if (req.http.host) {
        hash_data(req.http.host);
    } else {
        hash_data(server.ip);
    }
    if (req.http.X-Forwarded-Proto) {
        hash_data(req.http.X-Forwarded-Proto);
    }
    return (lookup);
}

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

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

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

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

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

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

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

sub vcl_backend_fetch{
    return (fetch);
}

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

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

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

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

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

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

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

    return (deliver);
}

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

    unset resp.http.Server;

}

/*
 We can come here "invisibly" with the following errors: 413, 417 & 503
*/
sub vcl_synth {
    set resp.http.Content-Type = "text/html; charset=utf-8";
    set resp.http.Retry-After = "5";

    synthetic( {"
        <?xml version="1.0" encoding="utf-8"?>
        <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

            <title>Varnish cache server: "} + resp.status + " " + resp.reason + {" </title>

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

"} + resp.reason + {"

Guru Meditation:

XID: "} + req.xid + {"

Varnish cache server

    "} );

    return (deliver);
}


remove example vcl

remove example vcl

# This is a basic VCL configuration file for varnish.  See the vcl(7)
# man page for details on VCL syntax and semantics.
#
# Default backend definition.  Set this to point to your content
# server.
#
backend default {
    .host = "www.varnish-cache.org";
    .port = "80";
}
import geo;
import std;

sub vcl_recv{
    if (req.restarts == 0) {
      if (req.http.x-forwarded-for) {
          set req.http.X-Forwarded-For =
              req.http.x-real-ip  + ", " + req.http.X-Forwarded-For + ", " + client.ip;
      } else {
        set req.http.X-Forwarded-For = req.http.x-real-ip + ", " + client.ip;
      }
    }
    if (req.url ~ "/svc/location/v1/current.json") {
        set req.http.IP = regsuball(req.http.X-Forwarded-For, ",.*", "");
        return (error);
    }
    // logic to do weather based on IP or NYT-W2 cookie
    if ((req.url ~ "/svc/weather/v2/current.json") || (req.url ~ "/svc/weather/v2/current-and-five-day-forecast.json") || (req.url ~ "/svc/weather/v2/current-and-seven-day-forecast.json")) {
        if (req.restarts == 0) {
            if (req.http.Cookie ~ "NYT_W2=[a-zA-Z0-9%:\-|]+") {
                set req.http.X-Weather = geo.get_weather_cookie(req.http.Cookie, "NYT_W2");
                set req.url = regsub(req.url, ".json", "/" + req.http.X-Weather + ".json");
                set req.url = regsub(req.url, " ", "%20");
            } else {
                // i think we want the first forwarded for ip.
                if (req.http.X-Forwarded-For ~ ",") {
                    if (!req.http.IP) {
                        set req.http.IP = regsuball(req.http.X-Forwarded-For, ",.*", "");
                    }
                } else {
                    if (!req.http.IP) {
                        set req.http.IP = req.http.x-real-ip;
                    }
                }
                set req.http.X-Weather = geo.weather_code(req.http.IP);
                set req.http.X-Weather = regsuball(req.http.X-Weather, " ", "%20");
                set req.http.X-Timezone = geo.timezone(req.http.IP);
                set req.url = regsub(req.url, ".json", "/" + req.http.X-Weather + ".json");
            }
        }
    }
}

sub vcl_error {
  if (req.url ~ "/svc/location/v1/current.json") {
    set obj.http.Content-Type = "text/json; charset=utf-8";
    synthetic
      geo.location(req.http.IP)
    ;
    return (deliver);
  }
}


Fastly PASS for uncacheable Journal content


Remove unused endpoints.

Remove unused endpoints.

- report removed in this
  [commit](https://github.com/reddit/reddit-mobile/commit/3af0b33161df61231387292fa61470fa9e1597b3#diff-79dad35511a55ef6ebdf34a68b034bdfL49)
- vote removed in this
  [commit](https://github.com/reddit/reddit-mobile/commit/d0cda057c9da770bf0339546d4a1d947855d1cdb#diff-79dad35511a55ef6ebdf34a68b034bdfL53)

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

sub vcl_recv {
  call device_detect;

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

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

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

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

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

sub vcl_recv {
  call device_detect;

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

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

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

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


Guard against missing x-pass Request Setting

    /* handle 5XX (or any other unwanted status code) */
    if (beresp.status >= 500 && beresp.status < 600) {

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

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

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

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

    # 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 since those can be temporary during the site build/index
        if (beresp.status == 404) {
            set beresp.ttl = 300s;
        }

    }

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

    if (beresp.http.Cache-Control ~ "private|no-cache|no-store") {
        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;
    }

    # Never cache 302s
    if (beresp.status == 302) {
        return (pass);
    }

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

    # 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") && !req.http.x-pass ) {
        unset beresp.http.set-cookie;

        # 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 they are image/script or CSS
        if (beresp.http.Content-Type ~ "(image|script|css)") {
            set beresp.http.Surrogate-Key = re.group.1;
        }
        return (deliver);
    }

    /* handle 5XX (or any other unwanted status code) */
    if (beresp.status >= 500 && beresp.status < 600) {

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

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

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

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

    # 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 since those can be temporary during the site build/index
        if (beresp.status == 404) {
            set beresp.ttl = 300s;
        }

    }

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

    if (beresp.http.Cache-Control ~ "private|no-cache|no-store") {
        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;
    }

    # Never cache 302s
    if (beresp.status == 302) {
        return (pass);
    }

    # 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") && !req.http.x-pass ) {
        unset beresp.http.set-cookie;

        # 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 they are image/script or CSS
        if (beresp.http.Content-Type ~ "(image|script|css)") {
            set beresp.http.Surrogate-Key = re.group.1;
        }
        return (deliver);
    }


Move the error message into vcl_recv (#3360)

# Note: It is VERY important to ensure that any changes to VCL will work
#       properly with both the current version of ``master`` and the version in
#       the pull request that adds any new changes. This is because the
#       configuration will be applied automatically as part of the deployment
#       process, but while the previous version of the code is still up and
#       running. Thus backwards incompatible changes must be broken up over
#       multiple pull requests in order to phase them in over multiple deploys.

sub vcl_recv {

    # Fastly is dropping support for TLSv1.0 and TLSv1.1, in preperation for
    # for that, we're going to block support for it now, using VCL. This will
    # let us present a better error message in the interim before Fastly shuts
    # off TLSv1.0 and TLSv1.1 support completely.
    if (tls.client.protocol ~ "^TLSv1(\.(0|1))?$") {
        set req.http.Error-Message = "Support for " tls.client.protocol " has been removed, please upgrade to a TLSv1.2+ client. Please see https://pyfound.blogspot.com/2017/01/time-to-upgrade-your-python-tls-v12.html";
        error 804 "Bad SSL Version";
    }

    # 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, "#.*$", "");

    # Fastly does some normalization of the Accept-Encoding header so that it
    # reduces the number of cached copies (when served with the common,
    # Vary: Accept-Encoding) that are cached for any one URL. This makes a lot
    # of sense, except for the fact that we want to enable brotli compression
    # for our static files. Thus we need to work around the normalized encoding
    # in a way that still minimizes cached copies, but which will allow our
    # static files to be served using brotli.
    if (req.url ~ "^/static/" && req.http.Fastly-Orig-Accept-Encoding) {
        if (req.http.User-Agent ~ "MSIE 6") {
            # For that 0.3% of stubborn users out there
            unset req.http.Accept-Encoding;
        } elsif (req.http.Fastly-Orig-Accept-Encoding ~ "br") {
            set req.http.Accept-Encoding = "br";
        } elsif (req.http.Fastly-Orig-Accept-Encoding ~ "gzip") {
            set req.http.Accept-Encoding = "gzip";
        } else {
            unset req.http.Accept-Encoding;
        }
    }

    # 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:
    #
    #   * /admin/
    #   * /search/
    #   * /account/login/
    #   * /account/logout/
    #   * /account/register/
    #   * /account/reset-password/
    #   * /account/verify-email/
    #   * /pypi
    if (req.url.path !~ "^/(admin/|search(/|$)|account/(login|logout|register|reset-password|verify-email)/|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);

    # We have a number of items that we'll pass back to the origin.
    # 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;
    }

    # Currently Fastly does not provide a way to access response headers when
    # the response is a 304 response. This is because the RFC states that only
    # a limit set of headers should be sent with a 304 response, and the rest
    # are SHOULD NOT. Since this stripping happens *prior* to vcl_deliver being
    # ran, that breaks our ability to log on 304 responses. Ideally at some
    # point Fastly offers us a way to access the "real" response headers even
    # for a 304 response, but for now, we are going to remove the headers that
    # allow a conditional response to be made. If at some point Fastly does
    # allow this, then we can delete this code.
    if (!req.http.Fastly-FF
            && std.tolower(req.http.Warehouse-Host) == "files.pythonhosted.org"
            && req.url.path ~ "^/packages/[a-f0-9]{2}/[a-f0-9]{2}/[a-f0-9]{60}/") {
        unset req.http.If-None-Match;
        unset req.http.If-Modified-Since;
    }

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

    # Redirect pypi.io, www.pypi.io, and warehouse.python.org to pypi.org, this
    # is purposely done *after* the HTTPS checks.
    if (std.tolower(req.http.host) ~ "^(www.pypi.org|(www.)?pypi.io|warehouse.python.org)$") {
        set req.http.Location = "https://pypi.org" req.url;
        error 750 "Redirect to Primary Domain";
    }
    # Redirect warehouse-staging.python.org to test.pypi.io.
    if (std.tolower(req.http.host) ~ "^(test.pypi.io|warehouse-staging.python.org)$") {
        set req.http.Location = "https://test.pypi.org" 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.http.host ~ "^(test-)?files.pythonhosted.org$"
            && 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.AWS-Access-Key-ID;
    unset req.http.AWS-Secret-Access-Key;
    unset req.http.AWS-Bucket-Name;

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

    # We never want to cache our admin URLs, while this should be "safe" due to
    # the architecure of Warehouse, it'll just be easier to debug issues if
    # these always are uncached.
    if (req.url ~ "^/admin/") {
        return(pass);
    }

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

sub vcl_fetch {

    # These are newer kinds of redirects which should be able to be cached by
    # default, even though Fastly doesn't currently have them in their default
    # list of cacheable status codes.
    if (http_status_matches(beresp.status, "303,307,308")) {
        set beresp.cacheable = true;
    }

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

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

    # Currently Fastly does not provide a way to access response headers when
    # the response is a 304 response. This is because the RFC states that only
    # a limit set of headers should be sent with a 304 response, and the rest
    # are SHOULD NOT. Since this stripping happens *prior* to vcl_deliver being
    # ran, that breaks our ability to log on 304 responses. Ideally at some
    # point Fastly offers us a way to access the "real" response headers even
    # for a 304 response, but for now, we are going to remove the headers that
    # allow a conditional response to be made. If at some point Fastly does
    # allow this, then we can delete this code, and also allow a 304 response
    # in the http_status_matches() check further down.
    if (!req.http.Fastly-FF
            && std.tolower(req.http.Warehouse-Host) == "files.pythonhosted.org"
            && req.url.path ~ "^/packages/[a-f0-9]{2}/[a-f0-9]{2}/[a-f0-9]{60}/") {
        unset resp.http.ETag;
        unset resp.http.Last-Modified;
    }

    # If we're not executing a shielding request, and the URL is one of our file
    # URLs, and it's a GET request, and the response is either a 200 or a 304
    # then we want to log an event stating that a download has taken place.
    if (!req.http.Fastly-FF
            && std.tolower(req.http.Warehouse-Host) == "files.pythonhosted.org"
            && req.url.path ~ "^/packages/[a-f0-9]{2}/[a-f0-9]{2}/[a-f0-9]{60}/"
            && req.request == "GET"
            && http_status_matches(resp.status, "200")) {
        log {"syslog "} req.service_id {" linehaul :: "} "2@" now "|" geoip.country_code "|" req.url.path "|" tls.client.protocol "|" tls.client.cipher "|" resp.http.x-amz-meta-project "|" resp.http.x-amz-meta-version "|" resp.http.x-amz-meta-package-type "|" req.http.user-agent;
        log {"syslog "} req.service_id {" downloads :: "} "2@" now "|" geoip.country_code "|" req.url.path "|" tls.client.protocol "|" tls.client.cipher "|" resp.http.x-amz-meta-project "|" resp.http.x-amz-meta-version "|" resp.http.x-amz-meta-package-type "|" req.http.user-agent;
    }

    # Set X-Robots-Header for everything _but_ prod
    if (std.tolower(req.http.host) !~ "^pypi.org$") {
        set resp.http.X-Robots-Header = "noindex";
    }

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

    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 == 804) {
        set obj.status = 403;
        set obj.response = "TLSv1.2+ is required";
        set obj.http.Content-Type = "text/plain; charset=UTF-8";
        synthetic req.http.Error-Message;
        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);
    }

}

# Note: It is VERY important to ensure that any changes to VCL will work
#       properly with both the current version of ``master`` and the version in
#       the pull request that adds any new changes. This is because the
#       configuration will be applied automatically as part of the deployment
#       process, but while the previous version of the code is still up and
#       running. Thus backwards incompatible changes must be broken up over
#       multiple pull requests in order to phase them in over multiple deploys.

sub vcl_recv {

    # Fastly is dropping support for TLSv1.0 and TLSv1.1, in preperation for
    # for that, we're going to block support for it now, using VCL. This will
    # let us present a better error message in the interim before Fastly shuts
    # off TLSv1.0 and TLSv1.1 support completely.
    if (tls.client.protocol ~ "^TLSv1(\.(0|1))?$") {
        error 804 "Bad SSL Version";
    }

    # 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, "#.*$", "");

    # Fastly does some normalization of the Accept-Encoding header so that it
    # reduces the number of cached copies (when served with the common,
    # Vary: Accept-Encoding) that are cached for any one URL. This makes a lot
    # of sense, except for the fact that we want to enable brotli compression
    # for our static files. Thus we need to work around the normalized encoding
    # in a way that still minimizes cached copies, but which will allow our
    # static files to be served using brotli.
    if (req.url ~ "^/static/" && req.http.Fastly-Orig-Accept-Encoding) {
        if (req.http.User-Agent ~ "MSIE 6") {
            # For that 0.3% of stubborn users out there
            unset req.http.Accept-Encoding;
        } elsif (req.http.Fastly-Orig-Accept-Encoding ~ "br") {
            set req.http.Accept-Encoding = "br";
        } elsif (req.http.Fastly-Orig-Accept-Encoding ~ "gzip") {
            set req.http.Accept-Encoding = "gzip";
        } else {
            unset req.http.Accept-Encoding;
        }
    }

    # 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:
    #
    #   * /admin/
    #   * /search/
    #   * /account/login/
    #   * /account/logout/
    #   * /account/register/
    #   * /account/reset-password/
    #   * /account/verify-email/
    #   * /pypi
    if (req.url.path !~ "^/(admin/|search(/|$)|account/(login|logout|register|reset-password|verify-email)/|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);

    # We have a number of items that we'll pass back to the origin.
    # 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;
    }

    # Currently Fastly does not provide a way to access response headers when
    # the response is a 304 response. This is because the RFC states that only
    # a limit set of headers should be sent with a 304 response, and the rest
    # are SHOULD NOT. Since this stripping happens *prior* to vcl_deliver being
    # ran, that breaks our ability to log on 304 responses. Ideally at some
    # point Fastly offers us a way to access the "real" response headers even
    # for a 304 response, but for now, we are going to remove the headers that
    # allow a conditional response to be made. If at some point Fastly does
    # allow this, then we can delete this code.
    if (!req.http.Fastly-FF
            && std.tolower(req.http.Warehouse-Host) == "files.pythonhosted.org"
            && req.url.path ~ "^/packages/[a-f0-9]{2}/[a-f0-9]{2}/[a-f0-9]{60}/") {
        unset req.http.If-None-Match;
        unset req.http.If-Modified-Since;
    }

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

    # Redirect pypi.io, www.pypi.io, and warehouse.python.org to pypi.org, this
    # is purposely done *after* the HTTPS checks.
    if (std.tolower(req.http.host) ~ "^(www.pypi.org|(www.)?pypi.io|warehouse.python.org)$") {
        set req.http.Location = "https://pypi.org" req.url;
        error 750 "Redirect to Primary Domain";
    }
    # Redirect warehouse-staging.python.org to test.pypi.io.
    if (std.tolower(req.http.host) ~ "^(test.pypi.io|warehouse-staging.python.org)$") {
        set req.http.Location = "https://test.pypi.org" 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.http.host ~ "^(test-)?files.pythonhosted.org$"
            && 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.AWS-Access-Key-ID;
    unset req.http.AWS-Secret-Access-Key;
    unset req.http.AWS-Bucket-Name;

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

    # We never want to cache our admin URLs, while this should be "safe" due to
    # the architecure of Warehouse, it'll just be easier to debug issues if
    # these always are uncached.
    if (req.url ~ "^/admin/") {
        return(pass);
    }

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

sub vcl_fetch {

    # These are newer kinds of redirects which should be able to be cached by
    # default, even though Fastly doesn't currently have them in their default
    # list of cacheable status codes.
    if (http_status_matches(beresp.status, "303,307,308")) {
        set beresp.cacheable = true;
    }

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

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

    # Currently Fastly does not provide a way to access response headers when
    # the response is a 304 response. This is because the RFC states that only
    # a limit set of headers should be sent with a 304 response, and the rest
    # are SHOULD NOT. Since this stripping happens *prior* to vcl_deliver being
    # ran, that breaks our ability to log on 304 responses. Ideally at some
    # point Fastly offers us a way to access the "real" response headers even
    # for a 304 response, but for now, we are going to remove the headers that
    # allow a conditional response to be made. If at some point Fastly does
    # allow this, then we can delete this code, and also allow a 304 response
    # in the http_status_matches() check further down.
    if (!req.http.Fastly-FF
            && std.tolower(req.http.Warehouse-Host) == "files.pythonhosted.org"
            && req.url.path ~ "^/packages/[a-f0-9]{2}/[a-f0-9]{2}/[a-f0-9]{60}/") {
        unset resp.http.ETag;
        unset resp.http.Last-Modified;
    }

    # If we're not executing a shielding request, and the URL is one of our file
    # URLs, and it's a GET request, and the response is either a 200 or a 304
    # then we want to log an event stating that a download has taken place.
    if (!req.http.Fastly-FF
            && std.tolower(req.http.Warehouse-Host) == "files.pythonhosted.org"
            && req.url.path ~ "^/packages/[a-f0-9]{2}/[a-f0-9]{2}/[a-f0-9]{60}/"
            && req.request == "GET"
            && http_status_matches(resp.status, "200")) {
        log {"syslog "} req.service_id {" linehaul :: "} "2@" now "|" geoip.country_code "|" req.url.path "|" tls.client.protocol "|" tls.client.cipher "|" resp.http.x-amz-meta-project "|" resp.http.x-amz-meta-version "|" resp.http.x-amz-meta-package-type "|" req.http.user-agent;
        log {"syslog "} req.service_id {" downloads :: "} "2@" now "|" geoip.country_code "|" req.url.path "|" tls.client.protocol "|" tls.client.cipher "|" resp.http.x-amz-meta-project "|" resp.http.x-amz-meta-version "|" resp.http.x-amz-meta-package-type "|" req.http.user-agent;
    }

    # Set X-Robots-Header for everything _but_ prod
    if (std.tolower(req.http.host) !~ "^pypi.org$") {
        set resp.http.X-Robots-Header = "noindex";
    }

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

    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 == 804) {
        set obj.status = 403;
        set obj.response = "TLSv1.2+ is required";
        set obj.http.Content-Type = "text/plain; charset=UTF-8";
        synthetic "Support for " tls.client.protocol " has been removed, please upgrade to a TLSv1.2+ client.";
        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);
    }

}