Adding multiple header middlewares

Hi all,

Here's my use case regarding the header middleware running in Docker:
I'd like to be able to create one middleware called std-headers with the file provider (std-headers@file), and then combine that with additional security features from a second or third middleware (i.e.: traefik-headers@docker,allowed-hosts@file).

I've tried multiple ways with and without using Chain, but it appears that the headers are added as one 'block' or 'set' - meaning all of the Headers are added at once, and if a second middleware also tries to modify the Headers, the second one will be ignored. When I look at the dashboard I can see the middlewares showing up for the router, but found that only one is actually being applied.

I'm not looking to replace headers (I'd expect an error/conflict), but I would like to leave off ContentSecurityPolicy for example and specify that per container.

Is it currently possible to do this? I would test by having all headers in one middleware (working ok), then separate the STS headers (easy to identify) and try to combine them with chain, or by calling both of them individually, etc. I can combine the headers middleware with auth or addprefix, but can't combine multiple header middlewares together.

Thanks!

I'm also curious how this is handled inside docker-compose with labels. I'd love to add an example to the docs once I figure it out. In meantime will check out the code :slight_smile:

Resurrecting this thread (Now end of Feb 2020). I just stumbled into this today as well. As of 2.1 it is still an issue. As a result I have lots of duplicated header code floating around in my compose files.

+1. Just ran into this myself too. Some kind of logic to AND / OR / NOT etc chained or combined headers would be sweet.

I don't know if I have misunderstood the problem you are describing, I am running 2.2.8 and using using chains have multiple middleware definitions adding (custom) headers with each set of custom headers present in the final output.

I've provided my config below in case helpful - have I misunderstood the problem you are describing?

  [http.middlewares.middlewares-secure-headers]
    [http.middlewares.middlewares-secure-headers.headers]
      accessControlAllowMethods= ["GET", "OPTIONS", "PUT"]
      accessControlMaxAge = 100
      hostsProxyHeaders = ["X-Forwarded-Host"]
      sslRedirect = true
      stsSeconds = 63072000
      stsIncludeSubdomains = true
      stsPreload = true
      forceSTSHeader = true
#      frameDeny = true #overwritten by customFrameOptionsValue
      contentTypeNosniff = true 
      browserXssFilter = true 
#      sslForceHost = true # add sslHost to all of the services
      referrerPolicy = "same-origin" 
#      Setting contentSecurityPolicy is more secure but it can break things. Proper auth will reduce the risk.
#      the below line also breaks some apps due to 'none' - sonarr, radarr, etc.
#      contentSecurityPolicy = "frame-ancestors '*.tld.domain:*';object-src 'none';script-src 'none';"
      featurePolicy = "camera 'none'; geolocation 'none'; microphone 'none'; payment 'none'; usb 'none'; vr 'none';" 
      [http.middlewares.middlewares-secure-headers.headers.customResponseHeaders]
        X-Robots-Tag = "none,noarchive,nosnippet,notranslate,noimageindex,"
        server = ""
        X-DJR-Debug = "debug header"

  [http.middlewares.middlewares-test-header]
    [http.middlewares.middlewares-test-header.header]
      [http.middlewares.middlewares-test-header.headers.customResponseHeaders]
        X-Custom-Response-Header = "Header value 1"

  [http.middlewares.middlewares-test-header2]
    [http.middlewares.middlewares-test-header2.header]
      [http.middlewares.middlewares-test-header2.headers.customResponseHeaders]
        X-Custom-Response-Header = "Header value 2"

  [http.middlewares.middlewares-test-header3]
    [http.middlewares.middlewares-test-header3.header]
      [http.middlewares.middlewares-test-header3.headers.customResponseHeaders]
        X-Custom-Test-Header = "Header value 3"
        X-Custom-Another-Header = "Header value 4"
        X-Custom-Different-Header = "Header value 5"

    [http.middlewares.chain-test-headers]
        [http.middlewares.chain-test-headers.chain]
            middlewares = ["middlewares-test-header", "middlewares-test-header3", "middlewares-secure-headers"]

18 months on, traefik 2.4.5 --- still unable to apply multiple middlewares to a router via "labels:" list in docker-compose.yml

middleware to require basicauth

  - "traefik.http.middlewares.site1-rtr443-auth.basicauth.users=user:$$apr1$$password"
  - "traefik.http.routers.site1-rtr443.middlewares=site1-rtr443-auth"

middleware to soft limit requests

  - "traefik.http.routers.site1-rtr443.middlewares=site1-rtr443-ratelimit"
  - "traefik.http.middlewares.site1-rtr443-ratelimit.ratelimit.average=5"
  - "traefik.http.middlewares.site1-rtr443-ratelimit.ratelimit.burst=10"

middleware to hard limit requests

  - "traefik.http.routers.site1-rtr443.middlewares=site1-rtr443-inflightreq"
  - "traefik.http.middlewares.site1-rtr443-inflightreq.inflightreq.amount=10"

All 3 middlewares appear in traefik dashboard.

But only the last middleware (inflightreq) is applied to "site1-rtr443" router.

This highly annoying waste of time is doubled by official documentation still noting nothing; instead, the clue is on reddit:

Hi,

Actually you can do it with chain middlewares. Chain - Traefik | Site | v2.4

Step one

By adding one middleware

  - "traefik.http.middlewares.auth-and-limits.chain.middlewares=site1-rtr443-auth@docker,site1-rtr443-ratelimit@docker,site1-rtr443-inflightreq@docker"

Step two

Remove all the middlewares - "traefik.http.routers.site1-rtr443.middlewares= ... ... ...

Step three

Use the new chain on your router

- "traefik.http.routers.site1-rtr443.middlewares=auth-and-limits@docker"