Skip to content

Middlewares

Usage

Middlewares can be used in several ways:

  • Entrypoint (ordered)
  • Middleware Compose (ordered) - reusable middleware configuration YAML files under config/middlewares
  • Docker labels (unordered)
  • Route files (unordered)

For unordered middlewares, you have to set priority manually when order matters

yaml
services:
  app:
    ...
    labels:
      proxy.aliases: myapp
      proxy.myapp.middlewares.redirectHTTP.priority: 1
      proxy.myapp.middlewares.cidrWhiteList.priority: 2
      proxy.myapp.middlewares.cidrWhiteList.allow: 127.0.0.1, 10.0.0.0/16

Middleware name and properties are case-insensitive and accept snake_case, PascalCase or camelCase

redirectHTTP, redirect_http and RedirectHttp are equivalent.

Syntax

Middleware Compose and Entrypoint Middleware

yaml
# middleware compose / entrypoint middleware syntax
<name>:
  - use: {middleware}
    {option1}: {value1}
    {option2}: {value2}
    ...

# middleware compose
# config/middlewares/whitelist.yml
myWhitelist:
  - use: CloudflareRealIP
  - use: CIDRWhitelist
    allow:
      - 127.0.0.1
      - 223.0.0.0/8

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

Docker Labels and Route Files

yaml
# docker labels
proxy.#1.middlewares.{middlewareName}.{optionName}: { value }
proxy.#1.middlewares.{middlewareName}.{optionName2}: { value2 }

# docker labels (yaml style)
proxy.#1.middlewares.{middlewareName}: |
  {optionName}: {value}
  {optionName2}: {value2}

# route file
myapp:
  middlewares:
    { middlewareName }:
      { optionName }: { value }
      { optionName2 }: { value2 }

Reusing Middleware Compose Objects

yaml
# docker compose
services:
  app:
    labels:
      proxy.#1.middlewares.myWhitelist@file:

# route file
myapp:
  middlewares:
    myWhitelist@file:

# entrypoint in config.yml
entrypoint:
  middlewares:
    - use: myWhitelist@file

Middleware Bypass Rules

Bypass middlewares with a list of rules. Matching any of them will bypass the middleware.

See Rules syntax

yaml
entrypoint:
  middlewares:
    - use: oidc
      bypass:
        - route immich # bypass for immich route
        - route public_service & path / # bypass for route "public_service" and root path "/"
        - remote 127.0.0.1 # bypass for localhost
        - remote 192.168.0.0/16 # bypass for 192.168.0.0/16 ip range
        - remote 10.0.0.0/8 # bypass for 10.0.0.0/8 ip range
        - remote 172.16.0.0/12 # bypass for 172.16.0.0/12 ip range
        - method HEAD # bypass for HEAD method
        - header Authorization "Bearer 1234567890" # bypass for auth header with specific token
        - query access_code "1234567890" # bypass for access_code query with specific value
        - cookie session "1234567890" # bypass for session cookie with specific value

Examples

Syntax Examples

yaml
# config.yml
entrypoint:
  middlewares:
    - use: cidr_whitelist
      allow:
        - 127.0.0.1
        - 10.0.0.0/16

# docker labels (yaml style)
proxy.#1.middlewares.cidr_whitelist.allow: |
  - 127.0.0.1
  - 10.0.0.0/16

# single line comma separated
proxy.#1.middlewares.cidr_whitelist.allow: 127.0.0.1, 10.0.0.0/16

# route file
openai:
  host: https://api.openai.com/
  middlewares:
    cidr_whitelist:
      allow:
        - 127.0.0.1
        - 10.0.0.0/16
    modify_request:
      set_headers:
        Host: api.openai.com
  homepage:
    show: false

Middleware with Bypass Rules

WARNING

Global OIDC is hard to be configured correctly.

yaml
# config.yml (global middlewares)
entrypoint:
  middlewares:
    - use: oidc
      bypass:
        - route pocket-id # bypass the IdP route
        - route immich & path /api/* # this allows immich route to bypass oidc (to use with its own oidc implementation)
        - route karakeep & path /api/v1/* # this allows karakeep mobile app (or any api requests) to bypass oidc
        # bypass local network
        - remote 127.0.0.1
        - remote 192.168.0.0/16
        - remote 10.0.0.0/8
        - remote 172.16.0.0/12
        - remote 100.64.0.0/10

# docker labels (route specific middlewares) (e.g. vaultwarden)
services:
  vw:
    ...
    labels:
      # this allows bitwarden apps (api requests) to bypass oidc
      proxy.#1.middlewares.oidc.bypass: path /identity/* | path /api/* | path /icons/*
  karakeep:
    ...
    labels:
      proxy.#1.middlewares.oidc.bypass: path /api/v1/*

Available middlewares

OIDC

Name: oidc

OIDC uses the settings from .env file, with configurable overrides.

OptionDescriptionDefaultRequired
allowed_usersOverride allowed usersGODOXY_OIDC_ALLOWED_USERSNo
allowed_groupsOverride allowed groupsGODOXY_OIDC_ALLOWED_GROUPSNo

hCaptcha

Name: hcaptcha

Protects against bots by solving a hCaptcha challenge.

OptionDescriptionDefaultRequired
site_keyhCaptcha site keyYes
secret_keyhCaptcha secret keyYes
session_expirySession expiry time24hNo

Explanation

  • User will see a captcha landing page before accessing the protected route.
  • Once the captcha is solved, the user will be redirected to the real page.
  • Users will need to solve the captcha again after each session_expiry period.

Screenshots

captcha page lightcaptcha page dark

Redirect http

Name: redirect_http

Redirect http requests to https

Custom error pages

See Custom Error Pages

Real IP

Name: real_ip

NOTE

This middleware is used for resolving the correct client IP from real_ip.header (e.g. X-Real-IP)

This affects:

  • $remote_addr and $remote_host
  • IP in the access log
  • CIDRWhitelist Middleware

Recommended to use on entrypoint

OptionDescriptionDefaultRequired
headerReal IP headerX-Real-IPNo
fromList of trusted CIDRsYes
recursiveRecursive modetrueNo
Recursive modeDescription
trueChoose the first IP that does not match the from list
falseChoose the last IP that does not match the from list

Example:

  • X-Forwarded-For: 1.2.3.4, 192.168.0.123, 10.0.0.123
  • from: 192.168.0.0/16
Recursive modeResult
true1.2.3.4
false10.0.0.123
yaml
# entrypoint
entrypoint:
  middlewares:
    - use: real_ip
      header: X-Real-IP
      from:
        - 127.0.0.1
        - 192.168.0.0/16
        - 10.0.0.0/8
      recursive: true

nginx equivalent:

nginx
location / {
  set_real_ip_from 127.0.0.1;
  set_real_ip_from 192.168.0.0/16;
  set_real_ip_from 10.0.0.0/8;

  real_ip_header    X-Real-IP;
  real_ip_recursive on;
}

Cloudflare Real IP

Name: cloudflare_real_ip

NOTE

This middleware requires no additional configuration.

It is a preset of Real IP for Cloudflare Proxy/Tunnels. It will skip all local IPs.

Preset Values

OptionDescription
headerCF-Connecting-IP
fromList of Cloudflare IPs (updated hourly from official sources)
recursivetrue

Trusted IPs:

CIDR Whitelist

Name: cidr_whitelist

See Request Level Access Control

Rate Limiter

Name: rate_limit

OptionDescriptionDefaultRequired
averageAverage requests per periodYes
burstMaximum number of requests allowed in a periodYes
periodsTime period in format number[unit]1sNo

Modify request or response

Names:

  • modify_request or request
  • modify_response or response
OptionDescriptionDefaultRequired
set_headersSet headersNo
add_headersAdd headersNo
hide_headersHide headersNo
add_prefixAdd prefix to pathNo

Supported variables

NOTE

To use variables, add $ ($$ for docker compose) before the variable name.

Single $ in docker compose is treated as environment variables.

VariableDescription
req_methodrequest http method
req_schemerequest URL scheme (http/https)
req_hostrequest host without port
req_portrequest port
req_addrrequest host with port (if present)
req_pathrequest URL path
req_queryraw query string
req_urlfull request URL
req_urirequest URI (encoded path?query)
req_content_typerequest Content Type header
req_content_lengthlength of request body (if present)
remote_addrclient's remote address (may changed by middlewares like RealIP and CloudflareRealIP)
remote_hostclient's remote IP, parsed from remote_addr
remote_portclient's remote port, parsed from remote_addr (may be empty)
resp_content_typeresponse Content Type header
resp_content_lengthlength of the response body
status_coderesponse status code
upstream_nameupstream server name (alias)
upstream_schemeupstream server scheme
upstream_hostupstream server host
upstream_portupstream server port
upstream_addrupstream server address with port (if present)
upstream_urlfull upstream server URL
header(name)get request header by name
resp_header(name)get response header by name
arg(name)get URL query parameter by name

Set headers

yaml
# docker labels
proxy.myapp.middlewares.request.set_headers: |
  X-Custom-Header1: value1, value2
  X-Real-IP: $$remote_host

# route file
myapp:
  middlewares:
    request:
      set_headers:
        X-Custom-Header1: value1, value2
        X-Real-IP: $remote_host

Add headers

yaml
# docker labels
proxy.myapp.middlewares.request.add_headers: |
  X-Custom-Header1: value1, value2
  X-Custom-Header2: value3

# route file
myapp:
  middlewares:
    request:
      add_headers:
        X-Custom-Header1: value1, value2
        X-Custom-Header2: value3

Hide headers

yaml
# docker labels
proxy.myapp.middlewares.request.hide_headers: |
  X-Custom-Header1
  X-Custom-Header2

# route file
myapp:
  middlewares:
    request:
      hide_headers:
        - X-Custom-Header1
        - X-Custom-Header2
nginx equivalents
nginx
location / {
  add_header X-Custom-Header1 value1, value2;
  more_set_headers "X-Custom-Header1: value1, value2";
  more_set_headers "X-Custom-Header2: value3";
  more_clear_headers "X-Custom-Header1";
  more_clear_headers "X-Custom-Header2";
}

X-Forwarded Headers

Hide X-Forwarded Headers

Name: hide_x_forwarded

Remove Forwarded and X-Forwarded-* headers before the request is sent to the upstream service.

yaml
# docker labels
proxy.myapp.middlewares.hide_x_forwarded:

# route file
myapp:
  middlewares:
    hide_x_forwarded:

Set X-Forwarded Headers

Name: set_x_forwarded

Replace existing X-Forwarded-* headers with headers provided by GoProxy.

yaml
# docker labels
proxy.myapp.middlewares.set_x_forwarded:

# route file
myapp:
  middlewares:
    set_x_forwarded:

Released under the MIT License.