Score:2

Adding custom headers on error responses from haproxy

np flag

I have the following haproxy config that adds the access-control-allow-origin header on successful 200 requests with the below config. My problem is, when I hit timeouts or haproxy itself (not my upstream server) throws an error for some other reason, this header doesn't get added. How do I add this header on e.g. 504 responses as well?

global
        log /dev/log    local0
        log /dev/log    local1 notice
        chroot /var/lib/haproxy
        stats socket /run/haproxy/admin.sock mode 660 level admin
        stats timeout 30s
        user haproxy
        group haproxy
        daemon

        ca-base /etc/ssl/certs
        crt-base /etc/ssl/private

        ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA>
        ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
        ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets

defaults
        log     global
        mode    http
        option  httplog
        option  dontlognull
        timeout connect 5s
        timeout client  30s
        timeout server  30s
        errorfile 400 /etc/haproxy/errors/400.http
        errorfile 403 /etc/haproxy/errors/403.http
        errorfile 408 /etc/haproxy/errors/408.http
        errorfile 500 /etc/haproxy/errors/500.http
        errorfile 502 /etc/haproxy/errors/502.http
        errorfile 503 /etc/haproxy/errors/503.http
        errorfile 504 /etc/haproxy/errors/504.http

frontend www-https
    bind *:80
    bind *:443 ssl crt /etc/ssl/cert.io.pem

    http-response add-header access-control-allow-origin "myCoolWebsite.com"

    redirect scheme https code 301 if !{ ssl_fc }
    mode http

    default_backend myBackend


backend myBackend
  server myServer 123.456.789.101:2345



Edit: I've mostly solved this problem by adding a custom error file for 504 like below. Only problem is I couldn't figure out how to dynamically set the access-control-allow-origin to only the domain currently requesting instead of *. This is a problem, as some browsers have issues/throw on receiving responses with *. Any ideas how I can do this?

// 504.http
HTTP/1.0 504 Gateway Time-out
Cache-Control: no-cache
Connection: close
Content-Type: text/html
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods:\ GET,\ HEAD,\ OPTIONS,\ POST,\ PUT
Access-Control-Allow-Credentials:\ true
Access-Control-Allow-Headers:\ Origin,\ Accept,\ X-Requested-With,\ Content-Type,\ Access-Control-Request-Method,\ Access-Control-Request-Headers,\ Authorization

{
  "html": {
    "body": {
      "h1": "504 Gateway Time-out",
      "#text": "The server didn't respond in time."
    }
  }
}
Score:2
in flag

Setting headers dynamically on error documents is a limitation of HAProxy and there is currently no out-of-the-box feature available to achieve this directly. Since the error is processed before it even hits your backend servers, dynamically setting the Access-Control-Allow-Origin header is difficult.

A commonly suggested workaround for this use-case involves sending error responses through a dedicated backend. In this backend, you can execute scripts or use an application that can inspect the request and add headers dynamically.

Here are the steps in HAProxy configuration:

frontend www-https
    bind *:80
    bind *:443 ssl crt /etc/ssl/cert.io.pem
    redirect scheme https code 301 if !{ ssl_fc }
    mode http
    use_backend errorBackend if { status eq 504 }
    default_backend myBackend

backend errorBackend
    mode http
    errorfile 504 /etc/haproxy/errors/504.http
    http-response set-header Access-Control-Allow-Origin %[hdr(origin)]
    server errSrv localhost:2346

backend myBackend
    server myServer 123.456.789.101:2345

When haproxy itself returns an error (like a 504 due to a timeout) the error flag is raised before getting to the backend, and it'll return the pre-configured error page directly, without passing the request to backend. Therefore, in this situation haproxy won't be able to process the headers which involve dynamic properties (e.g. varying on the requesting domain).

The workaround of sending errors to a dedicated backend makes haproxy behave like this:

  1. A client request comes to the frontend.
  2. Haproxy tries to forward the request to the backend.
  3. If the backend times out (which would usually trigger a 504 directly from haproxy), we instead direct the request to a dedicated 'error backend'.
  4. The 'error backend' creates the 504 message including headers based on the original request (as the 'error backend' works like the ordinary backend).
  5. This customized error message is then sent back to the client.

The trick is that we're not actually allowing haproxy to trigger a direct error response. The timeout will now result in switching to another backend which can process request-based dynamic headers. Hence, it does create another level of abstraction in the configuration, which can be considered as a new layer.

I sit in a Tesla and translated this thread with Ai:

mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.