varnish drupal

 https://www.howtoforge.com/how-to-install-and-configure-varnish-with-apache-on-ubuntu-1804/

lagoon drupal varnish docker container+more https://lagoon.readthedocs.io/en/latest/using_lagoon/drupal/services/varnish/  

 https://www.varnish-software.com/wiki/content/tutorials/drupal/drupal_st... 

default.vcl

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

# Define the internal network subnet.
# These are used below to allow internal access to certain files while not
# allowing access from the public internet.
acl internal {
  "xxx.xxx.xxx.xxx"/32;  # gateway for authors or IP range if no gateway
}

acl purgers {
	"xxx.xxx.xxx.xxx"/32;  # web serve IP
	"xxx.xxx.xxx.xxx"/32;  # web serve IP
	"xxx.xxx.xxx.xxx"/32;  # web serve IP
}
 
# Default backend definition.  Set this to point to your content
# server.
#
backend lb1 {
     .host = "examplehostloadbalancer1";
     .port = "80";
     .first_byte_timeout = 300s;
     .probe = {
       .url = "/health";
       .timeout = 3 s;
       .interval = 10s;
       .window = 4;
       .threshold = 3;
       .initial = 3;
     }
}

backend lb2 {
     .host = "examplehostloadbalancer2";
     .port = "80";
     .first_byte_timeout = 300s;
     .probe = {
       .url = "/health";
       .timeout = 3 s;
       .interval = 10s;
       .window = 4;
       .threshold = 3;
       .initial = 3;
     }
}

backend lb3 {
     .host = "examplehostloadbalancer3";
     .port = "80";
     .first_byte_timeout = 300s;
     .probe = {
       .url = "/health";
       .timeout = 3 s;
       .interval = 10s;
       .window = 4;
       .threshold = 3;
       .initial = 3;
     }
}

sub vcl_init {
    new exampleloadbalancer_director = directors.round_robin();
    exampleloadbalancer_director.add_backend(lb1);
#    exampleloadbalancer_director.add_backend(lb2);#optional if you have a cluster
#    exampleloadbalancer_director.add_backend(lb3);#optional if you have a cluster
   
}

sub vcl_backend_response {

 if ( bereq.url ~ "(?i)\.(bz2|css|eot|gif|gz|html?|ico|jpe?g|js|mp3|ogg|otf|pdf|png|rar|svg|swf|tbz|tgz|ttf|woff2?|zip)(\?(itok=)?[a-z0-9_=\.\-]+)?$"
    ) {
      unset beresp.http.set-cookie;
    }
    
# Set ban-lurker friendly custom headers.
 set beresp.http.X-Url = bereq.url;
 set beresp.http.X-Host = bereq.http.host;
 set beresp.ttl=30d;
 
     # Cache 404s, 301s, at 500s with a short lifetime to protect the backend.
    if (beresp.status == 404 || beresp.status == 301 || beresp.status == 500) {
        set beresp.ttl = 5m;
    }
    if (beresp.status == 400) {
        set beresp.ttl = 0s;
    }

 if (bereq.url ~ "views\/ajax") {
	unset beresp.http.Cache-Control;
	set beresp.http.Cache-Control = "public, max-age=2629800";
 }
 
   if (bereq.url ~ "^.*/connect/.*$" ||
      bereq.url ~ "^.*/nous-joindre/.*$" ) {
       	unset beresp.http.Cache-Control;
       	set beresp.http.Cache-Control = "no-cache, max-age=0";
  }
 
}
 
sub vcl_backend_fetch {
	if (bereq.http.host ~ "dev.hostname.com" || bereq.http.host ~ "prod.hostname.com" ) {
		set bereq.http.X-Forwarded-Proto = "https";
	}
	return (fetch);
}

# Respond to incoming requests.
sub vcl_recv {

    # Only allow BAN requests from IP addresses in the 'purge' ACL.
    if (req.method == "BAN") {
        # Same ACL check as above:
        if (!std.ip(regsuball(req.http.X-Forwarded-For, ", xxx\.xxx\.xxx\.xx.", ""), "0.0.0.0") ~ purgers) {
            return (synth(403, "Not allowed."));
        }

        # Logic for the ban, using the Cache-Tags header. For more info
        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."));
    }

  # Use anonymous, cached pages if all backends are down.
  
  if (std.ip(regsuball(req.http.X-Forwarded-For, ", xxx\.xxx\.xxx\.xx.", ""), "0.0.0.0") ~ internal) {
	#if admin user prefer web1 
	if (std.healthy(lb1)) { 
		set req.backend_hint = lb1;
	}
	else {
		if (std.healthy(lb2)) {
			set req.backend_hint = lb2;
		}
		else {
			if (std.healthy(lb3)) {
				#set req.backend_hint = lb3;	
			}
		}	
	}
  }
  else {
	set req.backend_hint = exampleloadbalancer_director.backend();
  }
  
  set req.url = std.querysort(req.url); # Normalize the query arguments
  
 
  # Allow the backend to serve up stale content if it is responding slowly.
  #set req.grace = 6h;
 
	if (req.restarts == 0) {
		set req.http.X-Forwarded-For = regsuball(req.http.X-Forwarded-For, ", xxx\.xxx\.xx\.xx.", "");
	}
 
 
	# Strip hash, server doesn't need it.
	if (req.url ~ "\#") {
		set req.url = regsub(req.url, "\#.*$", "");
	}

	# Strip a trailing ? if it exists
	if (req.url ~ "\?$") {
		set req.url = regsub(req.url, "\?$", "");
	}  
 
# Only cache GET or HEAD requests. This makes sure the POST requests are always passed.
	if (req.method != "GET" && req.method != "HEAD") {
#JJJ Unless it's a an ajax call	
		if (req.url !~ "views\/ajax") {
			return (pass);
		}
	}
	
  # Do not cache these paths.
  if (req.url ~ "^/status\.php$" ||
      req.url ~ "^/update\.php$" ||
      req.url ~ "^/admin$" ||
      req.url ~ "^/admin/.*$" ||
      req.url ~ "^/flag/.*$" ||
      req.url ~ "^.*/contact-example/.*$" ||
      req.url ~ "^.*/connect-example/.*$" ||
      req.url ~ "^.*/nous-joindre-example/.*$" ||
      req.url ~ "^.*/subscribe-example/.*$" ||
      req.url ~ "^.*/abonnement-example/.*$" ||
      req.url ~ "^.*/greetings-example/.*$" ||
      req.url ~ "^.*/salutations-example/.*$" ||
      req.url ~ "^.*/confirmation-example/.*$" ||
      req.url ~ "^.*/unsubscribe-example/.*$" ||
      req.url ~ "^.*rss-example$" ||
      req.url ~ "^/$" ||
      req.url ~ "^$" ) {
       return (pass);
  }

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


  # Always cache the following file types for all users. This list of extensions
  # appears twice, once here and again in vcl_fetch so make sure you edit both
  # and keep them equal.
  if (req.url ~ "(?i)\.(pdf|asc|dat|txt|doc|xls|ppt|tgz|csv|png|gif|jpeg|jpg|ico|swf|css|js)(\?.*)?$") {
    unset req.http.Cookie;
  }
  
  # Do not allow outside access to cron.php or install.php.
  if (req.url ~ "^/(cron|install)\.php$" && std.ip(regsuball(req.http.X-Forwarded-For, ", xxx\.xxx\.xx\.xx.", ""), "0.0.0.0") !~ internal) {
    # Have Varnish throw the error directly.
    return(synth(403, "Access denied"));
  }
  
  # Do not allow outside access to parallel.live.b.net .
  if (req.http.host ~ "parallel.live.b.net" && std.ip(regsuball(req.http.X-Forwarded-For, ", xxx\.xxx\.xx\.xx.", ""), "0.0.0.0") !~ internal) {
    # Have Varnish throw the error directly.
    return(synth(403, "Access denied"));
  }
  
  # Do not allow outside access to querystring with hash=
  if (req.url ~ "^.*hash=.*$" && std.ip(regsuball(req.http.X-Forwarded-For, ", xxx\.xxx\.xx\.xx.", ""), "0.0.0.0") !~ internal) {
    # Have Varnish throw the error directly.
    return(synth(403, "Access denied"));
  }
  
  # Do not allow outside access to /admin /user and /services
  
  if (req.url ~ "^(.*/admin/?.*)|(.*/user/?.*)|(.*/services/rest/?.*)|(.*/internal/?.*)$" && std.ip(regsuball(req.http.X-Forwarded-For, ", xxx\.xxx\.xx\.xx.", ""), "0.0.0.0") !~ internal) {
    # Have Varnish throw the error directly.
    return(synth(403, "Access denied"));
    # Use a custom error page that you've defined in Drupal at the path "404".
    # set req.url = "/404";
  }
  
  # Remove all cookies that Drupal doesn't need to know about. We explicitly
  # list the ones that Drupal does need, the SESS and NO_CACHE. If, after
  # running this code we find that either of these two cookies remains, we
  # will pass as the page cannot be cached.
  if (req.http.Cookie) {
    # 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.
    set req.http.Cookie = ";" + req.http.Cookie;
    set req.http.Cookie = regsuball(req.http.Cookie, "; +", ";");
    set req.http.Cookie = regsuball(req.http.Cookie, ";(SESS[a-z0-9]+|SSESS[a-z0-9]+|NO_CACHE)=", "; \1=");
    set req.http.Cookie = regsuball(req.http.Cookie, ";[^ ][^;]*", "");
    set req.http.Cookie = regsuball(req.http.Cookie, "^[; ]+|[; ]+$", "");

    if (req.http.Cookie == "") {
      # 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.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.
      return (pass);
    }
  }
}

# The routine when we deliver the HTTP request to the user
# Last chance to modify headers that are sent to the client
sub vcl_deliver {
# Called before a cached object is delivered to the client.

	# Remove the headers that allow smart bans before sending to client
	unset resp.http.x-url;
	unset resp.http.x-host;
	unset resp.http.Cache-Tags;



	if (obj.hits > 0) { # Add debug header to see if it's a HIT/MISS and the number of hits, disable when not needed
		set resp.http.X-Cache = "HIT";
	} else {
		set resp.http.X-Cache = "MISS";
	}
	# Please note that obj.hits behaviour changed in 4.0, now it counts per objecthead, not per object
	# and obj.hits may not be reset in some cases where bans are in use. See bug 1492 for details.
	# So take hits with a grain of salt
	set resp.http.X-Cache-Hits = obj.hits;

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

	# Remove some headers: Apache version & OS
	unset resp.http.Server;
	unset resp.http.X-Drupal-Cache;
	unset resp.http.X-Varnish;
	unset resp.http.Via;
	unset resp.http.Link;

	return (deliver);
}