Open-Source Wikis

/

Caddy

/

Systems

/

Replacer

caddyserver/caddy

Replacer

caddy.Replacer is the placeholder engine. It's how almost every config field becomes runtime-aware — {http.request.uri.path}, {env.HOME}, {file.api_key} all run through it.

Purpose

Resolve {token} placeholders in strings to live values. Plug-in providers contribute new placeholders without changing the core. The same Replacer instance threads through provisioning and request handling, so a value like tls {env.MY_DOMAIN} works.

Directory layout

Path Role
replacer.go The core Replacer: provider chain, static map, format helpers (~12 KB)
modules/caddyhttp/replacer.go HTTP-specific placeholder providers (~19 KB)
replacer_fuzz.go Fuzz harness for the parser

Key abstractions

Type Where Description
Replacer replacer.go Holds a list of replacementProviders + a static map[string]any
replacementProvider replacer.go Internal interface: replace(key string) (any, bool)
globalDefaultReplacementProvider replacer.go Built-in placeholders: system.*, time.*, host.*, random.*
HTTP request placeholder funcs modules/caddyhttp/replacer.go http.request.*, http.response.*, tls.client.*

How it works

graph LR
    Caller -->|"ReplaceAll(input, '')"| Replacer
    Replacer --> Static["static map<br/>(set via Set)"]
    Replacer --> Providers
    Providers --> Default[globalDefaultReplacementProvider]
    Providers --> ContextProv[per-request providers]
    Static -->|first hit wins| Out[output string]
    Providers -->|fallback| Out

A new replacer comes from caddy.NewReplacer(). Modules append providers with rep.Map(func(key string) (any, bool)) for context-specific lookups (e.g. the HTTP server appends a per-request provider that knows how to read request headers).

The static map is populated with rep.Set(key, value). It takes precedence over providers, so a route can override http.request.uri for downstream handlers.

ReplaceAll(input, empty) walks input, finds {...} tokens (with default value support {key:fallback}), and substitutes. Unknown tokens are replaced with empty if non-empty, otherwise left alone.

Variants: ReplaceKnown only substitutes recognized tokens, ReplaceFunc lets you intercept each substitution.

Built-in tokens (selection)

From replacer.go's globalDefaultReplacementProvider:

  • {system.hostname}, {system.slash}, {system.os}, {system.arch}, {system.wd}
  • {time.now}, {time.now.unix}, {time.now.year}, {time.now.iso_local}, {time.now.common_log}
  • {host.ip} — interface IPs.
  • {env.NAME} — environment variables.
  • {file.path} — read a file's contents (with size cap; useful for secrets).

From modules/caddyhttp/replacer.go (per-request provider):

  • {http.request.host}, {http.request.method}, {http.request.uri}, {http.request.uri.path}, {http.request.uri.path.<n>}, {http.request.uri.query}
  • {http.request.header.<name>}, {http.request.cookie.<name>}
  • {http.request.remote.host}, {http.request.remote.port}, {http.request.client_ip}
  • {http.request.body}, {http.request.body_base64} — debugging only
  • {http.request.tls.version}, {http.request.tls.client.subject}, etc.
  • {http.response.status}, {http.response.header.<name>}
  • {http.error.status_code}, {http.error.message} — inside error routes
  • {http.matchers.<name>...} — values captured by named matchers

Integration points

  • HTTP server: Server.ServeHTTP (server.go) creates a per-request replacer, attaches the HTTP provider, and stores it in the request context. Handlers read it via caddy.NewReplacer lookups or caddyhttp.GetVar.
  • Caddyfile adapter: placeholders are passed through verbatim. They aren't resolved until provisioning or runtime.
  • Modules' Provision: any module that wants to support placeholders in its config calls repl.ReplaceAll on its values inside Provision. Examples: modules/caddyhttp/headers/headers.go, modules/caddyhttp/reverseproxy/reverseproxy.go.

Entry points for modification

  • Add a global token? Extend globalDefaultReplacementProvider.replace in replacer.go.
  • Add an HTTP token? Edit modules/caddyhttp/replacer.go's per-request provider.
  • Make a module's config placeholder-aware? Call repl.ReplaceAll(field, "") on the field during Provision.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Replacer – Caddy wiki | Factory