404 page not found when using with PathPrefix

Background:
I'm exploring the use of Traefik as a reverse proxy to oversee multiple containers, each serving unique services. These containers don't have any backends(standalone application). My objective is to centralize these services under a unified domain (abc.domain.com ), allowing access to each service via distinct paths like abc.domain.com/user1 , abc.domain.com/user2 , and so on.

Problem Statement:
Before proceeding with my current requirement, I first attempted to configure path-based routing for a sample container (Grafana) using Traefik's PathPrefix rule. Upon trying to access localhost/grafana, the request successfully reaches the Grafana container. However, instead of being directed to localhost:6060/login (where Grafana is hosted), the browser incorrectly tries to access localhost/login, leading to a "404 page not found" error.

Expected Behavior:
My expectation is that Traefik would appropriately route requests to the designated containers based on the configured paths, ensuring that the request URLs accurately reflect the ports of the respective containers. Specifically, accessing localhost/grafana should seamlessly redirect to localhost:6060/login.

Docker Compose Configuration:

version: "3"
services:
  traefik:
    image: 'traefik:v2.7'
    container_name: 'traefik'
    command: 
      - "--api.insecure=true"
      - '--accesslog=true'
      - "--providers.docker"
      - "--log.level=DEBUG"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--providers.docker.exposedByDefault=false" 

    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"
      
  grafana:
    image: grafana/grafana
    container_name: grafana
    ports: 
     - "6060:3000"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.grafana.rule=Host(`localhost`) && PathPrefix(`/grafana`)"   
      - "traefik.http.routers.grafana.entrypoints=web"
      - "traefik.http.routers.grafana.service=grafana"
      - "traefik.http.services.grafana.loadbalancer.server.port=3000"


Request for Assistance:
I'm reaching out to seek your guidance on identifying the root cause of this issue and exploring potential solutions.
Any insights, recommendations, or best practices for configuring Traefik for effective path-based routing would be greatly appreciated!

PS: When Using with Host(grafana.localhost) its working fine but it's failing with PathPrefix

This belongs into the top 10 FAQs.

You can not simply place a GUI web app under a path, even when you use strip-prefix middleware to remove the path for the forwarded request.

The first page might load, but it usually contains links, script and images with absolute path names, so they can’t be loaded from the browser.

It only works when some kind of "base path" can be set in the GUI web application.

Best practice is to use a sub-domain instead, so the web application can run on root (/) path.

I'm not very familiar with web development, but I have a Vue.js application with a main.js file located in the router directory. I want to configure custom paths for accessing my containers through path-based routing. Can you guide me on what changes I need to make in the main.js file to set up custom and dynamic paths for my domain?

// The Vue build version to load with the `import` command

// (runtime-only or standalone) has been set in webpack.base.conf with an alias.

import Vue from 'vue'

import App from './App'

import router from './router'

import axios from 'axios'

Vue.config.productionTip = false

Vue.prototype.$http = axios

/* eslint-disable no-new */

new Vue({

el: '#app',

router,

components: { App },

template: '<App/>'

})

You need to make two changes:

Change the path of your start page (like /myApp1/), but also ensure that all resources are placed under that path (like /myApp1/static/, not root).

This is very Vue specific, so you probably won’t find an answer here.

Hey, I've been diving deep into Traefik configurations, especially regarding path-based routing. While many community members have found success with strip prefix middleware, my aim is to achieve path-based routing directly.

Check out this discussion thread where they discuss strip prefix middleware: traefik-is-unable-to-strip-prefix-before-forwarding-request-to-a-service

However, my goal remains fixed on implementing path-based routing. It's crucial for my project, so I'm determined to make it work even with using StripPrefix. Let's keep pushing forward until we crack this!

here is my docker-compose.yml:

version: "3"
services:
  traefik:
    image: 'traefik:v2.7'
    container_name: 'traefik'
    # network_mode: 'host'
    command: 
      - "--api.insecure=true"
      - '--accesslog=true'
      - "--providers.docker"
      - "--log.level=DEBUG"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--providers.docker.exposedByDefault=false" 
      # - "--providers.docker.endpoint=unix:///var/run/docker.sock"
      # - "--providers.docker.useBindPortIP=true"
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"
      
  grafana:
    image: grafana/grafana
    container_name: grafana
    ports: 
     - "6060:3000"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.grafana.rule=(Host(`localhost`) && Path(`/grafana`))"
      - "traefik.http.routers.grafana.entrypoints=web"
      - "traefik.http.routers.grafana.service=grafana"
      - "traefik.http.middlewares.grafana-stripprefix.stripprefix.prefixes=/grafana"
      - "traefik.http.routers.grafana.middlewares=grafana-stripprefix"
      - "traefik.http.services.grafana.loadbalancer.server.port=3000"
     


Also I would like to know about this, what if my application doesn't contains any links, script and images with absolute path name, will that work?

I didn't understand this properly, can you please explain with any example?

For the newbies among us:

When you request a URL from Traefik, like example.com/app, you can route by domain and (optional) path. When Traefik is forwarding to the target service, the path will be re-used (GET /app). So your target service needs to respond to this specific path.

You can use stripprefix middleware, to remove the path from the forwarding request. This works great with API apps, that only use a single request.

Usually modern web apps will need a lot of dependencies, like /static/script.js and /img/logo.jpg. Those are linked with absolute path from the initial page you load. So even if you get external path /app to load the first page, the browser will then try to load /static/script.js, to which Traefik can not route with path /app.

So if path based routing should work with a web app, you need to tell it to respond to the desired path, with the initial start page and also with all required dependencies.

Best practice is to use sub-domains, so you don't need to mess with path settings inside the web app - and sometimes its not even possible.

Two examples:

  • Traefik dashboard is fixed to /dashboard, this can't never be changed.
  • Wordpress can be configured to use a /app by setting a "base path".

Suppose you have an application running on your server, and it's accessible only through a specific port, like localhost:6060. This application doesn't have any built-in base path routing – it simply works on the port provided. Now, you want to make this application accessible under a specific base path, like example.com/myapp.

Can I do that, because it doesnt have any dependencies it just run a port without any base path?

Will StripPrefix work in this when any Route is not defined in the application?

You can use a path when it’s a single request.

You can not use a path when it’s a web app that has links on the page and uses scripts and images.

version: "3"
services:
  traefik:
    image: 'traefik:v2.7'
    container_name: 'traefik'
    # network_mode: 'host'
    command: 
      - "--api.insecure=true"
      - '--accesslog=true'
      - "--providers.docker"
      - "--log.level=DEBUG"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--providers.docker.exposedByDefault=false" 
      # - "--providers.docker.endpoint=unix:///var/run/docker.sock"
      # - "--providers.docker.useBindPortIP=true"
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"
      
  grafana:
    image: test:latest
    container_name: grafana
    ports: 
     - "6060:80"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.grafana.rule=(Host(`localhost`) && PathPrefix(`/user`))"
      - "traefik.http.routers.grafana.entrypoints=web"
      - "traefik.http.routers.grafana.service=grafana"
      - "traefik.http.middlewares.grafana-stripprefix.stripprefix.prefixes=/user"
      - "traefik.http.routers.grafana.middlewares=grafana-stripprefix"
      - "traefik.http.services.grafana.loadbalancer.server.port=80"

So the above is my docker-compose.yml , So I'm able to access localhost:6060 but I'm not able to access through localhost/user

P.S: I just used grafana as service name but this is not a grafana container and also If I set the host as app.localhost but Path based routing is not working

Here are my screenshots for better understanding


Use a current Traefik image, not a 3 year old version.

Use whoami service to see actual request sent to target service. Check simple Traefik example.

And again: page with background is a web app. Usually won’t work. Check browser developer tools network tab to see further requests and probably 404 not found errors.

Thank you @bluepuma77 , All doubts resolved!

Welcome to world of sub-domains :wink:

1 Like

Hey!
Again I got a doubt :smiling_face_with_tear:

I'm using the strip prefix for the request localhost/user, I'm telling to strip the prefix /user and forward this request to localhost:6060 right? ideally then it should work right?

The issue is that the initial page will tell the browser to load the image with a fixed path like /img/background.jpg.

The browser can never load that file, as it does not have /user path and therefore is never matched by Traefik to the right target service.

Ok.
Now if I use subdomain lets say I have registered a subdomain(blue.puma.com) to my server(172.80.69.9), now with traefik, can I host my application on (python-blue.puma.com) or (python.blue.puma.com)?
My question can be nerdy but I dont know more in advance about how domains work :smiling_face_with_tear:

You can use a domain (example.com), a subdomain (sub1.example.com) and a second level subdomain (sub2.sub1.example.com)

Just make sure the according DNS entry points to your Traefik instance.

Just for reference: simple Traefik example.

I'm looking for a solution to dynamically configure subdomains for my services. Imagine I have over 50 services generated dynamically by my application, each running in its own container. Instead of manually configuring DNS entries for each service, I want to access them through unique subdomains. For example, user1.subdomain.example.com, user2.subdomain.example.com, and so forth.

I'm considering using Traefik as my reverse proxy and load balancer. How can I achieve this with Traefik, and what do I need to ensure in the Traefik labels of my services to make this dynamic subdomain routing work seamlessly?

Some DNS providers enable wildcard sub-domains, so you need to check with your provider. Otherwise you need to create every single one manually.

In Traefik you can create a default rule, so the service name could be the sub-domain. Otherwise you can just set the desired sub-domain in labels for the service/container.

Check simple Traefik example.