Request Matchers
In the Caddyfile, a matcher token immediately following the directive can limit that directive’s scope. The matcher token can be one of these forms:
- to match all requests (wildcard; default).
/path
start with a forward slash to match a request path.@name
to specify a named matcher.
Matcher tokens are . If a matcher token is omitted, it is the same as a wildcard matcher (*
).
Examples
This directive applies to HTTP requests:
And this is the same:
reverse_proxy * localhost:9000
But this directive applies only to requests having a path starting with /api/
:
reverse_proxy /api/* localhost:9000
To match on anything other than a path, define a and refer to it using @name
:
@postfoo {
method POST
path /foo/*
}
reverse_proxy @postfoo localhost:9000
Wildcard matchers
The wildcard (or “catch-all”) matcher *
matches all requests, and is only needed if a matcher token is required. For example, if the first argument you want to give a directive also happens to be a path, it would look exactly like a path matcher! So you can use a wildcard matcher to disambiguate, for example:
root * /home/www/mysite
Otherwise, this matcher is not often used. It is convenient to omit it when possible; just a matter of preference.
Path matchers
Because matching by path is so common, a single path matcher can be inlined, like so:
redir /old.html /new.html
Path matcher tokens must start with a forward slash /
.
Path matching is an exact match by default; you must append a *
for a fast prefix match. Note that /foo*
will match /foo
and /foo/
as well as /foobar
; you might actually want /foo/*
instead.
Named matchers
All matchers that are not comprised of a single path matcher or a wildcard matcher must be named matchers. This is a matcher that is defined outside of any particular directive, and can be reused.
Defining a matcher with a unique name gives you more flexibility, allowing you to combine any available matchers into a set:
@name {
...
}
or, if there is only one matcher in the set:
@name ...
Then you can use the matcher like so: @name
For example:
@websockets {
header Connection *Upgrade*
header Upgrade websocket
}
reverse_proxy @websockets localhost:6001
This proxies only the requests that have a header field named “Connection” containing the word “Upgrade”, and another field named “Upgrade” with a value of “websocket”.
If the matcher set consists of only one matcher, a one-liner syntax also works:
@post method POST
reverse_proxy @post localhost:6001
A named matcher definition constitutes a matcher set. Matchers in a set are AND’ed together; i.e. all must match. For example, if you have both a header
and path
matcher in the set, both must match.
For most matchers that accept multiple values, those values are OR’ed; i.e. one must match in order for the matcher to match.
Standard matchers
Full matcher documentation can be found in each respective matcher module’s docs.
expression
⚠️ This module is still experimental and, as such, may experience breaking changes.
expression <cel...>
By any CEL (Common Expression Language) expression that returns true
or false
.
As a special case, Caddy (or Caddyfile shorthands) may be used in these CEL expressions, as they are preprocessed and converted to regular CEL function calls before being interpreted by the CEL environment.
Examples:
Match requests whose methods start with P
, e.g. PUT
or POST
.
expression {method}.startsWith("P")
Match requests where handler returned error status code 404
, would be used in conjunction with the handle_errors
directive.
expression {http.error.status_code} == 404
By files.
root
defines the directory in which to look for files. Default is the current working directory, or theroot
() if set (can be set via theroot
directive).try_files
checks files in its list that match the try_policy.try_policy
specifies how to choose a file. Default isfirst_exist
.first_exist
checks for file existence. The first file that exists is selected.smallest_size
chooses the file with the smallest size.largest_size
chooses the file with the largest size.most_recent_modified
chooses the file that was most recently modified.
split_path
will cause the path to be split at the first delimiter in the list that is found in each filepath to try. For each split value, the left-hand side of the split including the delimiter itself will be the filepath that is tried. For example,/remote.php/dav/
using a delimiter of.php
would try the file/remote.php
. Each delimiter must appear at the end of a URI path component in order to be used as a split delimiter. This is a niche setting and is mostly used when serving PHP sites.
Because try_files
with a policy of first_exist
is so common, there is a one-line shortcut for that:
file <files...>
An empty file
matcher (one with no files listed after it) will see if the requested file—verbatim from the URI, relative to the —exists.
Since rewriting based on the existence of a file on disk is so common, there is also a try_files
directive which is a shortcut of the file
matcher and a .
Examples:
Match requests where the path is a file that exists.
file
Match requests where the path followed by .html
is a file that exists, or if not, where the path is a file that exists.
try_files {path}.html {path}
}
header
header <field> <value>
By request header fields.
<field>
is the name of the HTTP header field to check.<value>
is the value the field must have to match.- If prefixed with
*
, it performs a fast suffix match. - If suffixed with
*
, it performs a fast prefix match. - If enclosed by
*
, it performs a fast substring match. - Otherwise, it is a fast exact match.
- If prefixed with
Example:
Match requests with the Connection header containing Upgrade
.
header Connection *Upgrade*
header_regexp
header_regexp [<name>] <field> <regexp>
Like header
, but supports regular expressions. Capture groups can be accessed via placeholder like {http.regexp.name.capture_group}
where name
is the name of the regular expression (optional, but recommended) and capture_group
is either the name or number of the capture group in the expression. Capture group 0
is the full regexp match, 1
is the first capture group, 2
is the second capture group, and so on.
Example:
Match requests where the Cookie header contains login_
followed by a hex string, with a capture group that can be accessed with {http.regexp.login.1}
.
header_regexp login Cookie login_([a-f0-9]+)
host
host <hosts...>
Example:
host sub.example.com
method
method <verbs...>
By the method (verb) of the HTTP request. Verbs should be uppercase, like POST
. Can match one or many methods.
Examples:
Match requests with the method.
method GET
Match requests with the PUT
or DELETE
methods.
method PUT DELETE
or, to negate multiple matchers which get AND’ed, open a block:
not {
<any other matchers...>
}
The results of the enclosed matchers will be negated.
Examples:
Match requests with paths that do NOT begin with /css/
OR /js/
.
not path /css/* /js/*
Match requests WITH NEITHER:
- an
/api/
path prefix, NOR - the
POST
request method
i.e. must have none of these to match:
not path /api/*
not method POST
Match requests WITHOUT BOTH:
- an
/api/
path prefix, AND - the
POST
request method
i.e. must have neither or either of these to match:
not {
path /api/*
method POST
}
path
path <paths...>
By request path, meaning the path component of the request’s URI. Path matches are exact, but wildcards *
may be used:
- At the end, for a prefix match (
/prefix/*
) - At the beginning, for a suffix match (
*.suffix
) - On both sides, for a substring match (
*/contains/*
) - In the middle, for a globular match (
/accounts/*/info
)
path_regexp
path_regexp [<name>] <regexp>
Like path
, but supports regular expressions. Capture groups can be accessed via placeholder like {http.regexp.name.capture_group}
where name
is the name of the regular expression (optional, but recommended) and capture_group
is either the name or number of the capture group in the expression. Capture group 0
is the full regexp match, 1
is the first capture group, 2
is the second capture group, and so on.
Example:
Match requests where the path ends a 6 character hex string followed by .css
or .js
as the file extension, with capture groups that can be accessed with {http.regexp.static.1}
and {http.regexp.static.2}
for each part enclosed in ( )
, respectively.
path_regexp static \.([a-f0-9]{6})\.(css|js)$
protocol
protocol http|https|grpc
By request protocol.
query
query <key>=<val>...
By query string parameters. Should be a sequence of key=value
pairs. Keys are matched exactly, case-sensitively. Values are matched exactly, but also support *
to match any value.
Example:
Match requests with a sort
query parameter with the value asc
query sort=asc
By remote (client) IP address. Accepts exact IPs or CIDR ranges.
Example:
Match requests from private IPv4 addresses.