Skip to main content
Request matchers determine which requests a route will handle. A route can have multiple matcher sets, and each set can contain multiple matchers. Matchers within a set are AND’ed together, while matcher sets are OR’ed.

Matcher Interface

Matchers implement the RequestMatcherWithError interface:
type RequestMatcherWithError interface {
    MatchWithError(*http.Request) (bool, error)
}
Matchers MUST NOT modify the request, with the only exception being its context.

Matcher Sets

A matcher set is a group of matchers that must all match (AND logic):
{
  "match": [
    {
      "host": ["example.com"],
      "path": ["/api/*"]
    }
  ]
}
Multiple matcher sets are OR’ed together:
{
  "match": [
    {"host": ["example.com"]},
    {"path": ["/public/*"]}
  ]
}
This matches requests to example.com OR requests to /public/* on any host.

Host Matcher

Module ID: http.matchers.host
host
array of strings
Matches requests by the Host header value (case-insensitive).When used in a top-level HTTP route, qualifying domain names may trigger automatic HTTPS, which automatically provisions and renews certificates.Wildcards (*) may be used to represent exactly one label of the hostname:
  • * matches localhost or internal but not example.com
  • *.example.com matches foo.example.com but not foo.bar.example.com
{
  "match": [{
    "host": ["example.com", "*.example.com"]
  }]
}

Path Matcher

Module ID: http.matchers.path
path
array of strings
Case-insensitive exact path matching (not prefix-based). Wildcards (*) supported:
  • /prefix/* - Prefix match
  • *.suffix - Suffix match
  • */contains/* - Substring match
  • /accounts/*/info - Globular match
Path matching is done in unescaped space. Escape sequences in patterns are compared with the request’s raw/escaped path.
{
  "match": [{
    "path": ["/api/*", "*.json"]
  }]
}

Path Regexp Matcher

Module ID: http.matchers.path_regexp
path_regexp
object
Matches requests using a regular expression on the URI path.
name
string
Optional unique name for this regexp. Used in placeholder names.
pattern
string
The regular expression in RE2 syntax.
Upon match, adds placeholders:
  • {http.regexp.name.capture_group}
  • {http.regexp.capture_group} (if no name)
{
  "match": [{
    "path_regexp": {
      "name": "api_version",
      "pattern": "^/api/v([0-9]+)/"
    }
  }]
}

Method Matcher

Module ID: http.matchers.method
method
array of strings
Matches requests by HTTP method (GET, POST, PUT, DELETE, etc.).
{
  "match": [{
    "method": ["GET", "POST"]
  }]
}

Query Matcher

Module ID: http.matchers.query
query
object
Matches requests by URI query string. Keys are exact, values support wildcards.
{
  "query": {
    "key": ["value"],
    "topic": ["api"],
    "debug": ["*"]
  }
}
Query string values are arrays because repeated keys are valid. The matcher succeeds if any configured value matches.

Header Matcher

Module ID: http.matchers.header
header
object
Matches requests by header fields. Performs fast, exact string comparisons. Wildcards supported for prefix/suffix/substring matching.
  • null value - header must not exist
  • Empty array - header must exist (any value)
  • Array of values - match if any value matches
{
  "header": {
    "Content-Type": ["application/json"],
    "X-Custom-*": ["*"]
  }
}

Header Regexp Matcher

Module ID: http.matchers.header_regexp
header_regexp
object
Matches requests using regular expressions on header fields.Keys are header field names, values are MatchRegexp objects with name and pattern fields.Adds placeholders like {http.regexp.name.capture_group}.

Protocol Matcher

Module ID: http.matchers.protocol
protocol
string
Matches requests by protocol. Recognized values:
  • http - HTTP (not HTTPS)
  • https - HTTPS
  • grpc - gRPC (Content-Type: application/grpc)
  • http/1, http/1.1 - Specific HTTP versions
  • http/2, http/3 - HTTP/2 or HTTP/3
  • http/2+ - HTTP/2 or later

TLS Matcher

Module ID: http.matchers.tls
tls
object
Matches HTTP requests based on the underlying TLS connection state.
handshake_complete
boolean
Matches if the TLS handshake has completed. QUIC 0-RTT early data may arrive before handshake completion.
If this matcher is specified but the request didn’t come over TLS, it will never match.

Not Matcher

Module ID: http.matchers.not
not
array
Matches requests by negating the results of its matcher sets. Each matcher set is OR’ed.Structure is an array of matcher set objects:
{
  "not": [
    {"path": ["/admin/*"]},
    {"remote_ip": ["192.168.0.0/16"]}
  ]
}
This matches if the path is NOT /admin/* AND the IP is NOT in 192.168.0.0/16.

Remote IP Matcher

Module ID: http.matchers.remote_ip Matches requests by the client’s IP address. Supports CIDR notation and IP ranges.

File Matcher

Module ID: http.matchers.file Matches requests if the requested file exists on the filesystem.

Expression Matcher

Module ID: http.matchers.expression Matches requests using CEL (Common Expression Language) expressions. Provides access to all request properties and Caddy placeholders.
@complex expression {path}.startsWith('/api') && {method} in ['GET', 'POST']

Configuration Examples

Multiple Matchers (AND)

{
  "match": [{
    "host": ["example.com"],
    "path": ["/api/*"],
    "method": ["GET", "POST"]
  }]
}
Matches only if ALL conditions are true.

Multiple Matcher Sets (OR)

{
  "match": [
    {"host": ["example.com"]},
    {"path": ["/public/*"]},
    {"remote_ip": ["10.0.0.0/8"]}
  ]
}
Matches if ANY condition is true.

Complex Matching

{
  "match": [
    {
      "host": ["api.example.com"],
      "path": ["/v1/*"],
      "not": [{"path": ["/v1/health"]}]
    }
  ]
}
Matches api.example.com/v1/* but NOT /v1/health.
For large host lists (>100), Caddy optimizes matching using binary search for exact matches and linear search for wildcards.

Build docs developers (and LLMs) love