-
Notifications
You must be signed in to change notification settings - Fork 4.6k
Description
Describe the bug
The Vault UI URL-encodes forward slashes (/) in KV secret paths, converting them to %2F. This causes compatibility issues with reverse proxies like Traefik v3.6.3+, which now block encoded slashes by default for security reasons. When accessing a KV secret with a path containing slashes (e.g., my-app/production/config), the UI generates encoded paths like:
/ui/vault/secrets/secret/kv/my-app%2Fproduction%2Fconfig
Instead of the expected:
/ui/vault/secrets/secret/kv/my-app/production/config
To Reproduce
Steps to reproduce the behavior:
- Deploy Vault behind Traefik v3.6.3+ with default settings
- Log into the Vault UI
- Navigate to a KV secrets engine
- Create a new secret with a path containing slashes (e.g., my-app/production/config)
- See 400 Bad Request error from Traefik due to encoded slashes in the request path
example logs
2025-12-17T20:10:14Z DBG github.com/traefik/traefik/v3/pkg/server/router/deny.go:52 > Rejecting request because it contains encoded character %2F in the URL path: /v1/secret/data/my-app%2Fproduction%2Fconfig
142.250.191.187 - - [17/Dec/2025:20:10:14 +0000] "POST /v1/secret/data/my-app%2Fproduction%2Fconfig HTTP/1.1" 400 0 "-" "-" 31 "vault@docker" "-" 0ms
Expected behavior
paths should not include encoded characters
Environment:
- Vault Server Version: (any recent version)
- Vault CLI Version: (any recent version)
- Server Operating System/Architecture: Linux/amd64
- Reverse Proxy: Traefik v3.6.3+
Additional context
Traefik v3.6.3 introduced security restrictions that block requests containing encoded characters in the path by default, including %2F (encoded slash). See: https://doc.traefik.io/traefik/v3.6/security/request-path/ While users can work around this by setting allowEncodedSlash: true in Traefik configuration, this reduces security posture. Ideally, the Vault UI would not encode slashes that are legitimate path separators.
docker compose for testing and verification
services:
traefik:
image: traefik:v3.6.5
ports:
- "80:80"
- "8080:8080"
command:
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--log.level=DEBUG"
- "--accesslog=true"
- "--accesslog.format=common"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
vault:
platform: linux/x86_64
image: hashicorp/vault:1.21
ports:
- '8200:8200'
environment:
- VAULT_DEV_ROOT_TOKEN_ID=dev-only-token
volumes:
- vault_data:/vault/data
labels:
- "traefik.enable=true"
- "traefik.http.routers.vault.rule=Host(`localhost`)"
- "traefik.http.routers.vault.entrypoints=web"
- "traefik.http.services.vault.loadbalancer.server.port=8200"
volumes:
vault_data: