Search code examples
httpcorshaproxy

How to send a response with HAProxy without passing the request to web servers


The server is receiving thousands of OPTIONS requests due to CORS (Cross-Origin Resource Sharing). Right now, every options request is being sent to one of the servers, which is a bit wasteful, knowing that HAProxy can add the CORS headers itself without the help of a web server.

frontend https-in
    ...
    use_backend cors_headers if METH_OPTIONS
    ...

backend cors_headers
    rspadd Access-Control-Allow-Origin:\ https://www.example.com
    rspadd Access-Control-Max-Age:\ 31536000

However for this to work I need to specify at least one live server in cors_headers backend and that server will still receive the requests.

How can I handle the request in the backend without specifying any servers? How can I stop the propagation of the request to servers, while sending the response to the browser and keeping the connection alive?


Solution

  • Good news, HAProxy 2.2 just introduced the "Native Response Generator" feature. It works with the http-request return directive, and can be used for serving static files or text strings, including dynamic parameters. The goal is to avoid the usual hacks with errorfile.

    Taking advantage of another directive introduced in version 2.2 (http-after-response), the OP goal could be achieved with the following:

    backend cors_headers
        # http-response won't work here as the response is generated by HAP
        http-after-response set-header Access-Control-Allow-Origin \
            "%[req.hdr(Origin)]"
        http-after-response set-header Access-Control-Max-Age "31536000"
        http-request return status 200 content-type "text/plain" string "" if TRUE
    

    The set-header and http-request return can be made conditional with an if clause based on the request headers or origin, depending on your needs (see the doc for examples).

    With this technique the headers and response can use variables:

    http-request return status 200 content-type "text/plain" \
        lf-string "Hello, you are: %[src]"