# Methods list which effectively expands to all HTTP methods
methods:
- ALL
Regular Rule
Regular rules allow definition and as such execution of arbitrary logic required by your upstream service. This page describes the available configuration options for a regular rule in detail.
In the simplest case, a regular rule reuses mechanisms from the previously defined catalogue in its pipelines. In more complex scenarios, a rule can reconfigure parts of the mechanisms being used. The specific parts that can be reconfigured or overridden depend on the mechanism itself and are described in the mechanism-specific documentation. Reconfiguration is always limited to the particular rule’s pipeline and does not affect other rules.
Configuration
A single regular rule consists of the following properties:
id
: string (mandatory)The unique identifier of the rule. It must be unique across all rules loaded by the same Rule Provider. To ensure uniqueness, it’s recommended to include the upstream service’s name and the rule’s purpose in the id. For example,
rule:my-service:public-api
.match
: RuleMatcher (mandatory)Defines the matching criteria for a rule, with the following properties:
routes
: RouteMatcher array (mandatory)Specifies route conditions for matching the rule to incoming HTTP requests with each entry having the following properties:
path
: string (mandatory)The Path Expression describing the request path this rule should match. It supports both simple and free (named) wildcards.
path_params
: PathParameterConditions (optional)Additional conditions which must be met for the values captured by named wildcards in the path expression. All defined conditions must succeed for the request path to be considered a match. Each entry supports the following properties:
name
: string (mandatory)The name of the wildcard.
type
: string (mandatory)The type of expression used to match the captured wildcard’s value. The supported types are:
glob
: to use a glob expression to match the captured value (/
is used as a delimiter, so*
matches anything until the next/
).regex
to use a regular expression to match the captured value.
value
: string (mandatory)The actual expression based on the given
type
.
backtracking_enabled
: boolean (optional)Enables or disables backtracking when a request matches the path expressions but fails to meet additional matching criteria, like
path_params
,hosts
, etc. This setting is inherited from the default rule and defaults to that rule’s setting. When enabled, the system will backtrack to attempt a match with a less specific rule (see Rule Matching Specificity & Backtracking for more details).hosts
: HostMatcher array (optional)Defines a set of hosts to match against the HTTP
Host
header. These conditions are "OR" conditions, meaning that at least one must match for a successful match. Each entry has the following properties:type
: string (mandatory)Specifies the type of expression for matching the host, which can be one of:
exact
to match the host exactlyglob
to use a glob expression which should be satisfied by the host of the incoming request (.
is used as a delimiter, which means*
will match anything until the next.
).regex
to use a regular expression which should be satisfied by the host of the incoming request.
value
: string (mandatory)The actual host expression based on the
type
.
scheme
: string (optional)The expected HTTP scheme. If not specified, both http and https are accepted.
methods
: string array (optional)Specifies the allowed HTTP methods (
GET
,POST
,PATCH
, etc). If not specified, all methods are allowed. To allow all methods except specific ones, useALL
and prefix the methods to exclude with!
. For example:# Methods list consisting of all HTTP methods without `TRACE` and `OPTIONS` methods: - ALL - "!TRACE" - "!OPTIONS"
allow_encoded_slashes
: string (optional)Controls how to handle URL-encoded slashes in request paths during matching and forwarding. Options include:
off
- Reject requests with encoded slashes (%2F
). This is the default behavior.on
- Accept requests with encoded slashes decoding them to/
.no_decode
- Accept requests with encoded slashes without touching them.
Handling URL-encoded slashes may differ across the proxies in front of heimdall, heimdall, and the upstream service. Accepting requests with encoded slashes could, depending on your rules, lead to Interpretation Conflict vulnerabilities resulting in privilege escalations. forward_to
: RequestForwarder (mandatory in Proxy operation mode)Defines the destination for proxied requests when heimdall operates in proxy mode. The following properties are supported:
host
: string (mandatory)Specifies the host (and port) to which the request should be forwarded. If no
rewrite
property (see below) is defined, the original URL’s scheme, path, and other components remain unchanged. For example, if the original request ishttps://mydomain.com/api/v1/something?foo=bar&bar=baz
and this property is set tomy-backend:8080
, the forwarded request will be sent tohttps://my-backend:8080/api/v1/something?foo=bar&bar=baz
.forward_host_header
: boolean (optional)Controls whether the
Host
header is forwarded to the upstream. Defaults totrue
.Note: If a header finalizer sets the
Host
header in theexecute
pipeline, its value takes precedence over this setting.rewrite
: OriginalURLRewriter (optional)Allows modifying additional parts of the original URL before forwarding the request. If set, at least one of the following supported (middleware) properties must be defined:
scheme
: string (optional)Specifies the URL scheme to use when forwarding the request. Defaults to the scheme of the original request.
Unless heimdall is started with the --insecure-skip-upstream-tls-enforcement
flag, onlyhttps
is allowed as the scheme.strip_path_prefix
: string (optional)This middleware strips the specified prefix from the original URL path before forwarding. E.g. if the path of the original url is
/api/v1/something
and the value of this property is set to/api/v1
, the request to the upstream will have the url path set to/something
.add_path_prefix
: string (optional)This middleware is applied after the execution of the
strip_path_prefix
middleware described above. If specified, heimdall will add the specified path prefix to the path used to forward the request to the upstream service. E.g. if the path of the original URL or the path resulting after the application of thestrip_path_prefix
middleware is/something
and the value of this property is set to/my-backend
, the request to the upstream will have the URL path set to/my-backend/something
.strip_query_parameters
: string array (optional)Removes specified query parameters from the original URL before forwarding. E.g. if the query parameters part of the original URL is
foo=bar&bar=baz
and the value of this property is set to["foo"]
, the query part of the request to the upstream will be set tobar=baz
execute
: Authentication & Authorization Pipeline (mandatory)Specifies the mechanisms used for authentication, authorization, contextualization, and finalization.
on_error
: Error Pipeline (optional)Specifies error handling mechanisms if the pipeline defined by the
execute
property fails. Defaults to the error pipeline defined in the default rule if not specified.
id: rule:foo:bar
match:
routes:
- path: /some/:identifier/followed/by/**
path_params:
- name: identifier
type: glob
value: "[a-z]"
scheme: https
hosts:
- type: exact
value: my-service.local
methods:
- GET
- POST
forward_to:
host: backend-a:8080
rewrite:
scheme: https
strip_path_prefix: /api/v1
execute:
# the following just demonstrates how to make use of specific
# mechanisms in the simplest possible form
- authenticator: foo
- authorizer: bar
- contextualizer: foo
- finalizer: zab
on_error:
- error_handler: foobar
Path Expression
Path expressions are used to match the incoming requests. When specifying these, you can make use of two types of wildcards:
free wildcard, which can be defined using
*
andsingle wildcard, which can be defined using
:
Both can be named and unnamed, with named wildcards allowing accessing of the matched segments in the pipeline of the rule using the defined name as a key on the Request.URL.Captures
object. Unnamed free wildcard is defined as **
and unnamed single wildcard is defined as :*
. A named wildcard uses some identifier instead of the *
, so like *name
for free wildcard and :name
for single wildcard.
The value of the path segment, respectively path segments available via the wildcard name is decoded. E.g. if you define the to be matched path in a rule as /file/:name
, and the actual path of the request is /file/%5Bid%5D
, you’ll get [id]
when accessing the captured path segment via the name
key. Not every path encoded value is decoded though. Decoding of encoded slashes happens only if allow_encoded_slashes
was set to on
.
There are some simple rules, which must be followed while using wildcards:
One can use as many single wildcards, as needed in any segment
A segment must start with
:
or*
to define a wildcardNo segments are allowed after a free (named) wildcard
If a regular segment must start with
:
or*
, but should not be considered as a wildcard, it must be escaped with\
.
Here some path examples:
/apples/and/bananas
- Matches exactly the given path/apples/and/:something
- Matches/apples/and/bananas
,/apples/and/oranges
and alike, but not/apples/and/bananas/andmore
or/apples/or/bananas
. Since a named single wildcard is used, the actual value of the path segment matched by:something
can be accessed in the rule pipeline usingsomething
as a key./apples/:junction/:something
- Similar to above. But will also match/apples/or/bananas
in addition to/apples/and/bananas
and/apples/and/oranges
./apples/and/some:thing
- Matches exactly/apples/and/some:thing
/apples/and/some**
- Matches exactly/apples/and/some**
/apples/**
- Matches any path starting with/apples/
, like/apples/and/bananas
but not/apples/
./apples/*remainingpath
- Same as above, but uses a named free wildcard/apples/**/bananas
- Is invalid, as there is a path segment after a free wildcard/apples/\*remainingpath
- Matches exactly/apples/*remainingpath
Here is an example demonstrating the usage of a single named wildcard:
id: rule:1
match:
routes:
- path: /files/:uuid/delete
hosts:
- type: exact
value: hosty.mchostface
execute:
- authorizer: openfga_check
config:
payload: |
{
"user": "{{ .Subject.ID }}",
"relation": "can_delete",
"object": "file:{{ .Request.URL.Captures.uuid }}"
}
Rule Matching Specificity & Backtracking
The implementation ensures that rules with more specific path expressions are matched first, regardless of their placement within a rule set. In fact, more specific rules are prioritized even when they are defined across different rule sets.
When a path expression matches a request, any additional conditions specified in the rule’s matching criteria are evaluated. Only if these conditions are met will the rule’s pipeline be executed.
If multiple rules share the same path expression and all their additional conditions match, the first matching rule will be applied. The matching order is determined by the sequence of rules in the rule set. |
If no rule is matched, and backtracking is enabled, the process will backtrack to attempt a match with the next less specific rule. Backtracking will stop when:
a less specific rule successfully matches (including evaluation of any additional conditions), or
a less specific rule fails to match and does not permit backtracking.
The following examples illustrate these principles:
Imagine the following set of rules
id: rule1
match:
routes:
- path: /files/**
execute:
- <pipeline definition>
id: rule2
match:
routes:
- path: /files/:team/:name
path_params:
- name: team
type: regex
value: "(team1|team2)"
backtracking_enabled: true
execute:
- <pipeline definition>
id: rule3
match:
routes:
- path: /files/team3/:name
execute:
- <pipeline definition>
The request to /files/team1/document.pdf
will be matched by rule2
, as it is more specific than rule1
. Consequently, the pipeline for rule2
will be executed.
The request to /files/team3/document.pdf
will be matched by rule3
, which is more specific than both rule1
and rule2
. As a result, the corresponding pipeline will be executed.
However, even though the request to /files/team4/document.pdf
matches the path defined in rule2
, the regular expression (team1|team2)
used in the path_params
for the team
parameter will not match. Since backtracking_enabled
is set to true
, backtracking will occur, and the request will be matched by rule1
, with its pipeline then being executed.
Authentication & Authorization Pipeline
As described in the Concepts section, this pipeline consists of mechanisms, previously configured in the mechanisms catalogue, organized in stages as described below, with authentication stage (consisting of authenticators) being mandatory.
Authentication Stage: List of authenticator references, each using authenticator as the key, followed by the required authenticator id. Regardless of their order in the pipeline, each authenticator serves as a fallback for the preceding one if it fails.
Some authenticators rely on the same sources to obtain the subject authentication object. For example, both the
jwt
andoauth2_introspection
authenticators retrieve tokens from theAuthorization
header by default. When using such authenticators within the same pipeline, it’s best to configure the more specific ones before the more general ones to optimize performance. In this case, thejwt
authenticator is more specific since it only processes tokens in JWT format. In contrast, theoauth2_introspection
authenticator is more general - it doesn’t depend on the token format and will attempt to handle any request containing a bearer token.Authorization Stage: List of contextualizer and authorizer references in any order (optional). Can also be mixed. As with authenticators, the list definition happens using either
contextualizer
orauthorizer
as key, followed by the requiredid
. All mechanisms in this list are executed in the order, they are defined. If any of these fails, the entire pipeline fails, which leads to the execution of the error pipeline. This list is optional.Finalization Stage: List of finalizer references using
finalizers
as key, followed by the required finalizerid
. All finalizers in this list are executed in the order they are defined. If any of these fail, the entire pipeline fails, which leads to the execution of the error pipeline. This list is optional. If a default rule is configured, and nofinalizers
are configured on a specific rule level, thefinalizers
from the default rule are used. If the default rule does not have anyfinalizers
configured either, no finalization will take place.
In all cases, the used mechanism can be partially reconfigured if supported by the corresponding type. Configuration goes into the config
properties. These reconfigurations are always local to the given rule. With other words, you can adjust your rule specific pipeline as you want without any side effects.
Execution of an contextualizer
, authorizer
, or finalizer
mechanisms can optionally happen conditionally by making use of a CEL expression in an if
clause, which has access to the Subject
and the Request
objects. If the if
clause is not present, the corresponding mechanism is always executed.
# list of authenticators
# defining the authentication stage
- authenticator: foo
- authenticator: bar
config:
subject: anon
# ... any further required authenticator
# list of authorizers and contextualizers in any order
# defining the authentication stage
- contextualizer: baz
config:
cache_ttl: 0s
- authorizer: zab
- contextualizer: foo
if: Subject.ID != "anonymous"
- contextualizer: bar
- authorizer: foo
if: Request.Method == "POST"
config:
expressions:
- expression: |
// some expression logic deviating from the
// definition in the pipeline configuration.
# ... any further required authorizer or contextualizer
# list of finalizers
# defining the finalization stage
- finalizer: foo
- finalizer: bar
config:
headers:
- X-User-ID: {{ quote .ID }}
# ... any further required finalizers
This example uses
two authenticators, with authenticator named
bar
being the fallback for the authenticator namedfoo
. This fallback authenticator is obviously of type anonymous as it reconfigures the referenced prototype to useanon
for subject id.multiple contextualizers and authorizers, with first contextualizer having its cache disabled (
cache_ttl
set to 0s) and the last authorizer being of type cel as it reconfigures the referenced prototype to use a different authorization expression.two finalizers, with the second one being obviously of type header, as it defines a
X-User-ID
header set to the value of the subject id to be forwarded to the upstream service.contextualizer
foo
is only executed if the authenticated subject is not anonymous.authorizer
foo
is only executed if the request method is HTTP POST.
Error Pipeline
Compared to the Authentication & Authorization Pipeline, the error pipeline is pretty simple. It is also a list of mechanism references, but all referenced types are error handler types. Thus, each entry in this list must have error_handler
as key, followed by the ìd
of the required error handler previously defined in the mechanism catalogue.
Execution of the error handlers should happen conditionally by making use of a CEL expression in an if
clause, which has access to the Error
and the Request
objects. Otherwise, the first error handler will be executed and the error pipeline will exit.
As with the authentication & authorization pipeline, partial reconfiguration of the used mechanisms is possible if supported by the corresponding type. The overrides are always local to the given rule as well.
- error_handler: foo
if: # rule specific condition
- error_handler: bar
config:
# rule specific config
This example uses two error handlers, named foo
and bar
. bar
will only be executed if foo
's error condition does not match. bar
does also override the error handler configuration as required by the given rule.
Last updated on Mar 5, 2025