├── .gitignore
├── README.md
├── conf.d
├── error-404.vcl
├── error.vcl
├── fetch
│ ├── drupal7.vcl
│ ├── forkcms.vcl
│ └── wordpress.vcl
└── receive
│ ├── drupal7.vcl
│ ├── forkcms.vcl
│ └── wordpress.vcl
├── custom.acl.vcl-sample
├── custom.backend.vcl-sample
├── custom.fetch.vcl-sample
├── custom.recv.vcl-sample
└── production.vcl
/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore these files
2 | .gitconfig
3 | secret
4 | default.vcl
5 | custom.acl.vcl
6 | custom.backend.vcl
7 | custom.fetch.vcl
8 | custom.recv.vcl
9 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Varnish Configuration Templates (boilerplate)
2 |
3 | ### 🚀 Need help implementing Varnish?
4 |
5 | I'm available [for consultancy](https://ma.ttias.be/consultancy/) if you're struggling with implementing Varnish and speeding up your site. Don't be afraid to [reach out!](https://ma.ttias.be/consultancy/)
6 |
7 | ### Warning: Varnish 3 is [end-of-life](https://ma.ttias.be/varnish-cache-3-0-is-end-of-life/)
8 |
9 | You can still use Varnish 3 of course, but there will be no more security or bug fixes to the Varnish 3.x release. It's probably wise to focus your Varnish adventures on the new [varnish 4 VCL config template](https://ma.ttias.be/varnish-4-0-0-released-together-with-configuration-templates/).
10 |
11 |
12 | ### Installation
13 |
14 | You can use the configuration templates found in this repository to quickly get started with a complete Varnish configuration that offers support for most functionality. Start of by looking into "production.vcl" and taking the bits you need, copy it to your own default.vcl.
15 |
16 | ### What is it?
17 |
18 | A set of configuration samples used for Varnish 3.0. This includes templates for:
19 | * Wordpress
20 | * Drupal (works decently for Drupal 7, depends on your addons obviously)
21 | * Joomla (WIP)
22 | * Fork CMS
23 | * OpenPhoto
24 |
25 | And various configuration for:
26 |
27 | * Server-side URL rewriting
28 | * Clean error pages for debugging
29 | * Virtual Host implementations
30 | * Various header normalizations
31 | * Cookie manipulations
32 | * 301/302 redirects from within Varnish
33 |
34 | ### Common troubleshooting
35 |
36 | Common list of errors and their fixes:
37 |
38 | * [FetchError http first read error: -1 11 (Resource temporarily unavailable)](https://ma.ttias.be/varnish-fetcherror-http-first-read-error-1-11-resource-temporarily-unavailable/)
39 | * [FetchError: straight insufficient bytes](https://ma.ttias.be/varnish-fetcherror-straight-insufficient-bytes/)
40 | * [FetchError: Gunzip+ESI Failed at the very end](https://ma.ttias.be/varnish-fetcherror-testgunzip-gunzip-esi-failed-very-end/)
41 |
42 | Basic troubleshooting:
43 |
44 | * [Test if your Varnish VCL compiles and Varnish starts](https://ma.ttias.be/varnish-running-in-foreground-but-fails-to-run-as-servicedaemon/)
45 | * [See which cookies are being stripped in your VCL](https://ma.ttias.be/varnish-tip-see-cookies-stripped-vcl/)
46 | * [Reload Varnish VCL without losing cache data](https://ma.ttias.be/reload-varnish-vcl-without-losing-cache-data/)
47 | * [Combine Apache'S HTTP authentication with Varnish IP whitelisting](https://ma.ttias.be/apache-http-authentication-with-x-forwarded-for-ip-whitelisting-in-varnish/)
48 |
49 | [Click here for a Varnish 4 VCL config template](https://github.com/mattiasgeniar/varnish-4.0-configuration-templates)
50 |
51 | [Click here for a Varnish 5 VCL config template](https://github.com/mattiasgeniar/varnish-5.0-configuration-templates)
52 |
--------------------------------------------------------------------------------
/conf.d/error-404.vcl:
--------------------------------------------------------------------------------
1 | # The vcl_error() procedure
2 | set obj.http.Content-Type = "text/html; charset=utf-8";
3 | set obj.http.Retry-After = "5";
4 |
5 | synthetic {"
6 |
7 |
8 |
34 |
35 | We're very sorry, but the page could not be loaded properly.
36 |
37 |
38 |
39 |
This should be fixed very soon, and we apologize for any inconvenience.
40 |
41 |
42 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
Debug Information
52 |
53 |
Variable
54 |
Value
55 |
56 |
57 |
General
58 |
59 |
60 |
XID
61 |
"} + req.xid + {"
62 |
63 |
64 |
Time
65 |
"} + now + {"
66 |
67 |
68 |
Request
69 |
70 |
71 |
HTTP host
72 |
"} + req.http.Host + {"
73 |
74 |
75 |
Request type
76 |
"} + req.request + {"
77 |
78 |
79 |
HTTP Protocol version
80 |
"} + req.proto + {"
81 |
82 |
83 |
URL
84 |
"} + req.url + {"
85 |
86 |
87 |
Cookies
88 |
"} + regsuball(req.http.cookie, "; ", " ") + {"
89 |
90 |
91 |
Accept-Encoding
92 |
"} + req.http.Accept-Encoding + {"
93 |
94 |
95 |
Cache-Control
96 |
"} + req.http.Cache-Control + {"
97 |
98 |
99 |
HTTP header
100 |
"} + req.http.header + {"
101 |
102 |
103 |
GZIP supported
104 |
"} + req.can_gzip + {"
105 |
106 |
107 |
Backend
108 |
"} + req.backend + {"
109 |
110 |
111 |
Server
112 |
113 |
114 |
Identity
115 |
"} + server.identity + {"
116 |
117 |
118 |
IP:port
119 |
"} + server.ip + {":"} + server.port + {"
120 |
121 |
122 |
Client
123 |
124 |
125 |
IP
126 |
"} + client.ip + {"
127 |
128 |
129 |
130 |
131 |
132 |
134 |
135 |
136 |
137 |
138 | "};
139 |
--------------------------------------------------------------------------------
/conf.d/fetch/drupal7.vcl:
--------------------------------------------------------------------------------
1 | # The vcl_fetch routine, when the request is fetched from the backend
2 |
3 | # For static content related to the theme, strip all backend cookies
4 | # Before you blindly enable this, have a read here: http://mattiasgeniar.be/2012/11/28/stop-caching-static-files/
5 | if (req.url ~ "^/themes/" && req.url ~ "\.(css|js|png|gif|jp(e?)g)") {
6 | unset beresp.http.cookie;
7 | }
8 |
9 | # A TTL of 30 minutes
10 | set beresp.ttl = 1800s;
11 |
--------------------------------------------------------------------------------
/conf.d/fetch/forkcms.vcl:
--------------------------------------------------------------------------------
1 | # The vcl_fetch routine, when the request is fetched from the backend
2 |
3 | # If it's from the /private area, don't touch it
4 | if (req.url !~ "(private|backend)") {
5 | # Remove the PHPSESSID cookie
6 | set beresp.http.cookie = regsuball(beresp.http.cookie, "PHPSESSID=[^;]+(; )?", "");
7 | }
8 |
9 | # For static content related to the theme, strip all backend cookies
10 | # Before you blindly enable this, have a read here: http://mattiasgeniar.be/2012/11/28/stop-caching-static-files/
11 | if (req.url ~ "^/frontend/" && req.url ~ "\.(css|js|png|gif|jp(e?)g)") {
12 | unset beresp.http.cookie;
13 | }
14 |
15 | # A TTL of 30 minutes
16 | set beresp.ttl = 1800s;
17 |
--------------------------------------------------------------------------------
/conf.d/fetch/wordpress.vcl:
--------------------------------------------------------------------------------
1 | # Drop any cookies Wordpress tries to send back to the client.
2 | if (!(req.url ~ "wp-(login|admin)")) {
3 | unset beresp.http.set-cookie;
4 | }
5 |
--------------------------------------------------------------------------------
/conf.d/receive/drupal7.vcl:
--------------------------------------------------------------------------------
1 | # A configuration file specific for Drupal 7
2 |
3 | # Either the admin pages or the login
4 | if (req.url ~ "/admin/?") {
5 | # Don't cache, pass to backend
6 | return (pass);
7 | }
8 |
9 | # Static content unique to the theme can be cached (so no user uploaded images)
10 | # Before you blindly enable this, have a read here: http://mattiasgeniar.be/2012/11/28/stop-caching-static-files/
11 | if (req.url ~ "^/themes/" && req.url ~ "\.(css|js|png|gif|jp(e)?g)") {
12 | unset req.http.cookie;
13 | }
14 |
15 | # Don't cache the install, update or cron files in Drupal
16 | if (req.url ~ "install\.php|update\.php|cron\.php") {
17 | return (pass);
18 | }
19 |
20 | # Uncomment this to trigger the vcl_error() subroutine, which will HTML output you some variables (HTTP 700 = pretty debug)
21 | #error 700;
22 |
23 | # Anything else left?
24 | if (!req.http.cookie) {
25 | unset req.http.cookie;
26 | }
27 |
28 | # Try a cache-lookup
29 | return (lookup);
30 |
--------------------------------------------------------------------------------
/conf.d/receive/forkcms.vcl:
--------------------------------------------------------------------------------
1 | # A configuration file specific to Fork CMS
2 |
3 | # Either the admin pages or the login
4 | if (req.url ~ "(private|backend)") {
5 | # Don't cache, pass to backend
6 | return (pass);
7 | }
8 |
9 | # Someone placed comments on the site, there are still cookies left
10 | if (req.http.cookie ~ "comment_(website|email|author)") {
11 | # Don't cache these pages, allow direct request
12 | return (pass);
13 | }
14 |
15 | # If no "comment_" cookies were found, we will simply remove the PHPSESSID
16 | # If your PHP configuration has a different naming for the PHP Session IDs, change it here
17 | set req.http.Cookie = regsuball(req.http.Cookie, "PHPSESSID=[^;]+(; )?", "");
18 |
19 | # Anything else left?
20 | if (!req.http.cookie) {
21 | unset req.http.cookie;
22 | }
23 |
24 | # Try a cache-lookup
25 | return (lookup);
26 |
--------------------------------------------------------------------------------
/conf.d/receive/wordpress.vcl:
--------------------------------------------------------------------------------
1 | # Drop any cookies sent to Wordpress.
2 | if (!(req.url ~ "wp-(login|admin)")) {
3 | unset req.http.cookie;
4 | }
5 |
6 | # Anything else left?
7 | if (!req.http.cookie) {
8 | unset req.http.cookie;
9 | }
10 |
11 | # Try a cache-lookup
12 | return (lookup);
13 |
--------------------------------------------------------------------------------
/custom.acl.vcl-sample:
--------------------------------------------------------------------------------
1 | acl purge {
2 | # For now, I'll only allow purges coming from localhost
3 | "127.0.0.1";
4 | "localhost";
5 | }
6 |
--------------------------------------------------------------------------------
/custom.backend.vcl-sample:
--------------------------------------------------------------------------------
1 | # First backend definition, with a built-in probe
2 | backend default {
3 | # I have Virtual Hosts that only listen to the Public IP
4 | # so no 127.0.0.1 for me
5 | # Backend is running on port 81
6 | .host = "127.0.0.1";
7 | .port = "80";
8 | .probe = {
9 | .url = "/ping";
10 | .timeout = 1s;
11 | .interval = 10s;
12 | .window = 5;
13 | .threshold = 2;
14 | }
15 | .first_byte_timeout = 300s; # How long to wait before we receive a first byte from our backend?
16 | .connect_timeout = 5s; # How long to wait for a backend connection?
17 | .between_bytes_timeout = 2s; # How long to wait between bytes received from our backend?
18 | }
19 |
20 | # Backend definition with entirely custom backend probes (raw HTTP headers) in a separated definition
21 | backend custom_healthchecks {
22 | .host = "127.0.0.1";
23 | .port = "80";
24 | .probe = custom_backend_probe; # Refer to the probe definition below, so we can reuse the probe.
25 |
26 | .first_byte_timeout = 300s;
27 | .connect_timeout = 5s;
28 | .between_bytes_timeout = 2s;
29 | }
30 |
31 | probe custom_backend_probe {
32 | .request = "GET /some/url HTTP/1.1"
33 | "Host: some.virtualhost.domain.Tld"
34 | "Connection: close"
35 | "Accept-Encoding: text/html"
36 | "User-Agent: Varnish Health Probe";
37 | .interval = 10s;
38 | .timeout = 10s;
39 | .window = 2;
40 | .threshold = 2;
41 | }
42 |
43 | # Below is an example redirector based on the Client IP (same returning class C subnet IP will get
44 | # rerouted to the same backend, as long as it's available).
45 | #
46 | # In order for these to work, you need to define 2 backends as shown above named 'web1' and 'web2' (replace 'default'
47 | # from the example above).
48 | director pool_clientip client {
49 | {
50 | .backend = web1;
51 | .weight = 1;
52 | }
53 | {
54 | .backend = web2;
55 | .weight = 1;
56 | }
57 | }
58 |
59 | # Below is an example that will round-robin (based on the weight) to each backend.
60 | # Web2 will get twice the hits as web1 since it has double the weight (= preference).
61 | director pool_clientip {
62 | {
63 | .backend = web1
64 | .weight = 1;
65 | }
66 | {
67 | .backend = web2
68 | .weight = 2;
69 | }
70 | }
71 |
72 |
--------------------------------------------------------------------------------
/custom.fetch.vcl-sample:
--------------------------------------------------------------------------------
1 | # stream big file, add missing extention below
2 | if (req.url ~ "\.(avi|deb|tar|gz|rar|iso|img|dmg|mkv|zip)$") {
3 | set beresp.do_stream = true;
4 | set beresp.ttl = 1d;
5 | }
6 | if (req.http.Host == "my-wp1.localhost" || req.http.Host == "my-wp2.localhost") {
7 | # Since this is a Wordpress setup, the Wordpress-specific Fetch
8 | include "conf.d/fetch/wordpress.vcl";
9 | } elsif (req.http.Host == "my-formcms1.localhost" || req.http.Host == "my-forkcms2.localhost") {
10 | # Include the FormCMS specific VCL
11 | include "conf.d/fetch/forkcms.vcl";
12 | } elsif (req.http.Host == "my-drupal1.localhost" || req.http.Host == "my-drupal2.localhost") {
13 | # Include the Drupal 7 specific VCL
14 | include "conf.d/fetch/drupal7.vcl";
15 | }
16 |
--------------------------------------------------------------------------------
/custom.recv.vcl-sample:
--------------------------------------------------------------------------------
1 | # Include the correct Virtual Host configuration file
2 | if (req.http.Host == "my-wp1.localhost" || req.http.Host == "my-wp2.localhost") {
3 | # The Wordpress specific receive
4 | include "conf.d/receive/wordpress.vcl";
5 | } elsif (req.http.Host == "my-forkcms1.localhost" || req.http.Host == "my-forkcms.localhost") {
6 | # The ForkCMS specific config
7 | include "conf.d/receive/forkcms.vcl";
8 | } elsif (req.http.Host == "my-drupal1.localhost" || req.http.Host == "my-drupal2.localhost") {
9 | # The Drupal 7 specific receive
10 | include "conf.d/receive/drupal7.vcl";
11 | }
12 |
--------------------------------------------------------------------------------
/production.vcl:
--------------------------------------------------------------------------------
1 | # Default backend definition. Set this to point to your content server.
2 | # all paths relative to varnish option vcl_dir
3 |
4 | include "custom.backend.vcl";
5 | include "custom.acl.vcl";
6 |
7 | # Handle the HTTP request received by the client
8 | sub vcl_recv {
9 | # shortcut for DFind requests
10 | if (req.url ~ "^/w00tw00t") {
11 | error 404 "Not Found";
12 | }
13 |
14 | if (req.restarts == 0) {
15 | if (req.http.X-Forwarded-For) {
16 | set req.http.X-Forwarded-For = req.http.X-Forwarded-For + ", " + client.ip;
17 | } else {
18 | set req.http.X-Forwarded-For = client.ip;
19 | }
20 | }
21 |
22 | # Normalize the header, remove the port (in case you're testing this on various TCP ports)
23 | set req.http.Host = regsub(req.http.Host, ":[0-9]+", "");
24 |
25 | # Remove the proxy header (see https://httpoxy.org/#mitigate-varnish)
26 | unset req.http.proxy;
27 |
28 | # Allow purging
29 | if (req.request == "PURGE") {
30 | if (!client.ip ~ purge) {
31 | # Not from an allowed IP? Then die with an error.
32 | error 405 "This IP is not allowed to send PURGE requests.";
33 | }
34 |
35 | # If you got this stage (and didn't error out above), do a cache-lookup
36 | # That will force entry into vcl_hit() or vcl_miss() below and purge the actual cache
37 | return (lookup);
38 | }
39 |
40 | # Only deal with "normal" types
41 | if (req.request != "GET" &&
42 | req.request != "HEAD" &&
43 | req.request != "PUT" &&
44 | req.request != "POST" &&
45 | req.request != "TRACE" &&
46 | req.request != "OPTIONS" &&
47 | req.request != "PATCH" &&
48 | req.request != "DELETE") {
49 | /* Non-RFC2616 or CONNECT which is weird. */
50 | return (pipe);
51 | }
52 |
53 | # Only cache GET or HEAD requests. This makes sure the POST requests are always passed.
54 | if (req.request != "GET" && req.request != "HEAD") {
55 | return (pass);
56 | }
57 |
58 | # For Websocket support, always pipe the requests: https://www.varnish-cache.org/docs/3.0/tutorial/websockets.html
59 | if (req.http.Upgrade ~ "(?i)websocket") {
60 | return (pipe);
61 | }
62 |
63 | # Configure grace period, in case the backend goes down. This allows otherwise "outdated"
64 | # cache entries to still be served to the user, because the backend is unavailable to refresh them.
65 | # This may not be desireable for you, but showing a Varnish Guru Meditation error probably isn't either.
66 | if (req.backend.healthy) {
67 | set req.grace = 30s;
68 | } else {
69 | unset req.http.Cookie;
70 | set req.grace = 6h;
71 | }
72 |
73 | # Some generic URL manipulation, useful for all templates that follow
74 | # First remove the Google Analytics added parameters, useless for our backend
75 | if (req.url ~ "(\?|&)(utm_source|utm_medium|utm_campaign|utm_content|gclid|cx|ie|cof|siteurl)=") {
76 | set req.url = regsuball(req.url, "&(utm_source|utm_medium|utm_campaign|utm_content|gclid|cx|ie|cof|siteurl)=([A-z0-9_\-\.%25]+)", "");
77 | set req.url = regsuball(req.url, "\?(utm_source|utm_medium|utm_campaign|utm_content|gclid|cx|ie|cof|siteurl)=([A-z0-9_\-\.%25]+)", "?");
78 | set req.url = regsub(req.url, "\?&", "?");
79 | set req.url = regsub(req.url, "\?$", "");
80 | }
81 |
82 | # Strip hash, server doesn't need it.
83 | if (req.url ~ "\#") {
84 | set req.url = regsub(req.url, "\#.*$", "");
85 | }
86 |
87 | # Strip a trailing ? if it exists
88 | if (req.url ~ "\?$") {
89 | set req.url = regsub(req.url, "\?$", "");
90 | }
91 |
92 | # This is an example to redirect with a 301/302 HTTP status code from within Varnish
93 | # if (req.http.Host ~ "secure.mysite.tld") {
94 | # # We may want to force our users from the secure site to the HTTPs version?
95 | # error 720 "https://secure.mysite.tld";
96 | # # If you want to keep the URLs intact, this also works:
97 | # error 720 "https://" + req.http.Host + req.url;
98 | # }
99 | #
100 | # Or to force a 302 temporary redirect, use error 721
101 | # if (req.http.Host ~ "temp.mysite.tld") {
102 | # # Temporary redirect
103 | # error 721 "http://mysite.tld/temp";
104 | # }
105 | #
106 |
107 | # Some generic cookie manipulation, useful for all templates that follow
108 | # Remove the "has_js" cookie
109 | set req.http.Cookie = regsuball(req.http.Cookie, "has_js=[^;]+(; )?", "");
110 |
111 | # Remove any Google Analytics based cookies
112 | set req.http.Cookie = regsuball(req.http.Cookie, "__utm.=[^;]+(; )?", "");
113 | set req.http.Cookie = regsuball(req.http.Cookie, "_ga=[^;]+(; )?", "");
114 | set req.http.Cookie = regsuball(req.http.Cookie, "utmctr=[^;]+(; )?", "");
115 | set req.http.Cookie = regsuball(req.http.Cookie, "utmcmd.=[^;]+(; )?", "");
116 | set req.http.Cookie = regsuball(req.http.Cookie, "utmccn.=[^;]+(; )?", "");
117 |
118 | # Remove DoubleClick offensive cookies
119 | set req.http.Cookie = regsuball(req.http.Cookie, "__gads=[^;]+(; )?", "");
120 |
121 | # Remove the Quant Capital cookies (added by some plugin, all __qca)
122 | set req.http.Cookie = regsuball(req.http.Cookie, "__qc.=[^;]+(; )?", "");
123 |
124 | # Remove the AddThis cookies
125 | set req.http.Cookie = regsuball(req.http.Cookie, "__atuv.=[^;]+(; )?", "");
126 |
127 | # Remove a ";" prefix in the cookie if present
128 | set req.http.Cookie = regsuball(req.http.Cookie, "^;\s*", "");
129 |
130 | # Are there cookies left with only spaces or that are empty?
131 | if (req.http.cookie ~ "^\s*$") {
132 | unset req.http.cookie;
133 | }
134 |
135 | # Normalize Accept-Encoding header
136 | # straight from the manual: https://www.varnish-cache.org/docs/3.0/tutorial/vary.html
137 | if (req.http.Accept-Encoding) {
138 | if (req.url ~ "\.(7z|bz2|docx|flac|flv|gz|jpeg|jpg|mka|mkv|mov|mp3|mp4|mpeg|mpg|ogg|ogm|opus|png|pptx|rar|svgz|tbz|tgz|txz|webm|webp|woff2|xlsx|xz|zip)$") {
139 | # No point in compressing these
140 | remove req.http.Accept-Encoding;
141 | } elsif (req.http.Accept-Encoding ~ "gzip") {
142 | set req.http.Accept-Encoding = "gzip";
143 | } elsif (req.http.Accept-Encoding ~ "deflate") {
144 | set req.http.Accept-Encoding = "deflate";
145 | } else {
146 | # unkown algorithm
147 | remove req.http.Accept-Encoding;
148 | }
149 | }
150 |
151 | # Large static files should be piped, so they are delivered directly to the end-user without
152 | # waiting for Varnish to fully read the file first.
153 | # TODO: once the Varnish Streaming branch merges with the master branch, use streaming here to avoid locking.
154 | if (req.url ~ "^[^?]*\.(7z|avi|bz2|flac|flv|gz|mka|mkv|mov|mp3|mp4|mpeg|mpg|ogg|ogm|opus|rar|tar|tgz|tbz|txz|wav|webm|xz|zip)(\?.*)?$") {
155 | unset req.http.Cookie;
156 | return (pipe);
157 | }
158 |
159 | # Remove all cookies for static files
160 | # A valid discussion could be held on this line: do you really need to cache static files that don't cause load? Only if you have memory left.
161 | # Sure, there's disk I/O, but chances are your OS will already have these files in their buffers (thus memory).
162 | # Before you blindly enable this, have a read here: https://ma.ttias.be/stop-caching-static-files/
163 | if (req.url ~ "^[^?]*\.(7z|avi|bmp|bz2|css|csv|doc|docx|eot|flac|flv|gif|gz|ico|jpeg|jpg|js|less|mka|mkv|mov|mp3|mp4|mpeg|mpg|odt|otf|ogg|ogm|opus|pdf|png|ppt|pptx|rar|rtf|svg|svgz|swf|tar|tbz|tgz|ttf|txt|txz|wav|webm|webp|woff|woff2|xls|xlsx|xml|xz|zip)(\?.*)?$") {
164 | unset req.http.Cookie;
165 | return (lookup);
166 | }
167 |
168 | # Send Surrogate-Capability headers to announce ESI support to backend
169 | set req.http.Surrogate-Capability = "key=ESI/1.0";
170 |
171 | if (req.http.Authorization) {
172 | # Not cacheable by default
173 | return (pass);
174 | }
175 |
176 | # Include custom vcl_recv logic
177 | include "custom.recv.vcl";
178 |
179 | return (lookup);
180 | }
181 |
182 | sub vcl_pipe {
183 | # Note that only the first request to the backend will have
184 | # X-Forwarded-For set. If you use X-Forwarded-For and want to
185 | # have it set for all requests, make sure to have:
186 | # set bereq.http.connection = "close";
187 | # here. It is not set by default as it might break some broken web
188 | # applications, like IIS with NTLM authentication.
189 |
190 | #set bereq.http.Connection = "Close";
191 |
192 | # Needed for WS (Websocket) support: https://www.varnish-cache.org/docs/3.0/tutorial/websockets.html
193 | if (req.http.upgrade) {
194 | set bereq.http.upgrade = req.http.upgrade;
195 | }
196 |
197 | return (pipe);
198 | }
199 |
200 | sub vcl_pass {
201 | return (pass);
202 | }
203 |
204 | # The data on which the hashing will take place
205 | sub vcl_hash {
206 | hash_data(req.url);
207 |
208 | if (req.http.host) {
209 | hash_data(req.http.host);
210 | } else {
211 | hash_data(server.ip);
212 | }
213 |
214 | # hash cookies for requests that have them
215 | if (req.http.Cookie) {
216 | hash_data(req.http.Cookie);
217 | }
218 |
219 | return (hash);
220 | }
221 |
222 | sub vcl_hit {
223 | # Allow purges
224 | if (req.request == "PURGE") {
225 | purge;
226 | error 200 "purged";
227 | }
228 |
229 | return (deliver);
230 | }
231 |
232 | sub vcl_miss {
233 | # Allow purges
234 | if (req.request == "PURGE") {
235 | purge;
236 | error 200 "purged";
237 | }
238 |
239 | return (fetch);
240 | }
241 |
242 | # Handle the HTTP request coming from our backend
243 | sub vcl_fetch {
244 | # Include custom vcl_fetch logic
245 | include "custom.fetch.vcl";
246 |
247 | # Parse ESI request and remove Surrogate-Control header
248 | if (beresp.http.Surrogate-Control ~ "ESI/1.0") {
249 | unset beresp.http.Surrogate-Control;
250 | set beresp.do_esi = true;
251 | }
252 |
253 | # https://www.varnish-cache.org/docs/3.0/tutorial/compression.html
254 | # gzip content that can be compressed
255 | # Do wildcard matches, since additional info (like charsets) can be added in the Content-Type header
256 | if (beresp.http.content-type ~ "text/plain"
257 | || beresp.http.content-type ~ "text/xml"
258 | || beresp.http.content-type ~ "text/css"
259 | || beresp.http.content-type ~ "text/html"
260 | || beresp.http.content-type ~ "application/(x-)?javascript"
261 | || beresp.http.content-type ~ "application/(x-)?font-ttf"
262 | || beresp.http.content-type ~ "application/(x-)?font-opentype"
263 | || beresp.http.content-type ~ "application/font-woff"
264 | || beresp.http.content-type ~ "application/vnd\.ms-fontobject"
265 | || beresp.http.content-type ~ "image/svg\+xml"
266 | ) {
267 | set beresp.do_gzip = true;
268 | }
269 |
270 | # If the request to the backend returns a code is 5xx, restart the loop
271 | # If the number of restarts reaches the value of the parameter max_restarts,
272 | # the request will be error'ed. max_restarts defaults to 4. This prevents
273 | # an eternal loop in the event that, e.g., the object does not exist at all.
274 | if (beresp.status >= 500 && beresp.status <= 599){
275 | return(restart);
276 | }
277 |
278 | # Enable cache for all static files
279 | # The same argument as the static caches from above: monitor your cache size, if you get data nuked out of it, consider giving up the static file cache.
280 | # Before you blindly enable this, have a read here: https://ma.ttias.be/stop-caching-static-files/
281 | if (req.url ~ "^[^?]*\.(7z|avi|bmp|bz2|css|csv|doc|docx|eot|flac|flv|gif|gz|ico|jpeg|jpg|js|less|mka|mkv|mov|mp3|mp4|mpeg|mpg|odt|otf|ogg|ogm|opus|pdf|png|ppt|pptx|rar|rtf|svg|svgz|swf|tar|tbz|tgz|ttf|txt|txz|wav|webm|webp|woff|woff2|xls|xlsx|xml|xz|zip)(\?.*)?$") {
282 | unset beresp.http.set-cookie;
283 | }
284 |
285 | # Sometimes, a 301 or 302 redirect formed via Apache's mod_rewrite can mess with the HTTP port that is being passed along.
286 | # This often happens with simple rewrite rules in a scenario where Varnish runs on :80 and Apache on :8080 on the same box.
287 | # A redirect can then often redirect the end-user to a URL on :8080, where it should be :80.
288 | # This may need finetuning on your setup.
289 | #
290 | # To prevent accidental replace, we only filter the 301/302 redirects for now.
291 | if (beresp.status == 301 || beresp.status == 302) {
292 | set beresp.http.Location = regsub(beresp.http.Location, ":[0-9]+", "");
293 | }
294 |
295 | # Set 2min cache if unset for static files
296 | if (beresp.ttl <= 0s || beresp.http.Set-Cookie || beresp.http.Vary == "*") {
297 | set beresp.ttl = 120s;
298 | return (hit_for_pass);
299 | }
300 |
301 | # If the backend response is an HTTP error (500, 502, 503), enter saint mode.
302 | # This will block this particular request from happening again to this backend, and will restart the request on the next available backend.
303 | # In case, for instance, the first backend is temporarily unavailable, this will restart the request to the second backend, without the client noticing it.
304 | #
305 | # Explained from the manual: by setting beresp.saintmode to a period of time, Varnish will not ask that backend again for this object for that amount of time.
306 | # This only works if you have multiple baceknds.
307 | if (beresp.status == 500 || beresp.status == 502 || beresp.status == 503) {
308 | # Don't use this server, for this particular URL, for the next 10 seconds.
309 | set beresp.saintmode = 10s;
310 |
311 | # Restart the HTTP request, this will automatically happen on the next available server (due to saintmode, see above).
312 | # But we don't want to restart POST requests, as that's dangerous (duplicate form submits etc.)
313 | if (req.request != "POST") {
314 | return(restart);
315 | }
316 | }
317 |
318 | # Keep all objects for 6h longer in the cache than their TTL specifies.
319 | # So even if HTTP objects are expired (they've passed their TTL), we can still use them in case all backends go down.
320 | # Remember: old content to show is better than no content at all (or an error page).
321 | set beresp.grace = 6h;
322 |
323 | return (deliver);
324 | }
325 |
326 | # The routine when we deliver the HTTP request to the user
327 | # Last chance to modify headers that are sent to the client
328 | sub vcl_deliver {
329 | if (obj.hits > 0) {
330 | set resp.http.X-Cache = "cached";
331 | } else {
332 | set resp.http.x-Cache = "uncached";
333 | }
334 |
335 | # Remove some headers: PHP version
336 | unset resp.http.X-Powered-By;
337 |
338 | # Remove some headers: Apache version & OS
339 | unset resp.http.Server;
340 | unset resp.http.X-Drupal-Cache;
341 | unset resp.http.X-Varnish;
342 | unset resp.http.Via;
343 | unset resp.http.Link;
344 |
345 | return (deliver);
346 | }
347 |
348 | sub vcl_error {
349 | if (obj.status >= 500 && obj.status <= 599) {
350 | # 4 retry for 5xx error
351 | return(restart);
352 | } elseif (obj.status >= 400 && obj.status <= 499 ) {
353 | # use 404 error page for 4xx error
354 | include "conf.d/error-404.vcl";
355 | } elseif (obj.status == 720) {
356 | # We use this special error status 720 to force redirects with 301 (permanent) redirects
357 | # To use this, call the following from anywhere in vcl_recv: error 720 "http://host/new.html"
358 | set obj.status = 301;
359 | set obj.http.Location = obj.response;
360 | return (deliver);
361 | } elseif (obj.status == 721) {
362 | # And we use error status 721 to force redirects with a 302 (temporary) redirect
363 | # To use this, call the following from anywhere in vcl_recv: error 720 "http://host/new.html"
364 | set obj.status = 302;
365 | set obj.http.Location = obj.response;
366 | return (deliver);
367 | } else {
368 | # for other errors (not 5xx, not 4xx and not 2xx)
369 | include "conf.d/error.vcl";
370 | }
371 |
372 | return (deliver);
373 | }
374 |
375 | sub vcl_init {
376 | return (ok);
377 | }
378 |
379 | sub vcl_fini {
380 | return (ok);
381 | }
382 |
--------------------------------------------------------------------------------