Skip to content

Middlewares

Quick Start

MethodOrderUse CaseConfiguration
EntrypointOrderedGlobal middlewaresconfig.yml
Middleware ComposeOrderedReusable configsconfig/middlewares/*.yml
Docker LabelsUnordered*Per-routeContainer labels
Route FilesUnordered*Per-routeRoute files

Set priority for unordered methods when order matters.

Basic Example

yaml
# Global middleware (entrypoint)
entrypoint:
  middlewares:
    - use: real_ip
      header: X-Real-IP
      from: [127.0.0.1, 192.168.0.0/16]

# Per-service (Docker labels)
services:
  app:
    labels:
      proxy.myapp.middlewares.redirect_http.priority: 1
      proxy.myapp.middlewares.cidr_whitelist.priority: 2
      proxy.myapp.middlewares.cidr_whitelist.allow: 127.0.0.1, 192.168.0.0/16

Naming Convention

Middleware names and options are case-insensitive: redirectHTTP, redirect_http, RedirectHttp are equivalent.

Applying Middlewares

Entrypoint & Middleware Compose

yaml
# Entrypoint (config.yml)
entrypoint:
  middlewares:
    - use: CloudflareRealIP
    - use: CIDRWhitelist
      allow:
        - 127.0.0.1
        - 223.0.0.0/8

# Middleware Compose (config/middlewares/whitelist.yml)
myWhitelist:
  - use: CloudflareRealIP
  - use: CIDRWhitelist
    allow:
      - 127.0.0.1
      - 223.0.0.0/8

Docker Labels

yaml
# Single line
proxy.#1.middlewares.{name}.{option}: { value }

# YAML block
proxy.#1.middlewares.{name}: |
  {option}: {value}

Route Files

yaml
myapp:
  middlewares:
    { name }:
      { option }: { value }

Reusing Compositions

yaml
# Docker labels
proxy.#1.middlewares.myWhitelist@file:

# Route file
myapp:
  middlewares:
    myWhitelist@file:

# Entrypoint
entrypoint:
  middlewares:
    - use: myWhitelist@file

Bypass Rules

yaml
middleware:
  bypass:
    - route myapp & path glob(/api/*)
    - remote 192.168.0.0/16
    - header User-Agent glob(*bot*)

See Rules syntax for complete documentation.

Middleware Reference

Authentication & Security

NamePurpose
oidcOpenID Connect authentication
hcaptchaBot protection with hCaptcha
forward_authExternal auth service delegation

OIDC

OptionDescriptionDefault
allowed_usersAllowed usersGODOXY_OIDC_ALLOWED_USERS
allowed_groupsAllowed groupsGODOXY_OIDC_ALLOWED_GROUPS
client_idClient IDGODOXY_OIDC_CLIENT_ID
client_secretClient secretGODOXY_OIDC_CLIENT_SECRET
scopeOAuth scopeGODOXY_OIDC_SCOPE

hCaptcha

OptionDescriptionDefaultRequired
site_keySite key-Yes
secret_keySecret key-Yes
session_expirySession expiry24hNo

captcha page lightcaptcha page dark

Users solve captcha once per session.

Forward Auth

OptionDescriptionDefault
routeAuth server routetinyauth
auth_endpointAuth endpoint path/api/auth/traefik
headersHeaders to forward["Remote-User", "Remote-Name", "Remote-Email", "Remote-Groups"]

Behavior:

  1. Sends GET request to route origin + auth_endpoint
  2. Populates X-Forwarded-* headers
  3. Non-2xx/3xx: Returns auth response to client
  4. 2xx: Copies headers and forwards upstream
yaml
# Docker labels
proxy.myapp.middlewares.forward_auth: |
  route: tinyauth
  auth_endpoint: /api/auth/traefik
  headers: Remote-User, Remote-Name, Remote-Email, Remote-Groups

IP Resolution

NamePurpose
real_ipResolve client IP from proxy headers
cloudflare_real_ipCloudflare IP resolution for proxied/tunneled traffic

Real IP

Recommended for entrypoint - affects $remote_addr, $remote_host, logs, and CIDRWhitelist.

OptionDescriptionDefaultRequired
headerReal IP headerX-Real-IPNo
fromTrusted CIDRs/IPs-Yes
recursiveRecursive modetrueNo

Recursive Mode:

  • true: First IP not in from list
  • false: Last IP not in from list

Example: X-Forwarded-For: 1.2.3.4, 192.168.0.123, 10.0.0.123, from: 192.168.0.0/16, 10.0.0.1

RecursiveResult
true1.2.3.4
false10.0.0.123

Cloudflare Real IP

Preset values:

  • header: CF-Connecting-IP
  • from: Cloudflare IPs + all local IPs
  • recursive: true

Access Control

NamePurpose
cidr_whitelistAllow/deny requests by IP range
rate_limitLimit request rate per client

CIDR Whitelist

See Request-level access control for documentation.

Rate Limiter

OptionDescriptionDefaultRequired
averageRequests per period-Yes
burstMax requests in period-Yes
periodsTime period format1sNo
yaml
rate_limit:
  average: 10
  burst: 20
  periods: 1m

Traffic Control

NamePurpose
redirect_httpRedirect HTTP to HTTPS
custom_error_pagesCustomize error responses

Redirect HTTP

No configuration options.

Custom Error Pages

See Custom Error Pages for documentation.

Request/Response Modification

NamePurpose
modify_request / requestModify request headers and paths
modify_response / responseModify response headers
hide_x_forwardedRemove X-Forwarded-* headers
set_x_forwardedOverride X-Forwarded-* headers

Modify Request/Response

OptionDescriptionDefault
set_headersReplace headers-
add_headersAdd headers-
hide_headersRemove headers-
add_prefixPath prefix-

Supported Variables

Use $ prefix in YAML files, $$ in Docker Compose.

See Variables Reference down below.

Header Modification

yaml
# Set headers (replace)
proxy.myapp.middlewares.request.set_headers: |
  X-Custom: value1, value2
  X-Real-IP: $$remote_host

# Add headers (append)
proxy.myapp.middlewares.request.add_headers: |
  X-Custom: value1, value2

# Hide headers (remove)
proxy.myapp.middlewares.request.hide_headers: X-Real-IP, X-Forwarded-For

X-Forwarded Headers

yaml
# Hide X-Forwarded headers
proxy.myapp.middlewares.hide_x_forwarded:

# Set X-Forwarded headers (override instead of append)
proxy.myapp.middlewares.set_x_forwarded:

Content Modification

NamePurpose
modify_htmlInject/replace HTML via CSS selectors
themedInject theme CSS into HTML

Modify HTML

OptionDescriptionDefault
targetCSS selector-
htmlHTML to inject-
replaceReplace modefalse

Behavior:

  • Only processes text/html and application/xhtml+xml
  • Append mode: modifies first matching element
  • Replace mode: replaces all matching elements
  • Gracefully handles errors

Supported Selectors: element (body), ID (#main), class (.container), attribute ([data-test='val'])

yaml
# Inject CSS into head
proxy.myapp.middlewares.modify_html: |
  target: head
  html: '<style>body { background: red; }</style>'

# Replace main content
proxy.myapp.middlewares.modify_html: |
  target: main
  html: '<section><h2>New</h2></section>'
  replace: true

Themed

OptionDescriptionConflicts
themePreset themecss
font_urlFont URL-
font_familyFont name-
cssCustom CSStheme

Themes: dark, dark-grey, solarized-dark

yaml
app:
  middlewares:
    themed:
      theme: dark
      font_url: https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap
      font_family: Inter

Variables Reference

CategoryVariableDescription
Requestreq_methodHTTP method
req_schemeURL scheme
req_hostHost without port
req_portPort number
req_addrHost:port
req_pathURL path
req_queryQuery string
req_urlFull URL
req_uriEncoded path?query
req_content_typeContent-Type header
req_content_lengthRequest body length
Clientremote_addrClient IP
remote_hostClient IP (parsed)
remote_portClient port
Responseresp_content_typeResponse Content-Type
resp_content_lengthResponse body length
status_codeHTTP status
Upstreamupstream_nameServer alias
upstream_schemeServer scheme
upstream_hostServer host
upstream_portServer port
upstream_addrServer address:port
upstream_urlFull server URL
Dynamicheader(name)Get header value
resp_header(name)Get response header
arg(name)Get query param

Examples

yaml
# Global middleware (config.yml)
entrypoint:
  middlewares:
    - use: cidr_whitelist
      allow:
        - 127.0.0.1
        - 10.0.0.0/16
    - use: modify_request
      set_headers:
        Host: api.openai.com
yaml
# Per-route (Docker labels)
proxy.#1.middlewares.cidr_whitelist.allow: |
  - 127.0.0.1
  - 10.0.0.0/16
proxy.#1.middlewares.oidc.bypass: |
  path glob(/identity/*) | path glob(/api/*) | path glob(/icons/*)
yaml
# OIDC with bypass (entrypoint)
entrypoint:
  middlewares:
    - use: oidc
      bypass:
        - route pocket-id
        - route immich & glob(path /api/*)
        - remote 127.0.0.1
        - remote 192.168.0.0/16

Released under the MIT License.