some blog : )

Varnish HTTP authentication

Posted at — Dec 14, 2011

Sometimes we need restrict access using basic HTTP authentication to site cached by Varnish.

Basic method is enable it on backend (Apache HTTPd, Nginx, lighttpd or any other web-server). But after the first correct request, it will be cached by Varnish and all other subsequent requests would be without authentication check.

Solution for this situation is to pass all requests with authentication to backend with next statement:

if (req.http.Authorization || req.http.Authenticate)
{
  return (pass);
}
As we remember, caching still required for this site. So, statement above is not suitable for our conditions.

Good workaround is to check HTTP authentication on Varnish level.

Well, then do it in VCL.


On vcl_recv section we will check an authorization header and invoke 401 error in case header is not present or not match required authorization string:

Varnish 3 (and below):

if (! req.http.Authorization ~ "Basic Zm9vOmJhcgo=")
{
  error 401 "Restricted";
}

Varnish 4+:

if (! req.http.Authorization ~ "Basic Zm9vOmJhcgo=")
{
  return(synth(401, "Restricted"));
}

where Zm9vOmJhcgo= is a base64 encoded string of login and password pair combined in next manner - "http-user:http-password". In this example it is a "foo:bar".

Use console terminal command to get encoded string (or use some other tool or, for example, base64encode.org service).

$ echo -n "foo:bar" | base64

Again, syntetic response configuration is a bit different per Varnish version. In both cases error handler for 401 needs to be created

Varnish 3 (and below) - code under vcl_error subroutine:

if (obj.status == 401) {
  set obj.http.Content-Type = "text/html; charset=utf-8";
  set obj.http.WWW-Authenticate = "Basic realm=Secured";
  synthetic {"
  <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
  "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd">
  <HTML>
  <HEAD>
  <TITLE>Error</TITLE>
  <META HTTP-EQUIV='Content-Type' CONTENT='text/html;'>
  </HEAD>
  <BODY><H1>401 Unauthorized.</H1></BODY>
  </HTML>
  "};
  return (deliver);
}

Varnish 4 (and newer) - code under vcl_synth subroutine:

  if (resp.status == 401) {
    set resp.status = 401;
    set resp.http.Content-Type = "text/html; charset=utf-8";
    set resp.http.WWW-Authenticate = "Basic realm=Authentication required. Please login";
    synthetic ({"
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
    "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd">
    <HTML>
    <HEAD>
    <TITLE>Error</TITLE>
    <META HTTP-EQUIV='Content-Type' CONTENT='text/html;'>
    </HEAD>
    <BODY><H1>401 Unauthorized.</H1></BODY>
    </HTML>
    "});
    return(deliver);
  }
}

Adding these snippets to your VCL enables HTTP authorization on Varnish and cache still present, so you still able to develop or debug your site in normal way.

Multiple user and password pairs could be set with && statement on header check:

if (! req.http.Authorization ~ "Basic Zm9vOmJhcgo=" # foo:bar
 && ! req.http.Authorization ~ "Basic bG9yZW06aXBzdW0K" # lorem:ipsum
) {
  error 401 "Restricted";
}