Added/fixed HTML commands

Added/fixed HTML commands

# Voice commands for gvim

include folders.vch;
include numbers.vch;
include letters.vch;
include pandas.vch;

Edit [My] Configuration = ':e $'  'MYVIMRC' {Enter};

<delimiters> := (Quotes = '"' | 'Single Quotes' = "'" | 'Double Quotes' = '"' | Parentheses = '('
		| Brackets = '[' | 'Curly Braces' = '{' | Braces = '{'
		| HTML = '<' | 'Close HTML' = '>' | 'Close Tag' = '>'	# NOTE: only works with commands I created
		| 'Equal Sign' = '='
);

# --- File Management ---------------
Open Document = {Alt+f} o;
List Files = {Esc}  ':bro ol' {Enter};
File <numbers> = $1  {Enter};
Latest File = {Esc}  ':bro ol' {Enter} 1{Enter};

Show (Projects | Folders) = {Esc}':e C:\Voice\folders.vch'  {Enter} Wait(100) {Down_9};
[Open] Project <folder> =  {Esc} ':e ' $1 {Enter} Wait(100) {Down_9};
[Open] Current Project = {Esc} ':e .' {Enter} Wait(100) {Down_9};
Up Directory = '-';
Search [Directory] <letters> = '/^' $1;
# :Sex = directory of current file
# :Vex for vertical split
#   :sp. = Split horizontally on current directory (:vsp. For vertical)
# % = new file

(List Buffers = 'ls' | Buffer List = 'ls' | 'Next Buffer' = 'bn' | 'Last Buffer' = 'bp' | 'Close Buffer' = 'bd'
	| 'Next Window' = 'bn' | 'Last Window' = 'bp' | 'Close Window' = 'bd')
	= {Esc}':' $1  {Enter};

Save Document = {Esc}  Wait(100) ':w' {Enter};
Save As = {Alt+f} a;
Go (Quit = 'quit' | Close = 'close' | 'Close without saving' = 'close!'  | Set = 'set') = ':' $1 {Enter};
[Go] Quit without Saving = {Esc}  Wait(100) ':quit!' {Enter};

Save and Reload = {Esc}  Wait(100) ':w' {Enter} Wait(100)  SendSystemKeys({Alt+Tab}) Wait(100) {Alt+r};

# --- Basic Navigation ---------------
<numbers> (Up = 'k' | Down = 'j' | Right = 'l' | Left = 'h') = $1 $2;
Homa = {Esc} 0;
Enda = {Esc}  '$';
(Top |  Go to Top) = {Esc} gg;
(Bottom | Go to Bottom) = {Esc} G;
(Move Forward | Move Right | Go Forward) <numbers> (words = 'w' |  blocks = '}') = $2 $3;
(Move Forward | Move Right | Go Forward) <numbers> (end | enda) words = $2 'e';
(Move Back | Move Left | Go Back) <numbers> (words = 'b' | blocks = '{') = $2 $3;
Fine (Text | Alpha) <letters> = {Esc} f $2;
Find Back <letters> = {Esc} F $1;
Fine Number 1..9 = {Esc} '/' $1 {Enter};
Go (Search | Find) = {Esc} '/\c';         # \c = case insensitive
Fine <delimiters> = {Esc} '/' $1 {Enter};
Insert (Before = 'i' | After = 'a') <delimiters> = {Esc} '/' $2 {Enter} Wait(100) $1;
Fine Tag = {Esc} '/' '<' {Enter};
Insert (Before = 'i' | After = 'a') tag = {Esc} '/' '<' {Enter} Wait(100) $1;

(Find Again | Next Fine | Next Find) = n;
Line 1..100 = {Esc} ':' $1  {Enter};

# --- Basic Editing ---------------
(Do | Go) (Insert = 'i' | Append = 'A' |
Escape = '{Esc}') = $2;
(Insert = 'i' |
Escape = '{Esc}') = $1;
Nexta = {Esc} A {Enter};
Enda Insert = {Esc} A;

(Do Again | Repeat That) = '.';
(Undo | Undo That) = {Esc} u;
Redo = {Esc} {Ctrl+r};

(Kill = 'x') <numbers> = $2  $1;
(Kill | Delete) Word = 'dw';
(Kill | Delete) <numbers>  Words = 'd' $2 'w';
Dee Dee = 'dd';
Cut to End of Line = 'd$';
Cut to End of Word = 'de';

('Go Visual' | Mark | 'Visual Mode') = V;
('Delete That' = 'd' |  'Yank That' = 'y' | 'Paste' = 'p' | 'Change That' = 'c' | 'Join That' = 'J') = $1;
Paste That = {Esc} Wait(100) '"+gP';
Copy That = '"*y';
Copy Line = {Esc} V Wait(100)  '"*y';
Select Line = {Esc} V;

do test = {Esc} i  Wait(100) "strawberry";

(Delete= 'd' | Change = 'c') Between Tags = $1 i t;
(Delete= 'd' | Change = 'c') (Next = '/' | Last = '?') Tag = {Esc} $2 '>' {Enter} $1 i t;
(Delete= 'd' | Change = 'c') between <delimiters> = $1 i $2;
(Delete= 'd' | Change = 'c') (Next = '/' | Last = '?') <delimiters> = {Esc} $2 $3 {Enter} $1 i $3;
(Delete= 'd' | Change = 'c') (Next = '/' | Last = '?') (Heading = '<h' | Header = '<h' | Paragraph = '<p')
		= {Esc} $2 '\c' $3 {Enter} $1 i t;

Select All = {Alt+e}  s {Enter};
Grab Everything = {Alt+e} s  {Enter} Wait(100) {Alt+e} c {Enter} Wait(100) {Alt+Tab};

1..20 Spaces = Repeat($1, ' ');

Delete Line = 'dd';
Delete <numbers> Lines = $1 'dd';

# --- HTML and Bootstrap commands (until I'm sure I can get Ultisnips to work) ------------
Next One = {Esc} A;
Start (Heading | Header) 1..5 = '<h' $2 '></h' $2 '>'   Wait(100) {Left_5};
Wrap [In] (Heading | Header) 1..5 = {Esc} I '<h' $2 '>' {Esc} A '</h' $2 '>' {Esc};
Start Para = {Esc} I '
';
Stop Para = {Esc} A '
' {Esc};

Paste Image = {Esc} a ' ';		# Might also add: width="200"

Start Div (id | class) = '
' Wait(100) {Left_2};
Stop Div = '
' {Enter};
Start Columns = {Esc} i  Wait(100) '
{Enter}
{Enter}'
		 {Enter} '
' {Enter}  '
'  {Esc} {Up_3}   Wait(100) A;
Start Column 1..12 = {Esc} a '
' {Enter} '
'{Enter}  {Esc} {Up_2} Wait(100) A;

# --- Ultisnips (if I can get it working on all PCs) ------------------------
Do Ultisnips = {Esc} ':UltiSnipsEdit'{Enter};

(Next = 'j' | Last = 'k') Tag = {Ctrl+$1};
Start Snippet= {Esc} i 'snippet  {Enter} {Enter}endsnippet' {Esc} {Up_2} A;
Start (
	D3 = 'd3_template' | Code = 'D3_code_wrapper'
| 	form | 'form text'  | 'form button' | 'form select'
) = {Esc} i $1  Wait(100)  {Tab};

# --- COMMANDS TO ADD ------------------------

# Using bookmarks or whatever they are called  e.g., if I accidentally do the wrong thing and end up on the wrong line,
# g;  - last place I was where I made a change
# Ctrl-o  - If I jump to a place, copy a line, I can snap back

# copying sets of lines, moving sets of lines
# :9yank - copy line 9
# :9t16 - copy line 9 to line 16
# :9.  - copy line 9 to current position

# For adding quotes, HTML tags, etc. to text that's already there:
# http://stackoverflow.com/questions/10305952/vim-enclose-word-in-tag

# Commands I'm not sure about
# H,M,L: top, middle, and bottom of screen

# Voice commands for gvim

include folders.vch;
include numbers.vch;
include letters.vch;
include pandas.vch;

Edit [My] Configuration = ':e $'  'MYVIMRC' {Enter};

<delimiters> := (Quotes = '"' | 'Single Quotes' = "'" | 'Double Quotes' = '"' | Parentheses = '('
		| Brackets = '[' | 'Curly Braces' = '{' | Braces = '{'
		| HTML = '<' | Tags = '<' | 'Close HTML' = '>' | 'Close Tag' = '>'	# NOTE: only works with commands I created
		| 'Equal Sign' = '='
);

# --- File Management ---------------
Open Document = {Alt+f} o;
List Files = {Esc}  ':bro ol' {Enter};
File <numbers> = $1  {Enter};
Latest File = {Esc}  ':bro ol' {Enter} 1{Enter};

Show (Projects | Folders) = {Esc}':e C:\Voice\folders.vch'  {Enter} Wait(100) {Down_9};
[Open] Project <folder> =  {Esc} ':e ' $1 {Enter} Wait(100) {Down_9};
[Open] Current Project = {Esc} ':e .' {Enter} Wait(100) {Down_9};
Up Directory = '-';
Search [Directory] <letters> = '/^' $1;
# :Sex = directory of current file
# :Vex for vertical split
#   :sp. = Split horizontally on current directory (:vsp. For vertical)
# % = new file

(List Buffers = 'ls' | Buffer List = 'ls' | 'Next Buffer' = 'bn' | 'Last Buffer' = 'bp' | 'Close Buffer' = 'bd'
	| 'Next Window' = 'bn' | 'Last Window' = 'bp' | 'Close Window' = 'bd')
	= {Esc}':' $1  {Enter};

Save Document = {Esc}  Wait(100) ':w' {Enter};
Save As = {Alt+f} a;
Go (Quit = 'quit' | Close = 'close' | 'Close without saving' = 'close!'  | Set = 'set') = ':' $1 {Enter};
[Go] Quit without Saving = {Esc}  Wait(100) ':quit!' {Enter};

Save and Reload = {Esc}  Wait(100) ':w' {Enter} Wait(100)  SendSystemKeys({Alt+Tab}) Wait(100) {Alt+r};

# --- Basic Navigation ---------------
<numbers> (Up = 'k' | Down = 'j' | Right = 'l' | Left = 'h') = $1 $2;
Homa = {Esc} 0;
Enda = {Esc}  '$';
(Top |  Go to Top) = {Esc} gg;
(Bottom | Go to Bottom) = {Esc} G;
(Move Forward | Move Right | Go Forward) <numbers> (words = 'w' |  blocks = '}') = $2 $3;
(Move Forward | Move Right | Go Forward) <numbers> (end | enda) words = $2 'e';
(Move Back | Move Left | Go Back) <numbers> (words = 'b' | blocks = '{') = $2 $3;
Fine (Text | Alpha) <letters> = {Esc} f $2;
Find Back <letters> = {Esc} F $1;
Fine Number 1..9 = {Esc} '/' $1 {Enter};
Go (Search | Find) = {Esc} '/\c';         # \c = case insensitive
Fine <delimiters> = {Esc} '/' $1 {Enter};
Insert (Before = 'i' | After = 'a') <delimiters> = {Esc} '/' $2 {Enter} Wait(100) $1;

(Find Again | Next Fine | Next Find) = n;
Line 1..100 = {Esc} ':' $1  {Enter};

# --- Basic Editing ---------------
(Do | Go) (Insert = 'i' | Append = 'A' |
Escape = '{Esc}') = $2;
(Insert = 'i' |
Escape = '{Esc}') = $1;
Nexta = {Esc} A {Enter};
Enda Insert = {Esc} A;

(Do Again | Repeat That) = '.';
(Undo | Undo That) = {Esc} u;
Redo = {Esc} {Ctrl+r};

(Kill = 'x') <numbers> = $2  $1;
(Kill | Delete) Word = 'dw';
(Kill | Delete) <numbers>  Words = 'd' $2 'w';
Dee Dee = 'dd';
Cut to End of Line = 'd$';
Cut to End of Word = 'de';

('Go Visual' | Mark | 'Visual Mode') = V;
('Delete That' = 'd' |  'Yank That' = 'y' | 'Paste' = 'p' | 'Change That' = 'c' | 'Join That' = 'J') = $1;
Paste That = {Esc} Wait(100) '"+gP';
Copy That = '"*y';
Copy Line = {Esc} V Wait(100)  '"*y';
Select Line = {Esc} V;

do test = {Esc} i  Wait(100) "strawberry";

(Delete= 'd' | Change = 'c') between <delimiters> = $1 i $2;
(Delete= 'd' | Change = 'c') (Next = '/' | Last = '?') <delimiters> = {Esc} $2 $3 {Enter} $1 i $3;
(Delete= 'd' | Change = 'c') (Next = '/' | Last = '?')
	(Heading = '<h' | Header = '<h' | Paragraph = '<p')
	= {Esc} $2 '\c' $3 {Enter} $1 i t;

Select All = {Alt+e}  s {Enter};
Grab Everything = {Alt+e} s  {Enter} Wait(100) {Alt+e} c {Enter} Wait(100) {Alt+Tab};

1..20 Spaces = Repeat($1, ' ');

Do Ultisnips = {Esc} ':UltiSnipsEdit'{Enter};

(Next = 'j' | Last = 'k') Tag = {Ctrl+$1};
Start Snippet= {Esc} i 'snippet  {Enter} {Enter}endsnippet' {Esc} {Up_2} A;
Start (
	D3 = 'd3_template' | Code = 'D3_code_wrapper'
| 	form | 'form text'  | 'form button' | 'form select'
) = {Esc} i $1  Wait(100)  {Tab};

Delete Line = 'dd';
Delete <numbers> Lines = $1 'dd';

# --- HTML and Bootstrap commands (until I'm sure I can get Ultisnips to work) ------------
Next One = {Esc} A;
Start (Heading | Header) 1..4 = '<h' $2 '></h' $2 '>'   Wait(100) {Left_5};
Start Div (id | class) = '
' Wait(100) {Left_2};
Stop Div = '
' {Enter};
Start Columns = {Esc} i  Wait(100) '
{Enter}
{Enter}'
		 {Enter} '
' {Enter}  '
'  {Esc} {Up_3}   Wait(100) A;
Start Column 1..12 = {Esc} a '
' {Enter} '
'{Enter}  {Esc} {Up_2} Wait(100) A;

# Paste Image = {Esc} a' ';
Paste Image = {Esc} a ' ';

# --- COMMANDS TO ADD ------------------------

# Using bookmarks or whatever they are called  e.g., if I accidentally do the wrong thing and end up on the wrong line,
# g;  - last place I was where I made a change
# Ctrl-o  - If I jump to a place, copy a line, I can snap back

# copying sets of lines, moving sets of lines
# :9yank - copy line 9
# :9t16 - copy line 9 to line 16
# :9.  - copy line 9 to current position

# For adding quotes, HTML tags, etc. to text that's already there:
# http://stackoverflow.com/questions/10305952/vim-enclose-word-in-tag

# Commands I'm not sure about
# H,M,L: top, middle, and bottom of screen


Simplified cookie logic in receive/wordpress.vcl

* The existing wp cookie check was *starting with* `wp-` or `wprdpress_`, but would not match a 2nd or higher position wp cookie
* The second url check for `wp-(login|admin)` was no longer necessary, so removing all cookies past that point is more efficient

# Pass all local or login/admin requests straight through
if (req.http.Host ~ "^local\." || (req.url ~ "wp-(login|admin)")) {
    return (pass);
}

# Pass all requests containing a wp- or wordpress_ cookie
# (meaning NO caching for logged in users)
if (req.http.Cookie ~ "^([^;]+;\s*)*?(wp-|wordpress_)") {
  return (pass);
}

# Drop *all* cookies sent to Wordpress, if we've gotten this far
unset req.http.Cookie;

# Try a cache-lookup
return (lookup);

# Pass all local or login/admin requests straight through
if (req.http.Host ~ "^local\." || (req.url ~ "wp-(login|admin)")) {
    return (pass);
}

if (req.http.Cookie ~ "^wp-" || req.http.Cookie ~ "^wordpress_") {
  return (pass);
}

# Drop any cookies sent to Wordpress.
if (!(req.url ~ "wp-(login|admin)")) {
    unset req.http.Cookie;
}

# Anything else left?
if (!req.http.Cookie) {
    unset req.http.Cookie;
}

# Try a cache-lookup
return (lookup);


refactor drupal varnish to support PURGE for Drupal 7

vcl 4.0;

import std;

# set backend default
backend default {
  .host = "${VARNISH_BACKEND_HOST:-nginx}";
  .port = "${VARNISH_BACKEND_PORT:-8080}";
  .first_byte_timeout = 35m;
  .between_bytes_timeout = 10m;
}

# Allow purging from localhost
# @TODO allow from openshift network
acl purge {
      "127.0.0.1";
      "10.0.0.0"/8;
      "172.16.0.0"/12;
      "192.168.0.0"/16;
}

# This configuration is optimized for Drupal hosting:
# Respond to incoming requests.
sub vcl_recv {
  if (req.url ~ "^/varnish_status$")  {
    return (synth(200,"OK"));
  }
  # set the backend, which should be used:
  set req.backend_hint = default;

  # Always set the forward ip.
   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.X-LAGOON-VARNISH ) {
    ## Pass all Requests which are handled via an upstream Varnish
    set req.http.X-LAGOON-VARNISH = "${HOSTNAME}-${LAGOON_GIT_BRANCH:-undef}-${LAGOON_PROJECT}, " + req.http.X-LAGOON-VARNISH;
    set req.http.X-LAGOON-VARNISH-BYPASS = "true";
  } else if (req.http.Fastly-FF) {
    ## Pass all Requests which are handled via Fastly
    set req.http.X-LAGOON-VARNISH = "${HOSTNAME}-${LAGOON_GIT_BRANCH:-undef}-${LAGOON_PROJECT}, fastly";
    set req.http.X-LAGOON-VARNISH-BYPASS = "true";
    set req.http.X-Forwarded-For = req.http.Fastly-Client-IP;
  } else if (req.http.CF-RAY) {
    ## Pass all Requests which are handled via CloudFlare
    set req.http.X-LAGOON-VARNISH = "${HOSTNAME}-${LAGOON_GIT_BRANCH:-undef}-${LAGOON_PROJECT}, cloudflare";
    set req.http.X-LAGOON-VARNISH-BYPASS = "true";
    set req.http.X-Forwarded-For = req.http.CF-Connecting-IP;
  } else if (req.http.X-Pull) {
    ## Pass all Requests which are handled via KeyCDN
    set req.http.X-LAGOON-VARNISH = "${HOSTNAME}-${LAGOON_GIT_BRANCH:-undef}-${LAGOON_PROJECT}, keycdn";
    set req.http.X-LAGOON-VARNISH-BYPASS = "true";
  } else {
    ## We set a header to let a Varnish Chain know that it already has been varnishcached
    set req.http.X-LAGOON-VARNISH = "${HOSTNAME}-${LAGOON_GIT_BRANCH:-undef}-${LAGOON_PROJECT}";

    ## Allow to bypass based on env variable `VARNISH_BYPASS`
    set req.http.X-LAGOON-VARNISH-BYPASS = "${VARNISH_BYPASS:-false}";
  }

  # Websockets are piped
  if (req.http.Upgrade ~ "(?i)websocket") {
      return (pipe);
  }

  if (req.http.X-LAGOON-VARNISH-BYPASS == "true" || req.http.X-LAGOON-VARNISH-BYPASS == "TRUE") {
    return (pass);
  }

  # SA-CORE-2014-004 preventing access to /xmlrpc.php
  if (req.url ~ "^/xmlrpc.php$") {
    return (synth(701, "Unauthorized"));
  }

  # Strip out Google Analytics campaign variables. They are only needed
  # by the javascript running on the page
  # utm_source, utm_medium, utm_campaign, gclid
   if(req.url ~ "(\?|&)(gclid|utm_[a-z]+)=") {
     set req.url = regsuball(req.url, "(gclid|utm_[a-z]+)=[^\&]+&?", "");
     set req.url = regsub(req.url, "(\?|&)$", "");
   }

  # Bypass a cache hit (the request is still sent to the backend)
  if (req.method == "REFRESH") {
      if (!client.ip ~ purge) { return (synth(405, "Not allowed")); }
      set req.method = "GET";
      set req.hash_always_miss = true;
  }

  # Only allow BAN requests from IP addresses in the 'purge' ACL.
  if (req.method == "BAN" || req.method == "URIBAN" || req.method == "PURGE") {
      # Only allow BAN from defined ACL
      if (!client.ip ~ purge) {
          return (synth(403, "Your IP is not allowed."));
      }

      # Only allows BAN if the Host Header has the style of with "${SERVICE_NAME:-varnish}:8080" or "${SERVICE_NAME:-varnish}".
      # Such a request is only possible from within the Docker network, as a request from external goes trough the Kubernetes Router and for that needs a proper Host Header
      if (!req.http.host ~ "^${SERVICE_NAME:-varnish}(:\d+)?$") {
          return (synth(403, "BAN only allowed from within own network."));
      }

      if (req.method == "BAN") {
        # Logic for the ban, using the Cache-Tags header.
        if (req.http.Cache-Tags) {
            ban("obj.http.Cache-Tags ~ " + req.http.Cache-Tags);
            # Throw a synthetic page so the request won't go to the backend.
            return (synth(200, "Ban added."));
        }
        else {
            return (synth(403, "Cache-Tags header missing."));
        }
      }

      if (req.method == "URIBAN" || req.method == "PURGE") {
        ban("req.url == " + req.url);
        # Throw a synthetic page so the request won't go to the backend.
        return (synth(200, "Ban added."));
      }

  }

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

  # We only try to cache  GET and HEAD, other things are passed
  if (req.method != "GET" && req.method != "HEAD") {
    return (pass);
  }

  # Any requests with Basic Auth are passed
  if (req.http.Authorization || req.http.Authenticate)
  {
    return (pass);
  }

  ## Pass requests which are from blackfire
  if (req.http.X-Blackfire-Query) {
    return (pass);
  }

  # Some URLs should never be cached
  if (req.url ~ "^/status\.php$" ||
      req.url ~ "^/update\.php$" ||
      req.url ~ "^/admin([/?]|$).*$" ||
      req.url ~ "^/info([/?]|$).*$" ||
      req.url ~ "^/flag([/?]|$).*$" ||
      req.url ~ "^.*/system/files([/?]|$).*$" ||
      req.url ~ "^/cgi" ||
      req.url ~ "^/cgi-bin"
  ) {
    return (pass);
  }

  # Plupload likes to get piped
  if (req.url ~ "^.*/plupload-handle-uploads.*$"
  ) {
    return (pipe);
  }

  # Handle compression correctly. Different browsers send different
  # "Accept-Encoding" headers, even though they mostly all support the same
  # compression mechanisms. By consolidating these compression headers into
  # a consistent format, we can reduce the size of the cache and get more hits.=
  # @see: http:// varnish.projects.linpro.no/wiki/FAQ/Compression
  if (req.http.Accept-Encoding) {
    if (req.http.Accept-Encoding ~ "gzip") {
      # If the browser supports it, we'll use gzip.
      set req.http.Accept-Encoding = "gzip";
    }
    else if (req.http.Accept-Encoding ~ "deflate") {
      # Next, try deflate if it is supported.
      set req.http.Accept-Encoding = "deflate";
    }
    else {
      # Unknown algorithm. Remove it and send unencoded.
      unset req.http.Accept-Encoding;
    }
  }

  # Always cache the following file types for all users.
  if (req.url ~ "(?i)\.(css|js|jpg|jpeg|gif|ico|png|tiff|tif|img|tga|wmf|swf|html|htm|woff|woff2|mp4|ttf|eot|svg)(\?.*)?$") {
    unset req.http.Cookie;
  }

  # Remove all cookies that Drupal doesn't need to know about. ANY remaining
  # cookie will cause the request to pass-through to a backend. For the most part
  # we always set the NO_CACHE cookie after any POST request, disabling the
  # Varnish cache temporarily. The session cookie allows all authenticated users
  # to pass through as long as they're logged in.
  #
  # 1. Append a semi-colon to the front of the cookie string.
  # 2. Remove all spaces that appear after semi-colons.
  # 3. Match the cookies we want to keep, adding the space we removed
  #    previously, back. (\1) is first matching group in the regsuball.
  # 4. Remove all other cookies, identifying them by the fact that they have
  #    no space after the preceding semi-colon.
  # 5. Remove all spaces and semi-colons from the beginning and end of the
  #    cookie string.
  if (req.http.Cookie) {
    set req.http.CookieCheck = ";" + req.http.Cookie;
    set req.http.CookieCheck = regsuball(req.http.CookieCheck, "; +", ";");
    set req.http.CookieCheck = regsuball(req.http.CookieCheck, ";(${VARNISH_COOKIECHECK:-SESS[a-z0-9]+|SSESS[a-z0-9]+|NO_CACHE})=", "; \1=");
    set req.http.CookieCheck = regsuball(req.http.CookieCheck, ";[^ ][^;]*", "");
    set req.http.CookieCheck = regsuball(req.http.CookieCheck, "^[; ]+|[; ]+$", "");

    set req.http.Cookie = req.http.Cookie + ";";

    if (req.http.CookieCheck == "") {
      # If there are no remaining cookies, remove the cookie header. If there
      # aren't any cookie headers, Varnish's default behavior will be to cache
      # the page.

      unset req.http.CookieCheck;
      unset req.http.Cookie;
    }
    else {
      # If there is any cookies left (a session or NO_CACHE cookie), do not
      # cache the page. Pass it on to Apache directly.
      unset req.http.CookieCheck;
      return (pass);
    }
  }

  # Cacheable, lookup in cache.
  return (hash);
}

sub vcl_pipe {
  # Support for Websockets
  if (req.http.upgrade) {
      set bereq.http.upgrade = req.http.upgrade;
      set bereq.http.connection = req.http.connection;
  }
}

sub vcl_hit {
    if (obj.ttl >= 0s) {
        # normal hit
        return (deliver);
    }
    # We have no fresh fish. Lets look at the stale ones.
    if (std.healthy(req.backend_hint)) {
        # Backend is healthy. If the object is not older then 30secs, deliver it to the client
        # and automatically create a separate backend request to warm the cache for this request.
        if (obj.ttl + 30s > 0s) {
            set req.http.grace = "normal(limited)";
            return (deliver);
        } else {
            # No candidate for grace. Fetch a fresh object.
            return(miss);
        }
    } else {
        # backend is sick - use full grace
        if (obj.ttl + obj.grace > 0s) {
            set req.http.grace = "full";
            return (deliver);
        } else {
            # no graced object.
            return (miss);
        }
    }
}

sub vcl_backend_response {
  # Allow items to be stale if needed.
  set beresp.grace = 6h;

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

  # If the backend sends a X-LAGOON-VARNISH-BACKEND-BYPASS header we directly deliver
  if(beresp.http.X-LAGOON-VARNISH-BACKEND-BYPASS == "TRUE") {
    return (deliver);
  }

  # Cache 404 and 403 for 10 seconds
  if(beresp.status == 404 || beresp.status == 403) {
    set beresp.ttl = 10s;
    return (deliver);
  }

  # Don't allow static files to set cookies.
  if (bereq.url ~ "(?i)\.(css|js|jpg|jpeg|gif|ico|png|tiff|tif|img|tga|wmf|swf|html|htm|woff|woff2|mp4|ttf|eot|svg)(\?.*)?$") {
    # beresp == Back-end response from the web server.
    unset beresp.http.set-cookie;
    unset beresp.http.Cache-Control;

    # If an asset would come back with statuscode 500 we only cache it for 10 seconds instead of the usual static file cache
    if (beresp.status == 500) {
      set beresp.ttl = 10s;
      return (deliver);
    }

    set beresp.ttl = ${VARNISH_ASSETS_TTL:-2628001}s;
    set beresp.http.Cache-Control = "public, max-age=${VARNISH_ASSETS_TTL:-2628001}";
    set beresp.http.Expires = "" + (now + beresp.ttl);
  }

  return (deliver);
}

# Set a header to track a cache HIT/MISS.
sub vcl_deliver {
  if (obj.hits > 0) {
    set resp.http.X-Varnish-Cache = "HIT";
  }
  else {
    set resp.http.X-Varnish-Cache = "MISS";
  }

  # Remove ban-lurker friendly custom headers when delivering to client.
  unset resp.http.X-Url;
  unset resp.http.X-Host;

  # unset Cache-Tags Header by default, can be disabled with VARNISH_UNSET_HEADER_CACHE_TAGS=true
  if (!${VARNISH_UNSET_HEADER_CACHE_TAGS:-false}) {
    unset resp.http.Cache-Tags;
  }

  unset resp.http.X-Generator;
  unset resp.http.Server;
  # Inject information about grace
  if (req.http.grace) {
    set resp.http.X-Varnish-Grace = req.http.grace;
  }
  set resp.http.X-LAGOON = "${HOSTNAME}-${LAGOON_GIT_BRANCH:-undef}-${LAGOON_PROJECT}>" + resp.http.X-LAGOON;
  return (deliver);
}

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

sub vcl_synth {
  if (resp.status == 701) {
    set resp.status = 401;
    set resp.http.Content-Type = "text/plain; charset=utf-8";
    synthetic({"XMLRPC Interface is blocked due to SA-CORE-2014-004 - mail support@amazee.io if you need it."});
    return (deliver);
  }
  if (resp.status == 700) {
    # Set a status the client will understand
    set resp.status = 200;
    # Create our synthetic response
    synthetic("");
    return(deliver);
}
  return (deliver);
}

sub vcl_backend_error {
    # Restart the request, when we have a backend server error, to try another backend.
    # Restart max twice.
    if (bereq.retries < 2) {
      return(retry);
    }

    set beresp.http.Content-Type = "text/html; charset=utf-8";
    set beresp.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>Server Error</title>

We are sorry...

We encountered a server-side error. This means that the problem is not with your computer or Internet connection, but rather with the website's server.

We are currently working on solving this problem and apologise for the inconvenience.

            Technical Information

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

            XID: "} + bereq.xid + {"

"} );
    return (deliver);
}

vcl 4.0;

import std;

# set backend default
backend default {
  .host = "${VARNISH_BACKEND_HOST:-nginx}";
  .port = "${VARNISH_BACKEND_PORT:-8080}";
  .first_byte_timeout = 35m;
  .between_bytes_timeout = 10m;
}

# Allow purging from localhost
# @TODO allow from openshift network
acl purge {
      "127.0.0.1";
      "10.0.0.0"/8;
      "172.16.0.0"/12;
      "192.168.0.0"/16;
}

# This configuration is optimized for Drupal hosting:
# Respond to incoming requests.
sub vcl_recv {
  if (req.url ~ "^/varnish_status$")  {
    return (synth(200,"OK"));
  }
  # set the backend, which should be used:
  set req.backend_hint = default;

  # Always set the forward ip.
   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.X-LAGOON-VARNISH ) {
    ## Pass all Requests which are handled via an upstream Varnish
    set req.http.X-LAGOON-VARNISH = "${HOSTNAME}-${LAGOON_GIT_BRANCH:-undef}-${LAGOON_PROJECT}, " + req.http.X-LAGOON-VARNISH;
    set req.http.X-LAGOON-VARNISH-BYPASS = "true";
  } else if (req.http.Fastly-FF) {
    ## Pass all Requests which are handled via Fastly
    set req.http.X-LAGOON-VARNISH = "${HOSTNAME}-${LAGOON_GIT_BRANCH:-undef}-${LAGOON_PROJECT}, fastly";
    set req.http.X-LAGOON-VARNISH-BYPASS = "true";
    set req.http.X-Forwarded-For = req.http.Fastly-Client-IP;
  } else if (req.http.CF-RAY) {
    ## Pass all Requests which are handled via CloudFlare
    set req.http.X-LAGOON-VARNISH = "${HOSTNAME}-${LAGOON_GIT_BRANCH:-undef}-${LAGOON_PROJECT}, cloudflare";
    set req.http.X-LAGOON-VARNISH-BYPASS = "true";
    set req.http.X-Forwarded-For = req.http.CF-Connecting-IP;
  } else if (req.http.X-Pull) {
    ## Pass all Requests which are handled via KeyCDN
    set req.http.X-LAGOON-VARNISH = "${HOSTNAME}-${LAGOON_GIT_BRANCH:-undef}-${LAGOON_PROJECT}, keycdn";
    set req.http.X-LAGOON-VARNISH-BYPASS = "true";
  } else {
    ## We set a header to let a Varnish Chain know that it already has been varnishcached
    set req.http.X-LAGOON-VARNISH = "${HOSTNAME}-${LAGOON_GIT_BRANCH:-undef}-${LAGOON_PROJECT}";

    ## Allow to bypass based on env variable `VARNISH_BYPASS`
    set req.http.X-LAGOON-VARNISH-BYPASS = "${VARNISH_BYPASS:-false}";
  }

  # Websockets are piped
  if (req.http.Upgrade ~ "(?i)websocket") {
      return (pipe);
  }

  if (req.http.X-LAGOON-VARNISH-BYPASS == "true" || req.http.X-LAGOON-VARNISH-BYPASS == "TRUE") {
    return (pass);
  }

  # SA-CORE-2014-004 preventing access to /xmlrpc.php
  if (req.url ~ "^/xmlrpc.php$") {
    return (synth(701, "Unauthorized"));
  }

  # Strip out Google Analytics campaign variables. They are only needed
  # by the javascript running on the page
  # utm_source, utm_medium, utm_campaign, gclid
   if(req.url ~ "(\?|&)(gclid|utm_[a-z]+)=") {
     set req.url = regsuball(req.url, "(gclid|utm_[a-z]+)=[^\&]+&?", "");
     set req.url = regsub(req.url, "(\?|&)$", "");
   }

  # Bypass a cache hit (the request is still sent to the backend)
  if (req.method == "REFRESH") {
      if (!client.ip ~ purge) { return (synth(405, "Not allowed")); }
      set req.method = "GET";
      set req.hash_always_miss = true;
  }

  # Only allow BAN requests from IP addresses in the 'purge' ACL.
  if (req.method == "BAN") {
      # Only allow BAN from defined ACL
      if (!client.ip ~ purge) {
          return (synth(403, "Your IP is not allowed."));
      }

      # Only allows BAN if the Host Header has the style of with "${SERVICE_NAME:-varnish}:8080" or "${SERVICE_NAME:-varnish}".
      # Such a request is only possible from within the Docker network, as a request from external goes trough the Kubernetes Router and for that needs a proper Host Header
      if (!req.http.host ~ "^${SERVICE_NAME:-varnish}(:\d+)?$") {
          return (synth(403, "BAN only allowed from within own network."));
      }

      # Logic for the ban, using the Cache-Tags header.
      if (req.http.Cache-Tags) {
          ban("obj.http.Cache-Tags ~ " + req.http.Cache-Tags);
      }
      else {
          return (synth(403, "Cache-Tags header missing."));
      }

      # Throw a synthetic page so the request won't go to the backend.
      return (synth(200, "Ban added."));
  }

  if (req.method == "URIBAN") {
    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."));
  }

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

  # We only try to cache  GET and HEAD, other things are passed
  if (req.method != "GET" && req.method != "HEAD") {
    return (pass);
  }

  # Any requests with Basic Auth are passed
  if (req.http.Authorization || req.http.Authenticate)
  {
    return (pass);
  }

  ## Pass requests which are from blackfire
  if (req.http.X-Blackfire-Query) {
    return (pass);
  }

  # Some URLs should never be cached
  if (req.url ~ "^/status\.php$" ||
      req.url ~ "^/update\.php$" ||
      req.url ~ "^/admin([/?]|$).*$" ||
      req.url ~ "^/info([/?]|$).*$" ||
      req.url ~ "^/flag([/?]|$).*$" ||
      req.url ~ "^.*/system/files([/?]|$).*$" ||
      req.url ~ "^/cgi" ||
      req.url ~ "^/cgi-bin"
  ) {
    return (pass);
  }

  # Plupload likes to get piped
  if (req.url ~ "^.*/plupload-handle-uploads.*$"
  ) {
    return (pipe);
  }

  # Handle compression correctly. Different browsers send different
  # "Accept-Encoding" headers, even though they mostly all support the same
  # compression mechanisms. By consolidating these compression headers into
  # a consistent format, we can reduce the size of the cache and get more hits.=
  # @see: http:// varnish.projects.linpro.no/wiki/FAQ/Compression
  if (req.http.Accept-Encoding) {
    if (req.http.Accept-Encoding ~ "gzip") {
      # If the browser supports it, we'll use gzip.
      set req.http.Accept-Encoding = "gzip";
    }
    else if (req.http.Accept-Encoding ~ "deflate") {
      # Next, try deflate if it is supported.
      set req.http.Accept-Encoding = "deflate";
    }
    else {
      # Unknown algorithm. Remove it and send unencoded.
      unset req.http.Accept-Encoding;
    }
  }

  # Always cache the following file types for all users.
  if (req.url ~ "(?i)\.(css|js|jpg|jpeg|gif|ico|png|tiff|tif|img|tga|wmf|swf|html|htm|woff|woff2|mp4|ttf|eot|svg)(\?.*)?$") {
    unset req.http.Cookie;
  }

  # Remove all cookies that Drupal doesn't need to know about. ANY remaining
  # cookie will cause the request to pass-through to a backend. For the most part
  # we always set the NO_CACHE cookie after any POST request, disabling the
  # Varnish cache temporarily. The session cookie allows all authenticated users
  # to pass through as long as they're logged in.
  #
  # 1. Append a semi-colon to the front of the cookie string.
  # 2. Remove all spaces that appear after semi-colons.
  # 3. Match the cookies we want to keep, adding the space we removed
  #    previously, back. (\1) is first matching group in the regsuball.
  # 4. Remove all other cookies, identifying them by the fact that they have
  #    no space after the preceding semi-colon.
  # 5. Remove all spaces and semi-colons from the beginning and end of the
  #    cookie string.
  if (req.http.Cookie) {
    set req.http.CookieCheck = ";" + req.http.Cookie;
    set req.http.CookieCheck = regsuball(req.http.CookieCheck, "; +", ";");
    set req.http.CookieCheck = regsuball(req.http.CookieCheck, ";(${VARNISH_COOKIECHECK:-SESS[a-z0-9]+|SSESS[a-z0-9]+|NO_CACHE})=", "; \1=");
    set req.http.CookieCheck = regsuball(req.http.CookieCheck, ";[^ ][^;]*", "");
    set req.http.CookieCheck = regsuball(req.http.CookieCheck, "^[; ]+|[; ]+$", "");

    set req.http.Cookie = req.http.Cookie + ";";

    if (req.http.CookieCheck == "") {
      # If there are no remaining cookies, remove the cookie header. If there
      # aren't any cookie headers, Varnish's default behavior will be to cache
      # the page.

      unset req.http.CookieCheck;
      unset req.http.Cookie;
    }
    else {
      # If there is any cookies left (a session or NO_CACHE cookie), do not
      # cache the page. Pass it on to Apache directly.
      unset req.http.CookieCheck;
      return (pass);
    }
  }

  # Cacheable, lookup in cache.
  return (hash);
}

sub vcl_pipe {
  # Support for Websockets
  if (req.http.upgrade) {
      set bereq.http.upgrade = req.http.upgrade;
      set bereq.http.connection = req.http.connection;
  }
}

sub vcl_hit {
    if (obj.ttl >= 0s) {
        # normal hit
        return (deliver);
    }
    # We have no fresh fish. Lets look at the stale ones.
    if (std.healthy(req.backend_hint)) {
        # Backend is healthy. If the object is not older then 30secs, deliver it to the client
        # and automatically create a separate backend request to warm the cache for this request.
        if (obj.ttl + 30s > 0s) {
            set req.http.grace = "normal(limited)";
            return (deliver);
        } else {
            # No candidate for grace. Fetch a fresh object.
            return(miss);
        }
    } else {
        # backend is sick - use full grace
        if (obj.ttl + obj.grace > 0s) {
            set req.http.grace = "full";
            return (deliver);
        } else {
            # no graced object.
            return (miss);
        }
    }
}

sub vcl_backend_response {
  # Allow items to be stale if needed.
  set beresp.grace = 6h;

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

  # If the backend sends a X-LAGOON-VARNISH-BACKEND-BYPASS header we directly deliver
  if(beresp.http.X-LAGOON-VARNISH-BACKEND-BYPASS == "TRUE") {
    return (deliver);
  }

  # Cache 404 and 403 for 10 seconds
  if(beresp.status == 404 || beresp.status == 403) {
    set beresp.ttl = 10s;
    return (deliver);
  }

  # Don't allow static files to set cookies.
  if (bereq.url ~ "(?i)\.(css|js|jpg|jpeg|gif|ico|png|tiff|tif|img|tga|wmf|swf|html|htm|woff|woff2|mp4|ttf|eot|svg)(\?.*)?$") {
    # beresp == Back-end response from the web server.
    unset beresp.http.set-cookie;
    unset beresp.http.Cache-Control;

    # If an asset would come back with statuscode 500 we only cache it for 10 seconds instead of the usual static file cache
    if (beresp.status == 500) {
      set beresp.ttl = 10s;
      return (deliver);
    }

    set beresp.ttl = ${VARNISH_ASSETS_TTL:-2628001}s;
    set beresp.http.Cache-Control = "public, max-age=${VARNISH_ASSETS_TTL:-2628001}";
    set beresp.http.Expires = "" + (now + beresp.ttl);
  }

  return (deliver);
}

# Set a header to track a cache HIT/MISS.
sub vcl_deliver {
  if (obj.hits > 0) {
    set resp.http.X-Varnish-Cache = "HIT";
  }
  else {
    set resp.http.X-Varnish-Cache = "MISS";
  }

  # Remove ban-lurker friendly custom headers when delivering to client.
  unset resp.http.X-Url;
  unset resp.http.X-Host;

  # unset Cache-Tags Header by default, can be disabled with VARNISH_UNSET_HEADER_CACHE_TAGS=true
  if (!${VARNISH_UNSET_HEADER_CACHE_TAGS:-false}) {
    unset resp.http.Cache-Tags;
  }

  unset resp.http.X-Generator;
  unset resp.http.Server;
  # Inject information about grace
  if (req.http.grace) {
    set resp.http.X-Varnish-Grace = req.http.grace;
  }
  set resp.http.X-LAGOON = "${HOSTNAME}-${LAGOON_GIT_BRANCH:-undef}-${LAGOON_PROJECT}>" + resp.http.X-LAGOON;
  return (deliver);
}

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

sub vcl_synth {
  if (resp.status == 701) {
    set resp.status = 401;
    set resp.http.Content-Type = "text/plain; charset=utf-8";
    synthetic({"XMLRPC Interface is blocked due to SA-CORE-2014-004 - mail support@amazee.io if you need it."});
    return (deliver);
  }
  if (resp.status == 700) {
    # Set a status the client will understand
    set resp.status = 200;
    # Create our synthetic response
    synthetic("");
    return(deliver);
}
  return (deliver);
}

sub vcl_backend_error {
    # Restart the request, when we have a backend server error, to try another backend.
    # Restart max twice.
    if (bereq.retries < 2) {
      return(retry);
    }

    set beresp.http.Content-Type = "text/html; charset=utf-8";
    set beresp.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>Server Error</title>

We are sorry...

We encountered a server-side error. This means that the problem is not with your computer or Internet connection, but rather with the website's server.

We are currently working on solving this problem and apologise for the inconvenience.

            Technical Information

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

            XID: "} + bereq.xid + {"

"} );
    return (deliver);
}


Fix the TLS message (#3358)

Fix the TLS message (#3358)

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

}

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

}


Varnish rule to cache static doc files

Varnish rule to cache static doc files

Normalize req.url part of vcl_hash for static files at project/doc/*
to avoid reading these files more than once from backend.

# Varnish VCL config for www.rust-ci.org

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

acl purge {
  "localhost";
}

sub vcl_recv {
  set req.backend = nginx;

  if ((req.url ~ "/p/") || (req.url ~ "/admin") || (req.url ~ "/artifacts") || (req.url ~ "/callback")) {
    return(pass);
  }

  # Normalize host header (for test env)
  set req.http.Host = "www.rust-ci.org";

  # Remove cookies from request
  unset req.http.cookie;

  if (req.request == "PURGE") {
    # Ignore query string (python-varnish adds "?" to PURGE URLs)
    set req.url = regsub(req.url, "\?.*$", "");
    if (!client.ip ~ purge) {
      error 405 "Not allowed.";
    }
    return (lookup);
  }
}

sub vcl_hash {

	# All the project/doc/.* static files are the same, except for search-index.js
	# Normalize the hashed req.url so that these files are only fetched once from S3

	if (req.url ~ "doc\/[^.]*\.(woff|css|js)$" && req.url !~ "search-index.js$") {
		hash_data(regsub(req.url, "(.*)\/doc", "/normalized"));
	} else {
		hash_data(req.url);
	}

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

sub vcl_hit {
  if (req.request == "PURGE") {
    purge;
    error 200 "Purged.";
  }
}

sub vcl_miss {
  if (req.request == "PURGE") {
    purge;
    error 200 "Purged.";
  }
}

sub vcl_fetch {
  # Remove set-cookie from backend response
  if (!(req.url ~ "/p/") && !(req.url ~ "/admin") && !(req.url ~ "/artifacts") && !(req.url ~ "/callback")) {
    unset beresp.http.set-cookie;
  }

  # Remove Expires from backend
  unset beresp.http.expires;

  # Set the clients TTL on this object
  if (req.url ~ "^/static") {
    set beresp.http.cache-control = "max-age=31536000";
  } else {
    set beresp.http.cache-control = "max-age=0";
  }

  # Set how long Varnish will keep it
  set beresp.ttl = 10w;

  # marker for vcl_deliver to reset Age:
  set beresp.http.magicmarker = "1";
}

sub vcl_deliver {
  if (resp.http.magicmarker) {
    # Remove the magic marker
    unset resp.http.magicmarker;

    # By definition we have a fresh object
    set resp.http.age = "0";
  }

  # Set HIT/MISS indicator
  if (obj.hits > 0) {
    set resp.http.X-Cache = "HIT";
  } else {
    set resp.http.X-Cache = "MISS";
  }
}

# Varnish VCL config for www.rust-ci.org

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

acl purge {
  "localhost";
}

sub vcl_recv {
  set req.backend = nginx;

  if ((req.url ~ "/p/") || (req.url ~ "/admin") || (req.url ~ "/artifacts") || (req.url ~ "/callback")) {
    return(pass);
  }

  # Normalize host header (for test env)
  set req.http.Host = "www.rust-ci.org";

  # Remove cookies from request
  unset req.http.cookie;

  if (req.request == "PURGE") {
    # Ignore query string (python-varnish adds "?" to PURGE URLs)
    set req.url = regsub(req.url, "\?.*$", "");
    if (!client.ip ~ purge) {
      error 405 "Not allowed.";
    }
    return (lookup);
  }
}

sub vcl_hit {
  if (req.request == "PURGE") {
    purge;
    error 200 "Purged.";
  }
}

sub vcl_miss {
  if (req.request == "PURGE") {
    purge;
    error 200 "Purged.";
  }
}

sub vcl_fetch {
  # Remove set-cookie from backend response
  if (!(req.url ~ "/p/") && !(req.url ~ "/admin") && !(req.url ~ "/artifacts") && !(req.url ~ "/callback")) {
    unset beresp.http.set-cookie;
  }

  # Remove Expires from backend
  unset beresp.http.expires;

  # Set the clients TTL on this object
  if (req.url ~ "^/static") {
    set beresp.http.cache-control = "max-age=31536000";
  } else {
    set beresp.http.cache-control = "max-age=0";
  }

  # Set how long Varnish will keep it
  set beresp.ttl = 10w;

  # marker for vcl_deliver to reset Age:
  set beresp.http.magicmarker = "1";
}

sub vcl_deliver {
  if (resp.http.magicmarker) {
    # Remove the magic marker
    unset resp.http.magicmarker;

    # By definition we have a fresh object
    set resp.http.age = "0";
  }

  # Set HIT/MISS indicator
  if (obj.hits > 0) {
    set resp.http.X-Cache = "HIT";
  } else {
    set resp.http.X-Cache = "MISS";
  }
}


Moved the logic to set a TTL for 404s.

Moved the 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 {

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