Load a synthetic output template from a file
In this tutorial we will override the VCL output template for vcl_synth
and vcl_backend_error
by loading it from a file.
These subroutines are used to compose the output when synthetic responses are returned. These are responses that do not originate from the origin server. When you trigger a synthetic response yourself, you end up in the vcl_synth
subroutine. When the synthetic response is the result of a failed backend response, you end up in vcl_backend_error
.
Built-in VCL for vcl_synth
Varnish’s built-in VCL uses the following template for synthetic output in vcl_synth
:
sub vcl_synth {
set resp.http.Content-Type = "text/html; charset=utf-8";
set resp.http.Retry-After = "5";
set resp.body = {"<!DOCTYPE html>
<html>
<head>
<title>"} + resp.status + " " + resp.reason + {"</title>
</head>
<body>
<h1>Error "} + resp.status + " " + resp.reason + {"</h1>
<p>"} + resp.reason + {"</p>
<h3>Guru Meditation:</h3>
<p>XID: "} + req.xid + {"</p>
<hr>
<p>Varnish cache server</p>
</body>
</html>
"};
return (deliver);
}
If you call return(synth(200,"Purge"));
in your VCL code, the following HTML response will be generated:
<!DOCTYPE html>
<html>
<head>
<title>200 Purged</title>
</head>
<body>
<h1>Error 200 Purged</h1>
<p>Purged</p>
<h3>Guru Meditation:</h3>
<p>XID: 123456</p>
<hr>
<p>Varnish cache server</p>
</body>
</html>
Built-in VCL for vcl_backend_error
While vcl_synth
is manually triggered through return(synth(...))
, vcl_backend_error
is automatically called when Varnish fails to receive a valid response from the origin server.
This is the output template in the built-in VCL when backend errors occur:
sub vcl_backend_error {
set beresp.http.Content-Type = "text/html; charset=utf-8";
set beresp.http.Retry-After = "5";
set beresp.body = {"<!DOCTYPE html>
<html>
<head>
<title>"} + beresp.status + " " + beresp.reason + {"</title>
</head>
<body>
<h1>Error "} + beresp.status + " " + beresp.reason + {"</h1>
<p>"} + beresp.reason + {"</p>
<h3>Guru Meditation:</h3>
<p>XID: "} + bereq.xid + {"</p>
<hr>
<p>Varnish cache server</p>
</body>
</html>
"};
return (deliver);
}
The output template is identical, but instead of using the resp
object, the beresp
object is used instead because the resp
object is not available in the backend context.
Overriding the synthetic output template
It makes sense to override this template and change the aesthetics of the output by adding your own. Depending on the type of HTTP service you are using, you can return HTML, XML, JSON, plain text or any other kind of output.
Here’s a simple example where we use a plain text output template in vcl_synth
:
sub vcl_synth {
set resp.http.Content-Type = "text/plain; charset=utf-8";
set resp.body = resp.reason + "(" + resp.status + ")";
return (deliver);
}
And here’s the equivalent for vcl_backend_error
:
sub vcl_backend_error {
set beresp.http.Content-Type = "text/plain; charset=utf-8";
set beresp.body = beresp.reason + "(" + beresp.status + ")";
return (deliver);
}
return (deliver)
when overriding the synthetic output template, otherwise the original output from the built-in VCL will be appended to yours.Loading the synthetic output template form a file
If you’re planning to produce a lot of synthetic output, it makes sense to store the template in a separate file and load that file in VCL.
The std.fileread()
function that is part of the std
VMOD will load a file from disk and return the string value. This value is stored in resp.body
and beresp.body
.
However, we still need to inject the reason phrase into the output. In the example VCL code below, we do this by replacing the <<REASON>>
placeholder with the resp.reason
and beresp.reason
values through the regsuball()
function:
vcl 4.1;
import std;
sub vcl_synth {
set resp.http.Content-Type = "text/html; charset=utf-8";
set resp.http.Retry-After = "5";
set resp.body = regsuball(std.fileread("/etc/varnish/synth.html"),"<<REASON>>",resp.reason);
return (deliver);
}
sub vcl_backend_error {
set beresp.http.Content-Type = "text/html; charset=utf-8";
set beresp.http.Retry-After = "5";
set beresp.body = regsuball(std.fileread("/etc/varnish/synth.html"),"<<REASON>>",beresp.reason);
return (deliver);
}
This example assumes that the /etc/varnish/synth.html
file contains a <<REASON>>
placeholder. Here’s what this could look like:
<!DOCTYPE html>
<html>
<head>
<title>An error occured</title>
</head>
<body>
<h1>An error occured</h1>
<p><<REASON>></p>
</body>
</html>
It is also possible to load multiple template files depending on the type of synthetic output you want to return.