Header Rewrite Plugin

This plugin allows you to modify arbitrary headers based on defined rules, for both requests and responses.

Purpose

Remapping an incoming client request to an origin server is at the heart of what we use Traffic Server for, but often we need to do more to requests than just change their destination according to simple rewriting rules against the URL.

We may need to direct requests to different origins based on a client cookie.

Our origins might return error response codes which are a little too customized and we want to condense the possible values to just the official codes.

We might want to strip a set of internal-use or debugging related HTTP headers from responses before sending them to clients, unless the original request had its own special header indicating that they should be retained.

Or perhaps we want to redirect certain requests differently when they come from a known group of IP addresses (say, our developers’ office network) and we have a file on our proxy caching servers called /var/developertesting. (Stranger QA methods exist.)

Maybe we want to deny access to a resource with an HTTP 403 if the client connected to Traffic Server over a particular port, used HEAD instead of GET, doesn’t list Spanish (Paraguay dialect) in their Accept-Language header, and either the origin server replied with 304 or we randomly generate an integer between 0 and 500 and get back anything greater than 290.

These more complicated transformations of requests and responses (even that last one) are made possible by this plugin.

Installation

This plugin is considered stable and is included with Traffic Server by default. There are no special steps necessary for its installation.

Configuration

Header rewrite configurations, the actual conditions and operations that make up the activity performed by the plugin, are specified in external files and not in-line with the various mapping (and remapping) rules you may have configured for your proxy. The location of this file is arbitrary, as long as the Traffic Server processes have permissions to read it, though you may find it useful to keep it in the same location as your other proxy configuration files.

The paths given to the configuration file(s) may be absolute (leading with a / character), or they may be relative to the Traffic Server configuration directory.

There are two methods for enabling this plugin, based on whether you wish it to operate globally on every request that passes through your proxy, or only on some subset of the requests by enabling it only for specific mapping rules.

Enabling Globally

This plugin may be enabled globally, so that the conditions and header rewriting rules are evaluated for every request made to your Traffic Server instance. This is done by adding the following line to your plugin.config:

header_rewrite.so [--geo-db-path=path/to/geoip.db] config_file_1.conf config_file_2.conf ...

You may specify multiple configuration files. Their rules will be evaluated in the order the files are listed.

The plugin takes an optional switch --geo-db-path. If MaxMindDB support has been compiled in, use this switch to point at your .mmdb file. This also applies to the remap context.

Enabling Per-Mapping

Alternatively, the plugin can be enabled for specific mapping rules, by modifying the relevant entries in your remap.config:

map http://a http://b @plugin=header_rewrite.so @pparam=rules1.conf ...

As with the global method above, multiple configuration files may be listed, each with its own @pparam=<file> and their contents will be evaluated in the order the files were specified.

Each ruleset that is configured per-mapping should have a special Hook Conditions defined. Without a defined hook, these rulesets will use the REMAP_PSEUDO_HOOK.

Rewriting Rules

Header rewriting rules consist of zero or more Conditions followed by one or more Operators. Conditions are used to limit the requests which will be affected by the operator(s). Additionally, both conditions and operators may have flags which modify their behavior.

A complete rule, consisting of two conditions and a single operator might look like the following:

cond %{STATUS} >399 [AND]
cond %{STATUS} <500
set-status 404

Which converts any 4xx HTTP status code from the origin server to a 404. A response from the origin with a status of 200 would be unaffected by this rule.

Conditions

Conditions are used as qualifiers, causing the associated operators to only be evaluated if the condition(s) are met. Conditions all take the following form:

cond %{<condition name>[:<argument>]} <operand> [<flags>]

Every condition begins with the literal string cond to indicate that this line is a condition, not an operator. This is followed by the condition name, inside curly braces and preceded by a percent sign (e.g. %{TRUE} for the condition named TRUE). Some condition names take an argument. Header conditions, for example, take the name of the header in question, and cookie conditions take the name of the cookie. For these, the condition name is followed by a colon and the argument value (e.g. %{HEADER:User-Agent} for a header condition against the User-Agent header).

The operand of a condition provides a value, pattern, or range against which to match. The format is described in Condition Operands below.

Finally, a condition may optionally have various flags associated with it. These are described in Condition Flags below.

The following sections list all of the condition types currently supported. For increased clarity in their usage, the optional [<flags>] portion of the condition is omitted from all of the examples.

ACCESS

cond %{ACCESS:<path>}

Returns true if Traffic Server was able to successfully update the access time on the file at <path>. This condition will return false if the file does not exist or Traffic Server cannot access it for any other reason.

CACHE

cond %{CACHE} <operand>

This condition provides the server’s cache lookup results to inform headers where the requested data was generated from. The cache values are:

Value

Description

none

No cache lookup was attempted.

miss

The object was not found in the cache.

hit-stale

The object was found in the cache, but it was stale.

hit-fresh

The object was fresh in the cache.

skipped

The cache lookup was skipped.

CLIENT-HEADER

cond %{CLIENT-HEADER:<name>} <operand>

Value of the header <name> from the original client request (regardless of the hook context in which the rule is being evaluated). Note that some headers may appear in an HTTP message more than once. In these cases, the value of the header operated on by this condition will be a comma separated string of the values from every occurrence of the header. More details are provided in Repeated Headers below.

CLIENT-URL

cond %{CLIENT-URL:<part>} <operand>

The URL of the original request. Regardless of the hook context in which the rule is evaluated, this condition will always operate on the original, unmapped URL supplied by the client. The <part> may be specified according to the options documented in URL Parts.

Note that the HOST <part> of the CLIENT-URL might not be set until the remap phase of the transaction. This happens when there is no host in the incoming URL and only set as a host header. During the remap phase the host header is copied to the CLIENT-URL. Use CLIENT-HEADER:Host if you are going to match the host.

CIDR

set-header @Client-CIDR %{CIDR:24,48}

This condition takes the client IP, and applies the provided CIDR style masks to the IP, before producing a string. The typical use of this conditions is as above, producing a header that contains a IP representation which has some privacy properties. It can of course also be used as a regular condition, and the output is a string that can be compared against. The two optional arguments are as follows:

IPv4-Mask    Length, in bits, of the IPv4 address to preserve. Default: 24
IPv6-Mask    Length, in bits, of the IPv6 address to preserve. Default: 48

The two arguments, if provided, are comma separated. Valid syntax includes:

%{CIDR}         Defaults to 24,48 (as above)
%{CIDR:16}      IPv4 CIDR mask is 16 bits, IPv6 mask is 48
%{CIDR:18,42}   IPv4 CIDR mask is 18 bits, IPv6 mask is 42 bits

A typical use case is to insert the @-prefixed header as above, and then use this header in a custom log format, rather than logging the full client IP. Another use case could be to make a special condition on a sub-net, e.g.:

cond %{CIDR:8} ="8.0.0.0"
    set-header X-Is-Eight "Yes"
cond %{CIDR:,8} ="fd00::" #note the IPv6 Mask is in the second position
    set-header IPv6Internal "true"

This condition has no requirements other than access to the Client IP, hence, it should work in any and all hooks.

FROM-URL

cond %{FROM-URL:<part>} <operand>

In a remapping context, this condition matches against the source URL from which the remapping was generated. This condition is valid only within configurations provided through remap.config as described in Enabling Per-Mapping above.

The <part> allows the operand to match against just a component of the URL, as documented in URL Parts below.

GEO

cond %{GEO:<part>} <operand>

Perform a GeoIP lookup of the client-IP, using a 3rd party library and DB. Currently the MaxMind GeoIP and MaxMindDB APIs are supported. The default is to do a Country lookup, but the following qualifiers are supported:

%{GEO:COUNTRY}      The country code (e.g. "US")
%{GEO:COUNTRY-ISO}  The country ISO code (e.g. 225)
%{GEO:ASN}          The AS number for the provider network (e.g. 7922)
%{GEO:ASN-NAME}     A descriptive string of the AS provider

These operators can be used both as conditionals, as well as values for setting headers. For example:

cond %{SEND_RESPONSE_HDR_HOOK} [AND]
cond %{GEO:COUNTRY} =US
    set-header ATS-Geo-Country %{GEO:COUNTRY}
    set-header ATS-Geo-Country-ISO %{GEO:COUNTRY-ISO}
    set-header ATS-Geo-ASN %{GEO:ASN}
    set-header ATS-Geo-ASN-NAME %{GEO:ASN-NAME}

HTTP-CNTL

cond %{HTTP-CNTL:<controller>}

This condition allows you to check the state of various HTTP controls. The controls are of the same name as for set-http-cntl. This condition returns a true or false value, depending on the state of the control. For example:

cond %{HTTP-CNTL:LOGGING} [NOT]

would only continue evaluation if logging is turned off.

ID

cond %{ID:REQUEST} >100

This condition provides access to three identifier values that ATS uses internally for things like logging and debugging. Since these are IDs, they are mostly useful as a value (operand) to other operators. The three types of IDs are

%{ID:REQUEST}    A unique, sequence number for the transaction
%{ID:PROCESS}    A UUID string, generated every time ATS restarts
%{ID:UNIQUE}     The combination of the previous two IDs

Now, even though these are conditionals, their primary use are as value arguments to another operator. For example:

set-header ATS-Req-UUID %{ID:UNIQUE}

INBOUND

cond %{INBOUND:TLS} /./

This condition provides access to information about the inbound (client, user agent) connection to ATS. The data that can be checked is

%{INBOUND:LOCAL-ADDR}      The local (ATS) address for the connection. Equivalent to %{IP:INBOUND}.
%{INBOUND:LOCAL-PORT}      The local (ATS) port for the connection.
%{INBOUND:REMOTE-ADDR}     The client address for the connection. Equivalent to %{IP:CLIENT}.
%{INBOUND:REMOTE-PORT}     The client port for the connection.
%{INBOUND:TLS}             The TLS protocol if the connection is over TLS, otherwise the empty string.
%{INBOUND:H2}              The string "h2" if the connection is HTTP/2, otherwise the empty string.
%{INBOUND:IPV4}            The string "ipv4" if the connection is IPv4, otherwise the empty string.
%{INBOUND:IPV6}            The string "ipv6" if the connection is IPv6, otherwise the empty string.
%{INBOUND:IP-FAMILY}       The IP family, either "ipv4" or "ipv6".
%{INBOUND:STACK}           The full protocol stack separated by ','.

All of the tags generated by this condition are from the protocol stack tags.

Because of the implicit match rules, using these as conditions is a bit unexpected. The condition listed above will be true if the inbound connection is over TLS because %{INBOUND:TLS} will only return a non-empty string for TLS connections. In contrast

cond %{INBOUND:TLS}

will be true for non-TLS connections because it will be true when %{INBOUND:TLS} is the empty string. This happens because the default matching is equality and the default value the empty string. Therefore the condition is treated as if it were

cond %{INBOUND:TLS}=""

which is true when the connection is not TLS. The arguments H2, IPV4, and IPV6 work the same way.

As a special matcher, the inbound IP addresses can be matched against a list of IP ranges, e.g.

cond %{INBOUND:REMOTE-ADDR} {192.168.201.0/24,10.0.0.0/8}

Note that this will not work against the non-IP based conditions, such as the protocol families, and the configuration parser will error out. The format here is very specific, in particular no white spaces are allowed between the ranges.

IP

cond %{IP:<part>} <operand>

This is one of four possible IPs associated with the transaction, with the possible parts being

%{IP:CLIENT}     Client's IP address. Equivalent to %{INBOUND:REMOTE-ADDR}.
%{IP:INBOUND}    ATS's IP address to which the client connected. Equivalent to %{INBOUND:LOCAL-ADDR}
%{IP:SERVER}     Upstream (next-hop) server IP address (typically origin, or parent)
%{IP:OUTBOUND}   ATS's outbound IP address, that was used to connect upstream (next-hop)

Note that both %{IP:SERVER} and %{IP:OUTBOUND} can be unset, in which case the empty string is returned. The common use for this condition is actually as a value to an operator, e.g.

cond %{SEND_RESPONSE_HDR_HOOK}
  set-header X-Client-IP %{IP:CLIENT}
  set-header X-Inbound-IP %{IP:INBOUND}
  set-header X-Server-IP %{IP:SERVER}
  set-header X-Outbound-IP %{IP:OUTBOUND}

As a special matcher, the IP can be matched against a list of IP ranges, e.g.

cond %{IP:CLIENT} {192.168.201.0/24,10.0.0.0/8}

The format here is very specific, in particular no white spaces are allowed between the ranges.

INTERNAL-TRANSACTION

cond %{INTERNAL-TRANSACTION}

Returns true if the current transaction was internally-generated by Traffic Server (using TSHttpTxnIsInternal()). These transactions are not initiated by external client requests, but are triggered (often by plugins) entirely within the Traffic Server process.

METHOD

cond %{METHOD} <operand>

The HTTP method (e.g. GET, HEAD, POST, and so on) used by the client for this transaction.

NEXT-HOP

cond %{NEXT-HOP:<part>} <operand>

Returns next hop current selected parent information. The following qualifiers are supported:

%{NEXT-HOP:HOST} Name of the current selected parent.
%{NEXT-HOP:PORT} Port of the current selected parent.

Note that the <part> of NEXT-HOP will likely not be available unless an origin server connection is attempted at which point it will available as part of the SEND_REQUEST_HDR_HOOK.

For example:

cond %{SEND_REQUEST_HDR_HOOK} [AND]
cond %{NEXT-HOP:HOST} =www.firstparent.com
    set-header Host vhost.firstparent.com

cond %{SEND_REQUEST_HDR_HOOK} [AND]
cond %{NEXT-HOP:HOST} =www.secondparent.com
    set-header Host vhost.secondparent.com

NOW

cond %{NOW:<part>} <operand>

This is the current time, in the local timezone as set on the machine, typically GMC. Without any further qualifiers, this is the time in seconds since EPOCH aka Unix time. Qualifiers can be used to give various other values, such as year, month etc.

%{NOW:YEAR}      Current year (e.g. 2016)
%{NOW:MONTH}     Current month (0-11, 0 == January)
%{NOW:DAY}       Current day of the month (1-31)
%{NOW:HOUR}      Current hour (0-23, in the 24h system)
%{NOW:MINUTE}    Current minute (0-59}
%{NOW:WEEKDAY}   Current weekday (0-6, 0 == Sunday)
%{NOW:YEARDAY}   Current day of the year (0-365, 0 == Jan 1st)

RANDOM

cond %{RANDOM:<n>} <operand>

Generates a random integer from 0 up to (but not including) <n>. Mathematically, [0,n) or 0 <= r < n.

STATUS

cond %{STATUS} <operand>

Numeric HTTP status code of the response.

TO-URL

cond %{TO-URL:<part>} <operand>

In a remapping context, this condition matches against the target URL to which the remapping is directed. This condition is valid only within configurations provided through remap.config as described in Enabling Per-Mapping above.

The <part> allows the operand to match against just a component of the URL, as documented in URL Parts below.

TRUE / FALSE

cond %{TRUE}
cond %{FALSE}

These conditions always return a true value and a false value, respectively. The true condition is implicit in any rules which specify no conditions (only operators).

TXN-COUNT

cond %{TXN-COUNT} <operand>

Returns the number of transactions (including the current one) that have been sent on the current client connection. This can be used to detect if the current transaction is the first transaction.

URL

cond %{URL:<part>} <operand>

The complete URL of the current transaction. This will automatically choose the most relevant URL depending upon the hook context in which the condition is being evaluated.

Refer to Requests vs. Responses for more information on determining the context in which the transaction’s URL is evaluated. The <part> may be specified according to the options documented in URL Parts.

SSN-TXN-COUNT

cond %{SSN-TXN-COUNT} <operand>

Returns the number of transactions between the Traffic Server proxy and the origin server from a single session. Any value greater than zero indicates connection reuse.

TCP-INFO

cond %{<name>}
    add-header @PropertyName "%{TCP-INFO}"

This operation records TCP Info struct field values as an Internal remap as well as global header at the event hook specified by the condition. Supported hook conditions include TXN_START_HOOK, SEND_RESPONSE_HEADER_HOOK and TXN_CLOSE_HOOK in the Global plugin and REMAP_PSEUDO_HOOK, SEND_RESPONSE_HEADER_HOOK and TXN_CLOSE_HOOK in the Remap plugin. Conditions supported as request headers include TXN_START_HOOK and REMAP_PSEUDO_HOOK. The other conditions are supported as response headers. TCP Info fields currently recorded include rtt, rto, snd_cwnd and all_retrans. This operation is not supported on transactions originated within Traffic Server (for e.g using the Traffic Server TSHttpTxnIsInternal())

Condition Operands

Operands provide the means to restrict the values, provided by a condition, which will lead to that condition evaluating as true. There are currently four types supported:

Operand

Description

/regex/

Matches the condition’s provided value against the regular expression. Start the regex with (?i) to flag it for a case insensitive match, e.g. /(?i)regex/ will match ReGeX.

<string

Matches if the value from the condition is lexically less than string.

>string

Matches if the value from the condition is lexically greater than string.

=string

Matches if the value from the condition is lexically equal to string.

The absence of an operand for conditions which accept them simply requires that a value exists (e.g. the content of the header is not an empty string) for the condition to be considered true.

Condition Flags

The condition flags are optional, and you can combine more than one into a comma-separated list of flags. Note that whitespaces are not allowed inside the brackets:

Flag

Description

AND

Indicates that both the current condition and the next must be true. This is the default behavior for all conditions when no flags are provided.

NOT

Inverts the condition.

OR

Indicates that either the current condition or the next one must be true, as contrasted with the default behavior from [AND].

NOCASE

Indicates that the string comparison, or regular expression, should be case-insensitive. The default is to be case-sensitive.

Operators

Operators are the part of your header rewriting rules which actually modify the header content of your requests and responses. They are always the final part of a rule, following any of the conditions which whittled down the requests and responses to which they will be applied.

Multiple operators may be specified for a single rule, and they will be executed in the order listed. The end of the rule is marked either by the end of the configuration file or the next appearance of a condition (whichever occurs first).

The following operators are available:

add-header

add-header <name> <value>

Adds a new <name> header line with the contents <value>. Note that this operator can produce duplicate headers if one of <name> already exists, or your configuration supplies multiple instances of this operator in different rules which are all invoked. This is not an issue for headers which may safely be specified multiple times, such as Set-Cookie, but for headers which may only be specified once you may prefer to use set-header instead.

The header’s <value> may be specified as a literal string, or it may take advantage of String concatenations to calculate a dynamic value for the header.

counter

counter <name>

Increments an integer counter called <name> every time the rule is invoked. The counter is initialized at 0 if it does not already exist. The name you give your counter is arbitrary, though it is strongly advisable to avoid conflicts with existing Traffic Server statistics.

This counter can be viewed at any time through the standard statistics APIs, including the Stats Over HTTP plugin.

Counters can only increment by 1 each time this operator is invoked. There is no facility to increment by other amounts, nor is it possible to initialize the counter with any value other than 0. Additionally, the counter will reset whenever Traffic Server is restarted.

no-op

no-op

This operator does nothing, takes no arguments, and has no side effects.

rm-destination

rm-destination <part>

Removes individual components of the remapped destination’s address. When changing the remapped destination, <part> should be used to indicate the component that is being modified (see URL Parts). Currently the only valid parts for rm-destination are QUERY, PATH, and PORT.

For the query parameter, this operator takes an optional second argument, which is a list of query parameters to remove (or keep with [INV] modifier).

rm-destination QUERY <comma separate list of query parameter>

rm-header

rm-header <name>

Removes the header <name>.

run-plugin

run-plugin <plugin-name>.so "<plugin-argument> ..."

This allows to run an existing remap plugin, conditionally, from within a header rewrite rule.

set-body

set-body <text>

Sets the body to <text>. Can also be used to delete a body with "". This is only useful when overriding the origin status, i.e. intercepting/pre-empting a request so that you can override the body from the body-factory with your own.

set-body-from

set-body-from <URL>

Will call <URL> (see URL in URL Parts) to retrieve a custom error response and set the body with the result. Triggering this rule on an OK transaction will send a 500 status code to the client with the desired response. If this is triggered on any error status code, that original status code will be sent to the client. Note: This config should only be set using READ_RESPONSE_HDR_HOOK

An example config would look like

cond %{READ_RESPONSE_HDR_HOOK} set-body-from http://www.example.com/second

Where http://www.example.com/second is the destination to retrieve the custom response from. This can be enabled per-mapping or globally. Ensure there is a remap rule for the second endpoint as well! An example remap config would look like

map /first http://www.example.com/first @plugin=header_rewrite.so @pparam=cond1.conf map /second http://www.example.com/second

set-config

set-config <name> <value>

Allows you to override the value of a Traffic Server configuration variable for the current connection. The variables specified by <name> must be overridable. For details on available Traffic Server configuration variables, consult the documentation for records.yaml. You can read more about overridable configuration variables in the developer’s documentation for TSHttpOverridableConfig.

set-conn-dscp

set-conn-dscp <value>

When invoked, sets the client side DSCP value for the current transaction. The <value> should be specified as a decimal integer.

set-conn-mark

set-conn-mark <value>

When invoked, sets the client side MARK value for the current transaction. The <value> should be specified as a decimal integer. Requires at least Linux 2.6.25.

set-debug

set-debug

When invoked, this operator enables the internal transaction debugging flag (via TSHttpTxnDebugSet()), which causes debug messages to be printed to the appropriate logs even when the debug tag has not been enabled. For additional information on Traffic Server debugging statements, refer to Debug Tags in the developer’s documentation.

Note: This operator is deprecated, use the set-http-cntl operator instead, with the TXN_DEBUG control.

set-destination

set-destination <part> <value>

Modifies individual components of the remapped destination’s address. When changing the remapped destination, <part> should be used to indicate the component that is being modified (see URL Parts), and <value> will be used as its replacement. You must supply a non-zero length value, otherwise this operator will be an effective no-op (though a warning will be emitted to the logs if debugging is enabled).

set-header

set-header <name> <value>

Replaces the value of header <name> with <value>, creating the header if necessary.

The header’s <value> may be a literal string, or take advantage of String concatenations to calculate a dynamic value for the header.

set-redirect

set-redirect <code> <destination>

When invoked, sends a redirect response to the client, with HTTP status <code>, and a new location of <destination>. If the QSA flag is enabled, the original query string will be preserved and added to the new location automatically. This operator supports String concatenations for <destination>. This operator can only execute on the READ_RESPONSE_HDR_HOOK (the default when the plugin is global), the SEND_RESPONSE_HDR_HOOK, or the REMAP_PSEUDO_HOOK.

set-status

set-status <code>

Modifies the HTTP status code used for the response. <code> must be a valid status code. This operator will also update the reason in the HTTP response, based on the code you have chosen. If you wish to override that with your own text, you will need to invoke the set-status-reason operator after this one.

set-status-reason

set-status-reason <reason>

Modifies the HTTP response to use <reason> as the HTTP status reason, instead of the standard string (which depends on the HTTP status code used).

set-timeout-out

set-timeout-out <type> <value>

Modifies the timeout values for the current transaction to <value> (specified in milliseconds). The <type> must be one of the following: active, inactive, connect, or dns.

skip-remap

skip-remap <value>

When invoked, and when <value> is any of 1, true, or TRUE, this operator causes Traffic Server to abort further request remapping. Any other value and the operator will effectively be a no-op.

Note: This operator is deprecated, use the set-http-cntl operator instead, with the SKIP_REMAP control.

set-http-cntl

set-http-cntl <controller> <flag>

This operator lets you turn off (or on) the logging for a particular transaction. The <flag> is any of 0, off, and false, or 1, on and true. The available controllers are:

Controller

Description

LOGGING

Turns off logging for the transaction (default: on)

INTERCEPT_RETRY

Allow intercepts to be retried (default: off)

RESP_CACHEABLE

Force the response to be cacheable (default: off)

REQ_CACHEABLE

Force the request to be cacheable (default: off)

SERVER_NO_STORE

Don’t allow the response to be written to cache (default: off)

TXN_DEBUG

Enable transaction debugging (default: off)

SKIP_REMAP

Don’t require a remap match for the transaction (default: off)

Operator Flags

Operator flags are optional, are separated by commas when using more than one for a single operator, and must not contain whitespace inside the brackets. For example, an operator with the L flag would be written in this manner:

set-destination HOST foo.bar.com [L]

The flags currently supported are:

Flag

Description

L

Last rule, do not continue.

I

Invert the semantics of the rules parameters

QSA

Append the results of the rule to the query string.

String concatenations

You can concatenate values using strings, condition values and variable expansions on the same line.

add-header CustomHeader “Hello from %{IP:SERVER}:%{INBOUND:LOCAL-PORT}”

This is the new, generic form of setting header values. Unfortunately, string concatenation is not yet supported in conditionals.

Note: In versions prior to ATS v9.0.0, an alternative string expansion was available. those expansions are no longer available, but the following table can help migrations:

Old expansion variable

Condition variable to use with concatenations

%<proto>

%{CLIENT-URL:SCHEME}

%<port>

%{CLIENT-URL:PORT}

%<chi>

%{IP:CLIENT}, %{INBOUND:REMOTE-ADDR} or e.g. %{CIDR:24,48}

%<cqhl>

%{CLIENT-HEADER:Content-Length}

%<cqhm>

%{METHOD}

%<cque>

%[CLIENT-URL}

%<cquup>

%{CLIENT-URL:PATH}

URL Parts

Some of the conditions and operators which use a request or response URL as their target allow for matching against specific components of the URL. For example, the CLIENT-URL condition can be used to test just against the query parameters by writing it as:

cond %{CLIENT-URL:QUERY} <operand>

The URL part names which may be used for these conditions and actions are:

Part

Description

HOST

Full hostname.

PATH

URL substring beginning with (but not including) the first / after the hostname up to, but not including, the query string. Note: previous versions of ATS had a %{PATH} directive, this will no longer work. Instead, you want to use %{CLIENT-URL:PATH}.

PORT

Port number.

QUERY

URL substring from the ?, signifying the beginning of the query parameters, until the end of the URL. Empty string if there were no query parameters.

SCHEME

URL scheme in use (e.g. http and https).

URL

The complete URL.

As another example, a remap rule might use the set-destination operator to change just the hostname via:

cond %{HEADER:X-Mobile} = "foo"
set-destination HOST foo.mobile.bar.com [L]

Requests vs. Responses

Both HTTP requests and responses have headers, a good number of them with the same names. When writing a rule against, for example, the Connection or Via headers which are both valid in a request and a response, how can you tell which is which, and how do you indicate to Traffic Server that the condition is specifically and exclusively for a request header or just for a response header? And how do you ensure that a header rewrite occurs against a request before it is proxied?

Hook Conditions

In addition to the conditions already described above, there are a set of special hook conditions. Only one of these conditions may be specified per ruleset, and they must be the first condition listed. Which hook condition is used determines the context in which the ruleset is evaluated, and whether the other conditions will default to operating on the client request headers or the origin response (or cache response) headers.

Hook conditions are written just like the other conditions, except that none of them take any operands:

cond %{<name>}

Because hook conditions must be the first condition in a ruleset, the use of one forces the beginning of a new ruleset.

header rewrite hooks

READ_REQUEST_HDR_HOOK

Evaluates as soon as the client request has been read, but prior to any further processing (including contacting origin servers or fetching objects from cache). Conditions and operators which adapt to matching or manipulating request or response entities (e.g. headers) depending on their context will all operate on the request variants when using this hook, as there is no response data yet.

This hook is not available to remap rules.

READ_REQUEST_PRE_REMAP_HOOK

For ruleset configurations provided via remap.config, this forces their evaluation as soon as the request has been read, but prior to the remapping. For all context-adapting conditions and operators, matching will occur against the request, as there is no response data available yet.

This hook is not available to remap rules.

REMAP_PSEUDO_HOOK

Similar to READ_REQUEST_HDR_HOOK, but only available when used in a remap context, evaluates prior to SEND_REQUEST_HDR_HOOK and allows the rewrite to evaluate as part of the remapping.

Because this hook is valid only within a remapping context, for configuration files shared by both the global plugin.config and individual remapping entries in remap.config, this hook condition will force the subsequent ruleset(s) to be valid only for remapped transactions.

SEND_REQUEST_HDR_HOOK

Forces evaluation of the ruleset just prior to contacting origin servers (or fetching the object from cache), but after any remapping may have occurred.

READ_RESPONSE_HDR_HOOK

Rulesets evaluated within this context will process only once the origin server response has been read, but prior to Traffic Server sending that response to the client.

This is the default hook condition for all globally-configured rulesets.

SEND_RESPONSE_HDR_HOOK

Evaluates rulesets just prior to sending the client response, but after any cache updates may have been performed. This hook context provides a means to modify aspects of the response sent to a client, while still caching the original versions of those attributes delivered by the origin server.

TXN_START_HOOK

Rulesets are evaluated when Traffic Server receives a request and accepts it. This hook context indicates that a HTTP transaction is initiated and therefore, can only be enabled as a global plugin.

TXN_CLOSE_HOOK

Rulesets are evaluated when Traffic Server completes a transaction, i.e., after a response has been sent to the client. Therefore, header modifications at this hook condition only makes sense for internal headers.

Affected Conditions

The following conditions are affected by the hook context in which they are evaluated and will adjust using request or response entities automatically:

Affected Operators

The following operators are affected by the hook context in which they are evaluated and will adjust modifying request or response entities automatically:

Caveats

Repeated Headers

Some headers may appear more than once in a request or a response. When this occurs, all values will be collapsed into a single comma-delimited string before the conditions see them. This avoids the problem of determining which header instance out of several a condition’s rule will be applied to, but it may introduce unexpected behavior in your operands.

For example, let us assume an origin response includes a header named X-Foo which specifies a keyword of some sort. This header may appear zero or more times and we wish to construct a HEADER condition that can handle this.

Condition A

This condition will match using a direct equality operand:

cond %{HEADER:X-Foo} =bar

Condition B

This condition will match using an unanchored regular expression:

cond %{HEADER:X-Foo} /bar/

Sample Headers

Both conditions A and B will match this response:

HTTP/1.1 200 OK
Date: Mon, 08 Feb 2016 18:11:30 GMT
Content-Length: 1234
Content-Type: text/html
X-Foo: bar

Only condition B will match this response:

HTTP/1.1 200 OK
Date: Mon, 08 Feb 2016 18:11:30 GMT
Content-Length: 1234
Content-Type: text/html
X-Foo: bar
X-Foo: baz

That is because the HEADER condition will see the value of X-Foo as bar,baz. Condition B will still match this because the regular expression, being unanchored, finds the substring bar. But condition A fails, as it is expecting the value to be the exact string bar, nothing more and nothing less.

Examples

Remove Origin Authentication Headers

The following ruleset removes any authentication headers from the origin response before caching it or returning it to the client. This is accomplished by setting the hook context and then removing the cookie and basic authentication headers.:

cond %{READ_RESPONSE_HDR_HOOK}
rm-header Set-Cookie
rm-header WWW-Authenticate

Count Teapots

Maintains a counter statistic, which is incremented every time an origin server has decided to be funny by returning HTTP 418:

cond %{STATUS} =418
counter plugin.header_rewrite.teapots

Normalize Statuses

For client-facing purposes only (because we set the hook context to just prior to delivering the response back to the client, but after all processing and possible cache updates have occurred), replaces all 4xx HTTP status codes from the origin server with 404:

cond %{SEND_RESPONSE_HDR_HOOK}
cond %{STATUS} >399
cond %{STATUS} <500
set-status 404

Remove Cache Control to Origins

Removes cache control related headers from requests before sending them to an origin server:

cond %{SEND_REQUEST_HDR_HOOK}
rm-header Cache-Control
rm-header Pragma

Enable Debugging Per-Request

Turns on Traffic Server debugging statements for a transaction, but only when a special header is present in the client request:

cond %{READ_REQUEST_HDR_HOOK}
cond %{CLIENT-HEADER:X-Debug} =supersekret
set-debug

Remove Internal Headers

Removes special internal/development headers from origin responses, unless the client request included a special debug header:

cond %{CLIENT-HEADER:X-Debug} =keep [NOT]
rm-header X-Debug-Foo
rm-header X-Debug-Bar

Return Original Method in Response Header

This rule copies the original HTTP method that was used by the client into a custom response header:

cond %{SEND_RESPONSE_HDR_HOOK}
set-header X-Original-Method %{METHOD}

Useless Example From Purpose

Even that useless example from Purpose in the beginning of this document is possible to accomplish:

cond %{INBOUND:LOCAL-PORT} =8090
cond %{METHOD} =HEAD
cond %{CLIENT-HEADER:Accept-Language} /es-py/ [NOT]
cond %{STATUS} =304 [OR]
cond %{RANDOM:500} >290
set-status 403

Add Cache Control Headers Based on Origin Path

This rule adds cache control headers to CDN responses based matching the origin path. One provides a max age and the other provides a “no-cache” statement to two different file paths.

cond %{SEND_RESPONSE_HDR_HOOK}
cond %{CLIENT-URL:PATH} /examplepath1/
add-header Cache-Control "max-age=3600" [L]
cond %{SEND_RESPONSE_HDR_HOOK}
cond %{CLIENT-URL:PATH} /examplepath2\/examplepath3\/.*/
add-header Cache-Control "no-cache" [L]

Redirect when the Origin Server Times Out

This rule sends a 302 redirect to the client with the requested URI’s Path and Query string when the Origin server times out or the connection is refused:

cond %{SEND_RESPONSE_HDR_HOOK}
cond %{STATUS} =502 [OR]
cond %{STATUS} =504
set-redirect 302 http://different_origin.example.com/%{CLIENT-URL:PATH} [QSA]

Check for existence of a header

This rule will modify the Cache-Control header, but only if it is not already set to some value, and the status code is a 2xx:

cond %{READ_RESPONSE_HDR_HOOK} [AND]
cond %{HEADER:Cache-Control} ="" [AND]
cond %{STATUS} >199 [AND]
cond %{STATUS} <300
set-header Cache-Control "max-age=600, public"

Add HSTS

Add the HTTP Strict Transport Security (HSTS) header if it does not exist and the inbound connection is TLS:

cond %{READ_RESPONSE_HDR_HOOK} [AND]
cond %{HEADER:Strict-Transport-Security} ="" [AND]
cond %{INBOUND:TLS} /./
set-header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"

This is mostly used by being attached to a remap rule that maps to a host known to support TLS. If the parallel OUTBOUND supported is added then this could be done by checking for inbound TLS both outbound TLS in the SEND_REQUEST_HDR_HOOK. However this technique may be used for a non-TLS upstream if the goal is to require the user agent to connect to Traffic Server over TLS.

Close Connections for draining

When a healthcheck file is missing (in this example, /path/to/the/healthcheck/file.txt), add a Connection: close header to have clients drop their connection, allowing the server to drain. Although Connection header is only available on HTTP/1.1 in terms of protocols, but this also works for HTTP/2 connections because the header triggers HTTP/2 graceful shutdown. This should be a global configuration.:

cond %{SEND_RESPONSE_HDR_HOOK}
cond %{ACCESS:/path/to/the/healthcheck/file.txt}    [NOT,OR]
set-header Connection "close"

Use Internal header to pass data

In Traffic Server, a header that begins with @ does not leave Traffic Server. Thus, you can use this to pass data to different Traffic Server systems. For instance, a series of remap rules could each be tagged with a consistent name to make finding logs easier.:

cond %{REMAP_PSEUDO_HOOK}
set-header @PropertyName "someproperty"

(Then in logging.yaml, log %<{@PropertyName}cqh>)

Remove Client Query Parameters

The following ruleset removes any query parameters set by the client.:

cond %{REMAP_PSEUDO_HOOK}
rm-destination QUERY

Remove only a few select query parameters:

cond %{REMAP_PSEUDO_HOOK} rm-destination QUERY foo,bar

Keep only a few select query parameters – removing the rest:

cond %{REMAP_PSEUDO_HOOK} rm-destination QUERY foo,bar [I]

Mimic X-Debug Plugin’s X-Cache Header

This rule can mimic X-Debug plugin’s X-Cache header by accumulating the CACHE condition results to a header.:

cond %{SEND_RESPONSE_HDR_HOOK} [AND]
cond %{HEADER:All-Cache} ="" [NOT]
set-header All-Cache "%{HEADER:All-Cache}, %{CACHE}"

cond %{SEND_RESPONSE_HDR_HOOK} [AND]
cond %{HEADER:All-Cache} =""
set-header All-Cache %{CACHE}

Add Identifier from Server with Data

This rule adds an unique identifier from the server if the data is fresh from the cache or if the identifier has not been generated yet. This will inform the client where the requested data was served from.:

cond %{SEND_RESPONSE_HDR_HOOK} [AND]
cond %{HEADER:ATS-SRVR-UUID} ="" [OR]
cond %{CACHE} ="hit-fresh"
set-header ATS-SRVR-UUID %{ID:UNIQUE}

Apply rate limiting for some select requests

This rule will conditiionally, based on the client request headers, apply rate limiting to the request.:

cond %{REMAP_PSEUDO_HOOK} [AND]
cond %{CLIENT-HEADER:Some-Special-Header} ="yes"
run-plugin rate_limit.so "--limit=300 --error=429"