Configure HAProxy to Accept Preflight Requests (CORS)

Recently I developed an API with Spring Boot. All the tests on the local environment went well with Postman. However, when deploying to prod through HAProxy, I got this CORS error:

There are also other errors regarding missing headers, origin…

There are some solutions available, such as adding code to my Spring Boot code base. I don’t like this method since it’s not pure.

Why add non-business code in API?

That’s why I need to find another solution.

It turned out, that the solution is simple.

If you prefer to watch the video, here it is:

https://youtu.be/IsLNCrdg154

Enable CORS for OPTIONS request in HAProxy

First, add a new backend only for serving CORS in HAProxy:

backend cors_backend
  http-after-response set-header Access-Control-Allow-Origin "*"
  http-after-response set-header Access-Control-Allow-Headers "*"
  http-after-response set-header Access-Control-Max-Age "31536000"
  http-request return status 200

Now, in the frontend part, add a check for OPTIONS request and direct that request to this backend:

frontend your_fe
  ...
   acl is_options method OPTIONS
   
...

  use_backend cors_backend if is_options
  # put your actual backend below this

Of course, in your actual backend, you need to set the CORS headers:

backend your_main_backend
  ...

  # START CORS
  http-response add-header Access-Control-Allow-Origin "*"
  http-response add-header Access-Control-Allow-Headers "*"
  http-response add-header Access-Control-Max-Age 3600
  http-response add-header Access-Control-Allow-Methods "GET, DELETE, OPTIONS, POST, PUT, PATCH"
  # END CORS

  server w1 server:8080

And that’s all you need. Now you can work with your API CORS-free

The Complete configuration

Here are the complete configuration files:

  • docker-compose.yml
version: '3.8'
services:
  haproxy_license_server:
    container_name: haproxy_license_server
    image: haproxy:2.4.0
    restart: always
    ports:
      - "7088:7088"
      - "8789:8788"
    volumes:
      - ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg
  • haproxy.cfg
global
  stats timeout 30s


defaults
  log  global
  mode  http
  option  httplog
  option  dontlognull
  timeout connect 5000
  timeout client  50000
  timeout server  50000
  option forwardfor
  option http-server-close
  
frontend ukata
  bind :8788
  mode tcp
  acl is_options method OPTIONS
  use_backend cors_backend if is_options
  default_backend ukata_backend

backend cors_backend
  http-after-response set-header Access-Control-Allow-Origin "*"
  http-after-response set-header Access-Control-Allow-Headers "*"
  http-after-response set-header Access-Control-Allow-Credentials "true"
  http-after-response set-header Access-Control-Allow-Methods "GET, DELETE, OPTIONS, POST, PUT, PATCH"
  http-after-response set-header Access-Control-Max-Age "31536000"
  http-request return status 200

backend ukata_backend
  mode http
  balance roundrobin
  # START CORS
  http-response add-header Access-Control-Allow-Origin "http://localhost:4200"
  http-response add-header Access-Control-Allow-Headers "*"
  http-response add-header Access-Control-Max-Age 3600
  http-after-response set-header Access-Control-Allow-Credentials "true"
  http-response add-header Access-Control-Allow-Methods "GET, DELETE, OPTIONS, POST, PUT, PATCH"
  # END CORS
  # Don't set IP address of the server, use the hostname instead
  # set local-dev-1, 2 to your local ip address (192.168.x.x)
  server w1 192.168.100.6:8788 check

Caution

On a production server, you may need to replace the “*” with only the hosts you allowed (from your frontend, for example). In that case, you can replace * with your client URL (starts with http/https).

4 thoughts on “Configure HAProxy to Accept Preflight Requests (CORS)”

  1. Hey, Thanks for this article, I am currently stuck with this problem, But I was unable to solve this:

    I have a single frontend application with multiple backend, Can you publish your ha-proxy config file and replace the confidential data with a dump one, May be via Github , Thanks

    Reply

Leave a Comment