Laravel • Varnish • Performance
by Dimitri König Reading time: ~4 minutes

Limit allowed http methods with Varnish Cache

Once again: if you don't need it, block it. Either block it completely or allow it only for certain urls

The default Varnish configuration allows only for GET and HEAD requests to be cached. Other default http methods are passed through directly to your backend. And everything else is piped by Varnish, meaning: Varnish does not inspect the request anymore but sends all bytes straight to your backend.

This is how the default Varnish config looks like for handling http methods:

1# This will never happen in properly formed traffic, so block it
2if (req.method == "PRI") {
3 return (synth(405));
4}
5 
6# pipe all non-default requests
7if (req.method != "GET" &&
8 req.method != "HEAD" &&
9 req.method != "PUT" &&
10 req.method != "POST" &&
11 req.method != "TRACE" &&
12 req.method != "OPTIONS" &&
13 req.method != "DELETE" &&
14 req.method != "PATCH"
15) {
16 return (pipe);
17}
18 
19# We only deal with GET and HEAD by default.
20if (req.method != "GET" && req.method != "HEAD") {
21 return (pass);
22}

Don't allow any http methods if you don't support them

The issue is that most applications only use the POST method, besides GET/HEAD. Everything else is not common, unless you are using an API, possibly with an Single-Page-Application. These types of applications usually use more http methods, sometimes PATCH+DELETE+PUT, and sometimes even OPTIONS for Cross-Origin Resource Sharing (CORS) requests. And even then those requests are restricted to a certain domain or some urls.

So the sensible approach here is to just block everything not needed, which would look like this:

1if (req.method != "GET" &&
2 req.method != "HEAD" &&
3 req.method != "POST" &&
4 
5 # uncomment if you need any
6 #req.method != "PUT" &&
7 #req.method != "TRACE" &&
8 #req.method != "OPTIONS" &&
9 #req.method != "DELETE" &&
10 #req.method != "PATCH"
11) {
12 # block it completely
13 return (synth(405));
14}
15if (req.method != "GET" && req.method != "HEAD") {
16 # We only deal with GET and HEAD by default.
17 return (pass);
18}

So far only Websockets need piping

If you use websockets then obviously you need to pipe all those requests through. But at that point you most probably have the required infrastructure in place to handle that traffic, especially with very many connections, so let them through.

Only allow some http methods for certain urls

This comes in handy if you have a backend which uses more then just POST requests. You can then exclude every http method for the whole frontend page, but allow more http methods for the admin area. This could look like this:

1# for /admin area access, allow only these methods
2if (req.url ~ "/admin" &&
3 req.method != "GET" &&
4 req.method != "POST" &&
5 req.method != "PUT" &&
6 req.method != "DELETE" &&
7 req.method != "PATCH"
8) {
9 # block it completely
10 return (synth(405));
11}
12 
13# for non-admin area access, allow GET+HEAD+POST
14if (req.url !~ "/admin" &&
15 req.method != "GET" &&
16 req.method != "HEAD" &&
17 req.method != "POST"
18) {
19 # block it completely
20 return (synth(405));
21}
22 
23if (req.method != "GET" && req.method != "HEAD") {
24 # We only deal with GET and HEAD by default.
25 return (pass);
26}

HEAD method

You may have noticed that I've removed the HEAD method from the allowed list of methods for the admin area. I have yet to see an admin area which uses that. But I've seen plenty of aggressive bot HEAD requests. So if you don't need it, block it.