A Traefik middleware plugin that lets a request through only when a header carries one of your API keys. Everything else gets a response you choose, such as 401 Unauthorized.
- Any header, with an optional scheme:
X-API-Key: <key>orAuthorization: Bearer <key>. - Several keys at once, so you can rotate them without downtime.
- Keys in plain text, as SHA-256 digests (
sha256:<hex>), or read from a Kubernetes Secret. - Exact-path, path-prefix and method exclusions for health checks and CORS preflights.
- A configurable rejection: status, body, content type and headers.
- Keys are compared as SHA-256 digests in constant time.
- Fails closed: an invalid configuration stops Traefik from serving the route rather than letting requests through.
Declare the plugin in Traefik's static configuration:
experimental:
plugins:
apikeyauth:
moduleName: github.com/firecrawl/traefik-api-key-auth
version: v0.1.0Traefik loads plugins at startup, so restart it after adding this.
Traefik's Kubernetes CRD provider replaces urn:k8s:secret:<secret>:<key> with that key of a Secret in the Middleware's namespace, so the key never appears in the Middleware itself:
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: api-key
namespace: my-app
spec:
plugin:
apikeyauth:
headerName: X-API-Key
keys:
- urn:k8s:secret:my-app-secrets:API_KEY
excludedPaths:
- /healthAttach it to an IngressRoute under middlewares, or to an Ingress with the annotation traefik.ingress.kubernetes.io/router.middlewares: my-app-api-key@kubernetescrd.
http:
middlewares:
api-key:
plugin:
apikeyauth:
headerName: Authorization
scheme: Bearer
keys:
- sha256:b16481f1ada69c0032fb6624d311dc4b3ed6dbb5a2becc4dd3d82f31014ee6e8
response:
statusCode: 401
contentType: application/json
body: '{"error":"unauthorized"}'
headers:
WWW-Authenticate: Bearerlabels:
- traefik.http.routers.my-app.middlewares=api-key
- traefik.http.middlewares.api-key.plugin.apikeyauth.headerName=X-API-Key
- traefik.http.middlewares.api-key.plugin.apikeyauth.keys=sha256:b16481f1ada69c0032fb6624d311dc4b3ed6dbb5a2becc4dd3d82f31014ee6e8Labels can be read by anyone with access to the Docker API, so use a sha256: digest here rather than the key itself.
- Fails closed. Missing or empty keys, a malformed digest or an invalid status make the middleware fail to build, and Traefik does not serve routes that use it. In Kubernetes the same happens when a
urn:k8s:secretreference cannot be resolved. - Whitespace. Surrounding whitespace is trimmed from keys and header values. A header value cannot carry it anyway, and secret files often end in a newline.
- One header only. A request that sends the header more than once is rejected.
- Exclusions are matched after resolving
.and..segments, so/health/../adminis checked as/admin. Requests that match an excluded method reach your backend without any check, so make sure the backend does nothing sensitive for them. - Forwarding. The header reaches your backend unchanged unless
removeHeaderis set.
# A new key
openssl rand -hex 32
# Its digest, for the sha256: form
printf '%s' "$KEY" | sha256sumTraefik runs plugins in the Yaegi Go interpreter, which provides the Go 1.22 standard library, so the module targets Go 1.22 and has no dependencies. go vet ./... reports any use of newer standard library APIs.
To try local changes, use Traefik's local plugin mode: place the repository at ./plugins-local/src/github.com/firecrawl/traefik-api-key-auth next to the Traefik binary and declare it with:
experimental:
localPlugins:
apikeyauth:
moduleName: github.com/firecrawl/traefik-api-key-auth