A Traefik middleware plugin that derives new request headers from existing ones using regex pattern matching. It replicates the behavior of the nginx map directive.
Header Map inspects one or more incoming request headers (e.g. Host, User-Agent), matches their values against a list of regex rules, and sets new headers with the corresponding values. If no rule matches, a configurable default value is used.
All mapping rules are defined in Traefik's dynamic configuration (Middleware CRD in Kubernetes, file provider, Docker labels, etc.), so adding or removing rules requires no plugin rebuild and no Traefik restart.
# traefik.yml
experimental:
plugins:
header-map:
moduleName: github.com/rodrigoechaide/header-map
version: v0.1.0# dynamic.yaml
http:
middlewares:
header-map:
plugin:
header-map:
mappings:
- sourceHeader: "Host"
targetHeader: "X-APP-ID"
defaultValue: "unknown_locale"
rules:
- regex: 'example\.com$'
value: "en"
- regex: 'example\.es$'
value: "es"
- sourceHeader: "User-Agent"
targetHeader: "X-UA-Device"
defaultValue: "desktop"
rules:
- regex: "(?i)(android|iphone|mobile)"
value: "mobile"
routers:
my-router:
rule: "PathPrefix(`/`)"
service: my-service
middlewares:
- header-mapapiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: header-map
namespace: traefik-system
spec:
plugin:
header-map:
mappings:
- sourceHeader: "Host"
targetHeader: "X-APP-ID"
defaultValue: "unknown_locale"
rules:
- regex: 'example\.com$'
value: "en"
- regex: 'example\.es$'
value: "es"
- sourceHeader: "User-Agent"
targetHeader: "X-UA-Device"
defaultValue: "desktop"
rules:
- regex: "(?i)(android|iphone|mobile)"
value: "mobile"| Field | Type | Required | Description |
|---|---|---|---|
mappings |
[]Mapping |
Yes | List of header mapping definitions |
Mapping:
| Field | Type | Required | Description |
|---|---|---|---|
sourceHeader |
string |
Yes | Request header to read (e.g. Host, User-Agent) |
targetHeader |
string |
Yes | Request header to set with the matched value |
defaultValue |
string |
No | Value to use when no rule matches. If empty, no header is set on miss. |
rules |
[]Rule |
No | Ordered list of regex rules. First match wins. |
Rule:
| Field | Type | Required | Description |
|---|---|---|---|
regex |
string |
Yes | Go-flavored regular expression to match against the source header value |
value |
string |
Yes | Value to set in the target header when the regex matches |
- For each mapping, the source header value is tested against rules in order. The first matching regex wins.
- If
sourceHeaderisHostand the header is absent from the request, the plugin falls back toreq.Host. - Regex patterns use Go's
regexpsyntax (RE2). Use(?i)for case-insensitive matching. - Regex patterns are compiled once when the middleware is initialized, not on every request.
Clone the repository and use the provided Makefile:
make test # run unit tests with coverage
make lint # run golangci-lint (requires golangci-lint installed)A ready-to-use local development setup is included in the docker/ directory. It loads the plugin from the repo root using Traefik's local plugin mode.
docker/
├── docker-compose.yml # Traefik + whoami backend
└── traefik-config/
├── traefik.yml # Traefik static config (local plugin registration)
└── dynamic.yml # Dynamic config (middleware rules + routing)
Start the stack:
cd docker
docker compose upTest the plugin:
# Host mapping: localhost -> "local-test"
curl -s http://localhost/ | grep X-App-Id
# Host mapping: example1.pl -> "pl"
curl -s -H "Host: example1.pl" http://localhost/ | grep X-App-Id
# User-Agent mapping: mobile device -> "mobile"
curl -s -H "User-Agent: Mozilla/5.0 (iPhone)" http://localhost/ | grep X-Ua-DeviceThe Traefik dashboard is available at http://localhost:8080.
Edit local/traefik-config/dynamic.yml to change mapping rules. Edit headermap.go at the repo root to change plugin logic, then restart the stack with docker compose restart traefik.
To make the plugin available in the Traefik Plugins Catalog:
- Ensure the GitHub repository is public.
- Add the
traefik-plugintopic to the repository. - Verify
.traefik.ymlexists at the repo root with validtestData. - Verify
go.modexists at the repo root. - Create a git tag (e.g.
v0.1.0). - The catalog polls GitHub daily and will pick up the plugin automatically.