Traefik headers middleware is not handling CORS preflight request

I have the following middleware defined:

[http.middlewares.cors-allow.headers]
accessControlAllowHeaders = [ "Authorization" ]
accessControlAllowMethods = [ "GET", "HEAD", "OPTIONS" ]
accessControlAllowOrigin = "*"
accessControlAllowCredentials = true

When performing an OPTIONS request on the URL (tried using curl), traefik does not handle the response, and instead passes it through to the service, which does not handle OPTIONS requests. This breaks CORS preflight requests.

What am I doing wrong? How can I troubleshoot this?

1 Like

Can you share your test curl ?

curl -i -X OPTIONS -H 'Origin: https://example.com' -H 'access-control-request-method: GET' https://api.example.com/info
HTTP/2 200 
access-control-allow-credentials: true
access-control-allow-headers: Authorization,Origin,Content-Type,Accept
access-control-allow-methods: GET,POST,HEAD,PUT,DELETE,PATCH,OPTIONS
access-control-allow-origin: https://example.com
access-control-max-age: 0
content-length: 0
date: Mon, 11 Jan 2021 00:31:25 GMT

@cakiwi

$ curl -i -X OPTIONS -H 'Origin https://example.com' -H 'access-control-request-method: GET' http://robotben-test-project.ide.localdomain/
HTTP/1.1 400 Bad Request: invalid header name
Content-Type: text/plain; charset=utf-8
Connection: close

400 Bad Request: invalid header name

[edit] Here are both configs in full (these are two separate files in the traefik config dir):

base.toml:

[http.routers.ide-controller]
rule = "Host(`controller.ide.localdomain`)"
service = "ide-controller"

[http.routers.dashboard]
rule = "Host(`dashboard.ide.localdomain`)"
service = "api@internal"
middlewares = [ "dashboard-auth" ]

[[http.services.ide-controller.loadBalancer.servers]]
url = "http://app:3000"

[http.middlewares.cors-allow.headers]
accessControlAllowHeaders = "*"
accessControlAllowMethods = "*"
accessControlAllowOrigin = "*"
accessControlAllowCredentials = true

[http.middlewares.auth-handler.forwardAuth]
address = "http://app:3000/auth"
trustForwardHeader = true

[http.middlewares.dashboard-auth.basicAuth]
users = [ "admin:XXXXXXXXXXXXXXXXXXXXXXXX" ]

theia-deployment--robotben--test-project.toml:

[http.routers.theia-deployment--robotben--test-project]
rule = "Host(`robotben-test-project.ide.localdomain`) || HostRegexp(`{webview:[^.]}.webview.robotben-test-project.ide.localdomain`)"
service = "theia-deployment--robotben--test-project"
middlewares = [ "cors-allow" ]

[http.routers.theia-deployment--robotben--test-project--preview]
rule = "Host(`preview.robotben-test-project.ide.localdomain`)"
service = "theia-deployment--robotben--test-project--preview"

[[http.services.theia-deployment--robotben--test-project.loadBalancer.servers]]
url = "http://172.24.0.5:3000"

[[http.services.theia-deployment--robotben--test-project--preview.loadBalancer.servers]]
url = "http://172.24.0.5:8080"

You missed the : after Origin

I really don't know if * is allowed here, maybe someone else knows. I might have time for a test later.

Ok, I've found the issue seems to be sporadic. I'm seeing this message in the logs:

middleware \"cors-allow@file\" does not exist

Which is odd, b/c the middleware is definitely there in the base config.I'm not changing or updating the base config at any time. But sometimes when I touch-update the individual deployment config, the middleware loads successfully.

Is there some race condition that can occur when updating configs that causes the middleware to not be found?

Should I instead be defining middleware with each individual deployment config and not the base config?

Check the log at startup. Maybe with --loglevel=DEBUG. I'm guessing an error on loading that middleware.

As per Cors Headers
This is the way I do it, lists of methods and origins, but using docker labels.
I'm not too savvy with the TOML I use yaml when I have to do a file provider/config, so I cannot parse a

[http.middlewares]
  [http.middlewares.testHeader.headers]
    accessControlAllowMethods= ["GET", "OPTIONS", "PUT"]
    accessControlAllowOriginList = ["https://foo.bar.org","https://example.org"]
    accessControlMaxAge = 100
    addVaryHeader = true

I define it on the traefik service using labels. A file provider defining the middleware should work just as well.

I defined it on the traefik service using labels also, like so:

services:
  reverse-proxy:
    image: traefik:v2.9
    ports:
      - ${API_PORT}:80
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./traefik.yml:/etc/traefik/traefik.yml
    labels:
      - "traefik.http.middlewares.cors.headers.accesscontrolallowmethods=*"
      - "traefik.http.middlewares.cors.headers.accesscontrolalloworiginlist=*"
      - "traefik.http.middlewares.cors.headers.accesscontrolmaxage=100"
      - "traefik.http.middlewares.cors.headers.addvaryheader=true"

Doesn't work though :confused:

Nothing here?

I'm facing the same issue...

http:
  middlewares:
    cors:
      headers:
        sslRedirect: true
        accessControlMaxAge: 100
        addVaryHeader: true
        accessControlAllowCredentials: true
        accessControlAllowMethods:
          - OPTIONS
          - POST
          - GET
          - PUT
          - DELETE
          - PATCH
        accessControlAllowHeaders: "*"
        accessControlAllowOriginList:
          - "*"
          - 'http://localhost:5173'
traefik:
    image: traefik:latest
    command:
      - "--api.dashboard=true"
      - "--api.debug=true"
      - "--log.level=DEBUG"
      - "--api.insecure=true"
      - "--providers.docker=true"
      - "--providers.file.filename=/etc/traefik/dynamic_conf.yml"
    ports:
      - "80:80"
      - "8080:8080"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./dynamic_conf.yml:/etc/traefik/dynamic_conf.yml
  
  auth-service:
    build: ./services/auth-service
    ports:
      - 8081:8081
    labels:
      - traefik.enable=true
      - traefik.http.routers.auth-service.rule=(Host(`soud.localhost`) && PathPrefix(`/auth`))
      - "traefik.http.routers.auth-service.middlewares=cors@file"
    env_file:
      - ./services/auth-service/.env
    depends_on:
      - mongodb
      - traefik

Is that config even working? Where is your entrypoint (listening port)?

Compare with simple Traefik example.

1 Like

Yeah, that config worked correctly because of the ports and the providers.docker=true. Initially, I overlooked the necessity of defining the HTTPS listening port.

I appreciate you sharing your repo to the Traefik example. It was immensely helpful in addressing several questions I had.

Ultimately, I discovered the root cause of my configuration issue. It stemmed from modifications I had previously made to the host file, which resulted in the localhost:8080 address no longer being reachable.

Again, thanks so much friend, you are a good friend!

This topic was automatically closed after 4 days. New replies are no longer allowed.