Appending and Removing Request/Response Headers
Gloo Edge can add and remove headers to/from requests and responses. We refer to this feature as “Header Manipulation”.
Header Manipulation is configured via the headerManipulation struct.
This struct can be added to Route Options , Virtual Host Options , and Weighted Destination Options .
The headerManipulation
struct contains four optional fields: requestHeadersToAdd
, requestHeadersToRemove
, responseHeadersToAdd
, and responseHeadersToRemove
. You can specify the key and value for the header directly in the manifest, or for requestHeadersToAdd
, you can reference a secret of the type gloo.solo.io/header
or Opaque
.
headerManipulation:
# add headers to request
requestHeadersToAdd:
- header:
key: HEADER_NAME
value: HEADER_VALUE
# if the header HEADER_NAME is already present,
# append the value.
append: true
- header:
key: HEADER_NAME
value: HEADER_VALUE
# if the header HEADER_NAME is already present,
# overwrite the value.
append: false
- headerSecretRef:
name: SECRET_NAME
namespace: SECRET_NAMESPACE
# The type of the secret must be gloo.solo.io/header or Opaque.
# Each key/value pair in the secret is added to the header.
# NOTE: Referencing a secret in a different namespace than the
# upstream is not recommended. Instead, ensure secrets are in the
# same namespace as the upstream.
# remove headers from request
requestHeadersToRemove:
- "HEADER_NAME"
- "HEADER_NAME"
# add headers to response
responseHeadersToAdd:
- header:
key: HEADER_NAME
value: HEADER_VALUE
# if the header HEADER_NAME is already present,
# append the value.
append: true
- header:
key: HEADER_NAME
value: HEADER_VALUE
# if the header HEADER_NAME is already present,
# overwrite the value.
append: false
# remove headers from response
responseHeadersToRemove:
- "HEADER_NAME"
- "HEADER_NAME"
Depending on where the headerManipulation
struct is added, the header manipulation will be applied on that level.
-
When using
headerManipulation
in routeoptions
, headers will be manipulated for all traffic matching that route. -
When using
headerManipulation
in virtual hostoptions
, headers will be manipulated for all traffic handled by the virtual host. -
When using
headerManipulation
in weighted destinationoptions
, headers will be manipulated for all traffic that is sent to the specific destination when it is selected for load balancing.
Envoy supports adding dynamic values to request and response headers. The percent symbol (%) is used to delimit variable names. See a list of the dynamic variables supported by Envoy in the envoy docs.
If you would like to use the headerSecretRef
with requestHeadersToAdd
, you can create the correct Secret type by using glooctl
. For example, the following command will create a secret named my-headers
:
glooctl create secret header my-headers --headers x-header-1=one,x-header-2=two
The secret will be created in the same namespace as the Gloo Edge installation by default. Inspecting the secret will show that the type is set to gloo.solo.io/header
. Each key/value pair in the secret will be added as a header to the request.
Referencing a secret in a different namespace than the upstream is not recommended. Instead, ensure secrets are in the same namespace as the upstream. You can additionally set the gloo.headerSecretRefNsMatchesUs
Helm field to true, which requires any secrets that are sent in headers to come from the same namespace as the destination upstream.
Example: Manipulating Headers on a Route
apiVersion: gateway.solo.io/v1
kind: VirtualService
metadata:
creationTimestamp: null
name: 'default'
namespace: 'gloo-system'
spec:
virtualHost:
domains:
- '*'
routes:
- matchers:
- prefix: '/petstore'
routeAction:
single:
upstream:
name: 'default-petstore-8080'
namespace: 'gloo-system'
options:
prefixRewrite: '/api/pets'
headerManipulation:
# add headers to all responses
# returned by this route
responseHeadersToAdd:
- header:
key: HEADER_NAME
value: HEADER_VALUE
- headerSecretRef
name: my-headers
namespace: gloo-system
status: {}
Example: Manipulating Headers on a VirtualHost
apiVersion: gateway.solo.io/v1
kind: VirtualService
metadata:
creationTimestamp: null
name: 'default'
namespace: 'gloo-system'
spec:
virtualHost:
domains:
- '*'
routes:
- matchers:
- prefix: '/petstore'
routeAction:
single:
upstream:
name: 'default-petstore-8080'
namespace: 'gloo-system'
options:
prefixRewrite: '/api/pets'
options:
headerManipulation:
# remove headers from all requests
# handled by this virtual host
requestHeadersToRemove:
- "x-my-header"
- "x-your-header"
status: {}
Example: Manipulating Headers on a Weighted Destination
apiVersion: gateway.solo.io/v1
kind: VirtualService
metadata:
creationTimestamp: null
name: 'default'
namespace: 'gloo-system'
spec:
virtualHost:
domains:
- '*'
routes:
- matchers:
- prefix: /myservice
routeAction:
multi:
destinations:
- weight: 9
destination:
upstream:
name: default-myservice-v1-8080
namespace: gloo-system
- weight: 1
destination:
upstream:
name: default-myservice-v2-8080
namespace: gloo-system
options:
headerManipulation:
# add headers to all requests
# that are load balanced to `default-myservice-v2-8080`
# on this route
requestHeadersToAdd:
- header:
key: HEADER_NAME
value: HEADER_VALUE
status: {}
Inheritance of request headers
Headers can be inherited by children options, such as shown in the following example with delegated routes. For more information about inheritance, see Inheritance rules. For more information about delegation, see Delegating with route tables.
- In your Virtual Service, set up a delegated route.
apiVersion: gateway.solo.io/v1 kind: VirtualService metadata: name: 'example' namespace: 'gloo-system' spec: virtualHost: domains: - 'example.com' routes: - matchers: - prefix: '/a' # delegate ownership of routes for `example.com/a` delegateAction: ref: name: 'a-routes' namespace: 'a' - matchers: - prefix: '/b' # delegate ownership of routes for `example.com/b` delegateAction: ref: name: 'b-routes' namespace: 'b'
- Add headers that you want all child objects to inherit in the VirtualHost parent object in the same VirtualService as the previous step. In the following example, the
x-gateway-start-time
header is added to requests, and thex-route-table: alphabet
header is added to responses.... virtualHost: options: headerManipulation: requestHeadersToAdd: - header: key: x-gateway-start-time value: '%START_TIME%' responseHeadersToAdd: - header: key: x-route-table value: alphabet append: false # overwrite the value of `x-route-table` if it already exists
- In the RouteTable child object, define other headers. In the following example, the
x-route-table
header is added to requests, and thex-route-table: a
header is added to responses.apiVersion: gateway.solo.io/v1 kind: RouteTable metadata: name: 'a-routes' namespace: 'a' spec: routes: - matchers: # the path matchers in this RouteTable must begin with the prefix `/a/` - prefix: '/a/1' routeAction: single: upstream: name: 'foo-upstream' options: headerManipulation: requestHeadersToAdd: - header: key: x-route-table value: a responseHeadersToAdd: - header: key: x-route-table value: a
- Now, requests that match the route
/a/1
get the following headers:- The
x-gateway-start-time
request header is inherited from the parent VirtualHost option. - The
x-route-table
request header is set in the child Route option. - The
x-route-table
response header in the parent overwrites the child object’s value ofa
instead toalphabet
.
- The
Due to how header manipulations are processed, less specific headers overwrite more specific headers.
From the Envoy docs:
Headers are appended to requests/responses in the following order: weighted cluster level headers, route level headers, virtual host level headers and finally global level headers.
In the previous example of the x-route-table
response header, the virtual host level header overwrites the route level header because the virtual host level header is evaluated after the route level header.
- If you set
append: true
or omit this field on the virtual host, then the route level response header (a
) would get appended to the virtual host level header (alphabet
). - If you set
append: false
on the route, the route does not affect the virtual host because the route is evaluated before the virtual host. In the example, the response header would stayx-route-table: alphabet
.
Reversing the order of header manipulation evaluation
You can reverse the order in which header manipulations are evaluated so that order of evaluation becomes: global level headers, virtual host level headers, route level headers, and finally weighted cluster level headers. With the order of evaluation being reversed, more specific header manipulations can override less specific ones.
To reverse the order of evaluation, set the mostSpecificHeaderMutationsWins
field to true
in the routeOptions settings for a Gateway.
The route options that you set on the Gateway will apply to all routes that the Gateway serves.
apiVersion: gateway.solo.io/v1
kind: Gateway
metadata: # collapsed for brevity
spec:
routeOptions:
mostSpecificHeaderMutationsWins: true
status: # collapsed for brevity
Following the VirtualService and RouteTable setup in the previous section, add the append
setting to false
on the RouteTable. This way, the header values added by the RouteTable override less specific headers instead of append to the values.
apiVersion: gateway.solo.io/v1
kind: RouteTable
metadata:
name: 'a-routes'
namespace: 'a'
spec:
routes:
- matchers:
# the path matchers in this RouteTable must begin with the prefix `/a/`
- prefix: '/a/1'
routeAction:
single:
upstream:
name: 'foo-upstream'
options:
headerManipulation:
requestHeadersToAdd:
- header:
key: x-route-table
value: a
responseHeadersToAdd:
- header:
key: x-route-table
value: a
append: false
With mostSpecificHeaderMutationsWins
set to true
and append
set to false
, now the x-route-table
response header in the child RouteTable overwrites the parent object’s value of alphabet
instead to a
.