When routing traffic through a service mesh, you often need to inject tracing IDs, enforce security policies, or pass metadata between services. Service Mesh (ASM) supports HTTP header manipulation at the route level through the headers field of the VirtualService CustomResourceDefinition (CRD). You can add, overwrite, or remove headers on both requests and responses without modifying application code.
Prerequisites
Before you begin, ensure that you have:
-
The HTTPBin application deployed in your ASM instance. For instructions, see Deploy the HTTPBin application
Header operations reference
VirtualService provides three operations for manipulating headers:
|
Operation |
YAML field |
Behavior |
Value type |
|
|
|
Adds the specified header with the given value. If the header does not exist, it is created. |
|
|
|
|
Overwrites the header value. Creates the header if it does not exist. |
|
|
|
|
Deletes the header entirely. |
|
Static and dynamic header values
Header values can be static strings or dynamic Envoy command operators enclosed in % symbols. For example, %UPSTREAM_CLUSTER% indicates the name of a service provider. All HTTP command operators used for access logs can be specified in custom request or response headers.
|
Variable |
Description |
|
|
Timestamp when the request started |
|
|
Name of the upstream service cluster |
For the full list of available variables, see Command operators in the Envoy documentation.
Configure header manipulation
The following VirtualService configuration demonstrates all three operations on both request and response headers:
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: httpbin-vs
spec:
gateways:
- httpbin
hosts:
- '*'
http:
- route:
- destination:
host: httpbin
port:
number: 8000
weight: 100
headers:
request:
add:
x-custom-request-header: "custom-value" # Append a static value
x-dynamic-request-header: "%START_TIME%" # Append a dynamic value
set:
x-another-request-header: "another-value" # Overwrite or create
remove:
- x-unwanted-header # Delete entirely
response:
add:
x-custom-response-header: "custom-response-value"
set:
x-another-response-header: "another-response-value"
remove:
- x-unwanted-response-header
This configuration applies the following changes:
Request headers
-
Adds
x-custom-request-headerwith the static valuecustom-value. -
Adds
x-dynamic-request-headerwith the request start timestamp, resolved at runtime from%START_TIME%. -
Overwrites
x-another-request-headerwith the valueanother-value. If this header does not exist, it is created. -
Removes
x-unwanted-header.
Response headers
-
Adds
x-custom-response-headerwith the valuecustom-response-value. -
Overwrites
x-another-response-headerwith the valueanother-response-value. If this header does not exist, it is created. -
Removes
x-unwanted-response-header.
The VirtualService CRD can be used to set and modify HTTP headers. However, Envoy access logs do not record these changes if Envoy retains the default configurations of access logs. If you want to record custom headers in Envoy access logs, you must modify the log format of Envoy.
Verify custom headers in access logs
ASM allows you to customize log formats. The custom expressions of access logs can obtain values from request headers, response headers, and Envoy built-in values. For instructions, see Customize the format of access logs.
Add the following fields to your access log format:
|
Field name |
Type |
Log format expression |
|
|
Request attribute |
|
|
|
Request attribute |
|
|
|
Response attribute |
|
Response headers modified by VirtualService may not appear in access logs if Envoy applies the modification after the logging step in its filter chain. This is expected behavior, not a configuration error. See the HTTPBin pod example below.
After you update the log format, check the access logs for both the gateway pod and the HTTPBin pod.
Gateway pod access log
The gateway pod captures both request and response header modifications:
{
"bytes_received": "9",
"bytes_sent": "33",
"response_code": "200",
"my-x-custom-request-header": "custom-value",
"my-x-dynamic-request-header": "2024-01-16T14:49:21.187Z",
"my-x-custom-response-header": "custom-response-value"
}
HTTPBin pod access log
The HTTPBin sidecar does not capture response headers added by VirtualService because the modification occurs after the sidecar's logging point. The my-x-custom-response-header field shows -:
{
"bytes_received": "9",
"bytes_sent": "33",
"response_code": "200",
"my-x-custom-request-header": "custom-value",
"my-x-dynamic-request-header": "2024-01-16T14:49:21.187Z",
"my-x-custom-response-header": "-"
}