HAProxy doesn’t compress responses by default, which means you’re sending uncompressed data to clients, burning bandwidth and slowing down page loads.
Let’s see it in action. Imagine a simple HAProxy config serving a static HTML file:
frontend http_frontend
bind *:80
default_backend http_backend
backend http_backend
server static_server 127.0.0.1:8080
And a simple web server on port 8080:
from http.server import SimpleHTTPRequestHandler, HTTPServer
class Handler(SimpleHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header("Content-type", "text/html")
self.end_headers()
self.wfile.write(b"<!DOCTYPE html><html><body><h1>This is a long response that would benefit from compression.</h1></body></html>")
httpd = HTTPServer(('localhost', 8080), Handler)
httpd.serve_forever()
If you hit this with curl -v http://localhost/, you’ll see the Content-Length header, but no Content-Encoding: gzip.
To enable gzip compression, we’ll add a couple of lines to our HAProxy configuration. We need to tell HAProxy to use the compress option within the http-request section. This option has several sub-parameters. The most important ones are type and level.
frontend http_frontend
bind *:80
http-request set-header X-Forwarded-Proto http
http-request set-header X-Forwarded-For http_proxy
http-request set-header Host %[req.hdr(Host)]
http-response set-header X-Powered-By HAProxy
# Enable compression for text-based content types
http-response compress text/html text/plain text/css application/javascript application/json
default_backend http_backend
backend http_backend
server static_server 127.0.0.1:8080
Here, http-response compress text/html text/plain text/css application/javascript application/json tells HAProxy to look at the Content-Type header of the response. If it matches any of the listed MIME types, HAProxy will attempt to compress the response body using gzip.
After applying this change and restarting HAProxy, if you run curl -v http://localhost/ again, you should now see the Content-Encoding: gzip header in the response. The response body will also be significantly smaller, as the server will send the compressed data.
The compress option can also take a level parameter, which controls the compression ratio versus CPU usage. The default level is 6. You can specify a different level like this: http-response compress level 9 text/html. Level 9 offers the highest compression but uses more CPU. Levels 1-9 are available.
The compress directive also supports algorithm gzip and algorithm deflate. By default, it uses gzip. If you need to support older clients that might not handle gzip well, you could potentially use deflate, though gzip is universally supported now.
A common pitfall is forgetting to list all the content types you want to compress. If you’re serving images or other binary files, you definitely don’t want to compress them, as they are often already compressed or compression might even increase their size. The directive applies only to the MIME types you explicitly list.
The http-response compress directive works by buffering the response body. Once the entire response is received from the backend server, HAProxy checks the Content-Type. If it’s in the allowed list, it compresses the buffered body and sends the compressed version to the client, adding the Content-Encoding: gzip header. This buffering means that for very large responses, there might be a slight increase in latency and memory usage on the HAProxy server itself, but this is usually a worthwhile trade-off for the bandwidth savings and improved client-side performance.
You might also see HAProxy’s http-request allow if !{ req.hdr(Accept-Encoding) -i gzip } used in conjunction with compression. This isn’t directly related to enabling compression on the HAProxy side, but rather to respecting client capabilities. If a client doesn’t send an Accept-Encoding: gzip header, HAProxy will skip compression for that request. The http-response compress directive, however, will compress regardless of the Accept-Encoding header unless you add specific logic to check it. The most robust setup is to use http-response compress and let clients signal their capabilities via Accept-Encoding, and HAProxy will naturally only send compressed data if the client requests it. If you always want to compress, you’d omit any client-side Accept-Encoding checks.
If you’ve enabled compression and are still not seeing the Content-Encoding: gzip header, double-check the exact MIME types you’ve listed in your http-response compress directive and ensure they match what your backend server is sending. Also, verify that HAProxy is actually receiving a response from the backend; if the backend is failing, HAProxy won’t have a response to compress.
The next thing you’ll likely want to configure is how HAProxy handles caching of compressed assets.