A.k.a. a terrible, terrible hack. There are hints around this solution on the internets, but I couldn't find a good explanation of what's going on, so here you go.
listen example mode http bind 127.0.0.1:8000 errorfile 503 /tmp/example.http
HTTP/1.0 400 Bad request Cache-Control: no-cache Connection: close Content-Type: text/html <html><body><h1>400 Bad request</h1> This is an example. </body></html>
haproxy -f example.cfg
Alright, let's try this a different way.
frontend example-frn mode http bind 127.0.0.1:8000 use_backend example-bck backend example-bck mode http errorfile 503 /tmp/example.http
The other file is the same.
Here's the relevant part from the documentation.
errorfile <code> <file> returns a file contents instead of errors generated by HAProxy. [...] The files are returned verbatim on the TCP socket. This allows any trick such as redirections to another URL or site, as well as tricks to clean cookies, force enable or disable caching, etc... The package provides default error files returning the same contents as default errors.
Translation: we can tell HAProxy to, when serving a specific error code, instead of serving its built-in error content, serve the contents of a file. BUT! That file is "returned verbatim on the TCP socket", including HTTP headers and all. This allows all kinds of tricks, and one trick that isn't mentioned is changing the HTTP response code.
The last ingredient: if a
backend directive doesn't have
UP servers, it will return a
Combining all these, we can say: hey HAProxy, here's a backend that
will never have a server because I won't configure any. (So you'll
always serve 503 on this backend). And oh, HAProxy, whenever you'd
503, serve this file instead. (And the file just so happens
to contain a
400 response, but it could be whatever else we want).
Again, from the documentation:
The files should not exceed the configured buffer size (BUFSIZE), which generally is 8 or 16 kB, otherwise they will be truncated. It is also wise not to put any reference to local contents (eg: images) in order to avoid loops between the client and HAProxy when all servers are down, causing an error to be returned instead of an image. For better HTTP compliance, it is recommended that all header lines end with CR-LF and not LF alone.
The files are read at the same time as the configuration and kept in memory. For this reason, the errors continue to be returned even when the process is chrooted, and no file change is considered while the process is running. A simple method for developing those files consists in associating them to the 403 status code and interrogating a blocked URL.
So basically, keep the file small and simple. In practice this means
that probably no user-facing error pages should be served by HAProxy;
you're better of delegating that to a web server. This technique can
still be useful for handling cases that users are not expected to
encounter. For example in the hosting of this blog, I'm using it to
restrict incoming requests to two domains, and return a descriptive
error if HAProxy receives a request with a different
What's your favorite HAProxy trick?