Using Traefik’s stripPrefix Middleware with Docker Labels to Hide Internal Paths
Learn how to use Traefik’s label‑based Docker provider and the stripPrefix middleware to hide internal service paths while keeping backend code unchanged.
31 Jul 2026, 16:46 UTC

Problem: Backend services expect traffic at the root, but you want a clean public URL
You have a set of containerized services that listen on / internally (for example, a simple web app that generates links like /static/style.css). Externally you want to reach them under a versioned or namespaced path such as https://api.example.com/v1/ without changing the application code. Manually rewriting URLs in each service is error‑prone and creates drift between environments.
Thesis: Traefik’s label‑based Docker provider lets you attach the stripPrefix middleware directly to a router, removing the public path before forwarding to the container—no static config reloads needed.
How dynamic discovery works
When Traefik runs with the Docker provider, it watches containers for labels that match the traefik.enable pattern. Each labeled container can define a router, a service, and one or more middlewares entirely through labels. Traefik builds the routing table in memory; when a container starts or stops, the table updates automatically.
Key label pieces for stripPrefix
traefik.enable=true– turns the container into a routing object.traefik.http.routers..rule=Host(`api.example.com`) && PathPrefix(`/v1/`)– matches the public hostname and the version prefix.traefik.http.routers..middlewares=strip-v1@docker– references a middleware defined elsewhere.traefik.http.middlewares.strip-v1.stripprefix.prefix=/v1– tells Traefik to cut the/v1/segment before forwarding.traefik.http.services..loadbalancer.server.port=80– points to the container’s listening port.
Worked example (docker‑compose)
Below is a minimal compose file that launches Traefik and a test “whoami” container. The whoami service echoes the request it receives, making it easy to see whether the prefix was stripped.
version: '3.8'
services:
traefik:
image: traefik:v3.0
command:
- '--api.insecure=true'
- '--providers.docker=true'
- '--entrypoints.web.address=:80'
ports:
- '80:80'
- '8080:8080' # dashboard
volumes:
- '/var/run/docker.sock:/var/run/docker.sock:ro'
labels:
- 'traefik.enable=true'
- 'traefik.http.routers.traefik.rule=Host(`traefik.localhost`)"
- 'traefik.http.routers.traefik.service=api@internal'
whoami:
image: traefik/whoami
labels:
- 'traefik.enable=true'
- 'traefik.http.routers.whoami.rule=Host(`api.example.com`) && PathPrefix(`/v1/`)"
- 'traefik.http.routers.whoami.middlewares=strip-v1@docker'
- 'traefik.http.middlewares.strip-v1.stripprefix.prefix=/v1'
- 'traefik.http.services.whoami.loadbalancer.server.port=80'
To try this locally, replace api.example.com with a name that resolves to your host (e.g., add an entry to /etc/hosts pointing to 127.0.0.1).
Verification steps
- Start the stack:
docker compose up -d(requires permission to access the Docker socket; running as a user in thedockergroup or usingsudois typical). - Request the public path:
curl -H "Host: api.example.com" http://localhost/v1/. The whoami container should return a response that includesHost: api.example.comandURI: /—note the missing/v1/segment. - Check Traefik’s dashboard at
http://localhost:8080(orhttp://traefik.localhost:8080if you set the Host rule) to see the routerwhoami, middlewarestrip-v1, and servicewhoamilisted as enabled. - Inspect the container logs:
docker compose logs whoamishould show the request line without the prefix, confirming that stripping occurred before the request reached the app.
Trade‑off and limitation
The stripPrefix middleware works well when the backend generates only relative URLs or uses the X-Forwarded-Prefix header to rebuild links. If the application emits absolute redirects (e.g., Location: http://api.example.com/v1/login) the client will receive a link that still contains the stripped prefix, leading to 404s. Mitigation options include:
- Configuring the backend to be prefix‑aware (many frameworks have a “base URL” setting).
- Adding the
addPrefixmiddleware after the service to re‑inject the prefix for redirects, or using theheadersmiddleware to adjustLocationheaders. - Using Traefik’s
RedirectSchemeorRedirectRegexmiddlewares at the router level when you need to rewrite the entire path.
Another operational consideration is security: exposing the Docker socket (even read‑only) gives Traefik broad visibility into the host. In production you should restrict access with a socket proxy, limit the socket to read‑only mode, or use Traefik’s provider.constraints to label only a subset of containers.
Actionable closing
If you need to expose internal services under a clean public path without altering the containers, start by adding the traefik.enable label and a stripPrefix middleware as shown. Verify the strip works with a simple whoami test, then adjust your application or add complementary middlewares to handle any absolute URLs it generates. Keep an eye on Docker‑socket exposure and consider tightening that access as you move toward production.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.