2 services in 2 ports of the same container. One works, the identical other doesn't

I have 2 websites running in the same Nginx container, each one of them listening in a different port. Both sites work well without Traefik but I would like to use Traefik version 2.2.8 to access them using a prefix instead of having to enter the port name in the URL.

  • Currently: h††p://localhost:80 > site 1 home page. Wanted: h††p://localhost/site1/ > site 1 home page.
  • Currently: h††p://localhost:81 > site 2 home page. Wanted: h††p://localhost/site2/ > site 2home page.

Using Docker labels I have defined 2 routers and 2 middlewares, one for each site. Each router has a unique name, a unique prefix rule, a unique service and shared middleware. Each service has a unique name and and a unique load balancer port.

Site 2 is working fine with Traefik. When I visit h††p://localhost/site2/ I can see site 2 home page and Traefik logs show the correct router (site2@docker) is being used:

* traefik    | 172.19.0.1 - - [24/Aug/2020:09:58:31 +0000] "GET /site2/ HTTP/1.1" 200 75115 "-" "-" 51 "site2@docker" "[-])" 14ms

However, when I visit h††p://localhost/site1/ I get a 404 error and Traefik logs show incorrect router is being used (traefik@docker):

traefik    | 172.19.0.1 - - [24/Aug/2020:09:58:33 +0000] "GET /site1/ HTTP/1.1" 404 19 "-" "-" 52 "traefik@docker" "-" 0ms

If I look at the Traefik dashboard or the Traefik API responses I think I'm correctly linking routes with services, but I may have misunderstood the docs. It seems the later router definition is replacing the former.

Here is my Docker Compose file

version: '3'
services:

  traefik:
    container_name: traefik
    image: traefik:latest
    labels:
      - traefik.enable=true
      - traefik.http.routers.traefik.service=api@internal
      - traefik.http.routers.traefik.rule=Host(`localhost`)
    networks:
      - traefik
    ports:
      - "80:80"
    volumes:
      - ./traefik.yml:/etc/traefik/traefik.yml:ro

  nginx:
    container_name: nginx
    image: nginx:alpine
    labels:
      - traefik.enable=true
      - traefik.http.middlewares.nginx-stripprefix.stripprefix.prefixes=/site1,/site2
      # SITE 1
      - traefik.http.routers.site1.service=site1@docker
      - traefik.http.routers.site1.rule=PathPrefix(`/site1`)
      - traefik.http.routers.site1.middlewares=nginx-stripprefix
      - traefik.http.services.site1.loadbalancer.server.port=80
      # SITE 2
      - traefik.http.routers.site2.service=site2@docker
      - traefik.http.routers.site2.rule=PathPrefix(`/site2`)
      - traefik.http.routers.site2.middlewares=nginx-stripprefix
      - traefik.http.services.site2.loadbalancer.server.port=81
    networks:
      - traefik
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./www:/www

networks:
  traefik:
    external: true

Here is my Traefik config file

api:
  dashboard: true

entryPoints:
  h††p:
    address: ":80"

accessLog: {}
log:
  level: DEBUG

providers:
  docker:
    network: traefik
    exposedByDefault: false
    defaultRule: "Host(`localhost`) && PathPrefix(`/{{ index .Labels \"com.docker.compose.service\" }}`)"

And here is my Nginx config file (only the relevant part)

server {
	server_name localhost;
	listen 80;
	root /www/site1;
}
server {
	server_name localhost;
	listen 81;
	root /www/site2;
}

Here is the API response for /api/http/routers:

[
  {
    "entryPoints": ["http"],
    "middlewares": ["nginx-stripprefix@docker"],
    "service": "site1@docker",
    "rule": "PathPrefix(`/site1`)",
    "status": "enabled",
    "using": ["http"],
    "name": "site1@docker",
    "provider": "docker"
  },
  {
    "entryPoints": ["http"],
    "middlewares": ["nginx-stripprefix@docker"],
    "service": "site2@docker",
    "rule": "PathPrefix(`/site2`)",
    "status": "enabled",
    "using": ["http"],
    "name": "site2@docker",
    "provider": "docker"
  },
  {
    "entryPoints": ["http"],
    "service": "api@internal",
    "rule": "Host(`localhost`)",
    "status": "enabled",
    "using": ["http"],
    "name": "traefik@docker",
    "provider": "docker"
  }
]

Could someone please help me to understand what I'm I doing wrong? why the /site2 prefix is being correctly served by the site2@docker router/service but /site1 prefix is being served by the traefik@docker router/service?

The funny thing is the set up was working for a while, but I added a few extra sites and it stopped working. After deleting the extra sites and recreating the containers, it never worked again.

I find out the issue is related with the length of the rule.

The above message is a simplification of my actual scenario. My actual scenario uses prefixes that vary in length. If the lengths of all my sites rules is the same, the the above config works. i.e:

- traefik.http.routers.site1.rule=PathPrefix(`/site1`)
- traefik.http.routers.site2.rule=PathPrefix(`/site2`)

But if one of the two rules is longer than the other, the sorter will not work even if the path matches i.e:

- traefik.http.routers.site1.rule=PathPrefix(`/site1`)
- traefik.http.routers.site2.rule=PathPrefix(`/site22`)

the site1 will return 404.

If I assign same priority to all routers, then none of the routers work. i.e:

- traefik.http.routers.site1.rule=PathPrefix(`/site1`)
- traefik.http.routers.site1.priority=1
- traefik.http.routers.site2.rule=PathPrefix(`/site22`)
- traefik.http.routers.site2.priority=1

then both site1 and site22 will return 404.