Coding Range

Masking headers in Varnish

April 15th, 2013

It’s always a good idea not to let your HTTP headers tell the world your exact setup. Unfortunately, Nginx won’t let you turn off the Server header, it requires you to edit the source code and recompile.

If you have Varnish in front (or any other reverse proxy), however, you have a point in the pipeline where you can alter the response before it gets transmitted.

All the guides I found were old versions of Varnish, so here are some rules for 3.0:

sub vcl_fetch {
    unset beresp.http.Server;
    if (beresp.ttl <= 0s ||
        beresp.http.Set-Cookie ||
        beresp.http.Vary == "*") {
                 * Mark as "Hit-For-Pass" for the next 2 minutes
                set beresp.ttl = 120 s;
                return (hit_for_pass);
    return (deliver);

sub vcl_deliver {
    remove resp.http.Via;
    remove resp.http.X-Varnish;

    return (deliver);

sub vcl_error {
    set obj.http.Content-Type = "text/html; charset=utf-8";
    set obj.http.Retry-After = "5";
    remove obj.http.Server;

    synthetic {"
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    <title>"} + obj.status + " " + obj.response + {"</title>
    <h1>Error "} + obj.status + " " + obj.response + {"</h1>
    <p>"} + obj.response + {"</p>
    <h3>Guru Meditation:</h3>
    <p>XID: "} + req.xid + {"</p>
    return (deliver);

Everything in vcl_fetch below unset is the default rule. I’ve also told Varnish to remove it’s own headers in vcl_deliver and vcl_error (as well as <p>Varnish cache server</p>) to stop it speaking too loudly too.