Response headers
Use the ResponseHeaderModifier filter to add, append, overwrite, or remove headers from a response before it is sent back to the client.
For more information, see the HTTPHeaderFilter specification.
Before you begin
-
Follow the Get started guide to install kgateway.
-
Follow the Sample app guide to create a gateway proxy with an HTTP listener and deploy the httpbin sample app.
-
Get the external address of the gateway and save it in an environment variable.
export INGRESS_GW_ADDRESS=$(kubectl get svc -n kgateway-system http -o jsonpath="{.status.loadBalancer.ingress[0]['hostname','ip']}") echo $INGRESS_GW_ADDRESSkubectl port-forward deployment/http -n kgateway-system 8080:8080
Add response headers
Add headers to incoming requests before they are sent back to the client. If the response already has the header set, the value of the header in the ResponseHeaderModifier filter is appended to the value of the header in the response.
-
Set up a header modifier that adds a
my-response: helloresponse header. Choose between the HTTPRoute for a Gateway API-native way, or TrafficPolicy for more flexible attachment options such as a gateway-level policy.kubectl apply -f- <<EOF apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: httpbin-headers namespace: httpbin spec: parentRefs: - name: http namespace: kgateway-system hostnames: - headers.example rules: - filters: - type: ResponseHeaderModifier responseHeaderModifier: add: - name: my-response value: hello backendRefs: - name: httpbin port: 8000 EOFSetting Description spec.parentRefsThe name and namespace of the gateway that serves this HTTPRoute. In this example, you use the httpgateway that was created as part of the get started guide.spec.rules.filters.typeThe type of filter that you want to apply to incoming requests. In this example, the ResponseHeaderModifierfilter is used.spec.rules.filters.responseHeaderModifier.addThe name and value of the response header that you want to add. spec.rules.backendRefsThe backend destination you want to forward traffic to. In this example, all traffic is forwarded to the httpbin app that you set up as part of the get started guide. Note: The steps in this section use the Envoy-based kgateway data plane. The steps do not work with the agentgateway data plane.
-
Create an HTTPRoute resource for the route that you want to modify. Note that the example selects the http Gateway that you created before you began.
kubectl apply -f- <<EOF apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: httpbin-headers namespace: httpbin spec: parentRefs: - name: http namespace: kgateway-system hostnames: - headers.example rules: - backendRefs: - name: httpbin port: 8000 EOF -
Create a TrafficPolicy that adds a
my-response: helloheader to a response. The following example attaches the TrafficPolicy to the http Gateway.kubectl apply -f- <<EOF apiVersion: gateway.kgateway.dev/v1alpha1 kind: TrafficPolicy metadata: name: httpbin-headers namespace: kgateway-system spec: targetRefs: - group: gateway.networking.k8s.io kind: Gateway name: http headerModifiers: response: add: - name: my-response value: hello EOF
-
-
Send a request to the httpbin app on the
headers.exampledomain. Verify that you get back a 200 HTTP response code and that you see themy-responseheader in the response.curl -vi http://$INGRESS_GW_ADDRESS:8080/response-headers -H "host: headers.example:8080"curl -vi localhost:8080/response-headers -H "host: headers.example"Example output:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17* Mark bundle as not supporting multiuse < HTTP/1.1 200 OK HTTP/1.1 200 OK < access-control-allow-credentials: true access-control-allow-credentials: true < access-control-allow-origin: * access-control-allow-origin: * < content-type: application/json; encoding=utf-8 content-type: application/json; encoding=utf-8 < content-length: 3 content-length: 3 < x-envoy-upstream-service-time: 0 x-envoy-upstream-service-time: 0 < my-response: hello my-response: hello < server: envoy server: envoy -
Optional: Remove the resources that you created.
kubectl delete httproute httpbin-headers -n httpbinkubectl delete httproute httpbin-headers -n httpbin kubectl delete TrafficPolicy httpbin-headers -n kgateway-system
Set response headers
Setting headers is similar to adding headers. If the response does not include the header, it is added by the ResponseHeaderModifier filter. However, if the request already contains the header, its value is overwritten with the value from the ResponseHeaderModifier filter.
-
Set up a header modifier that sets a
my-response: customresponse header. Choose between the HTTPRoute for a Gateway API-native way, or TrafficPolicy for more flexible attachment options such as a gateway-level policy.kubectl apply -f- <<EOF apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: httpbin-headers namespace: httpbin spec: parentRefs: - name: http namespace: kgateway-system hostnames: - headers.example rules: - filters: - type: ResponseHeaderModifier responseHeaderModifier: set: - name: my-response value: custom backendRefs: - name: httpbin port: 8000 EOFSetting Description spec.parentRefsThe name and namespace of the gateway that serves this HTTPRoute. In this example, you use the httpGateway that was created as part of the get started guide.spec.rules.filters.typeThe type of filter that you want to apply to incoming requests. In this example, the ResponseHeaderModifierfilter is used.spec.rules.filters.responseHeaderModifier.setThe name and value of the response header that you want to set. spec.rules.backendRefsThe backend destination you want to forward traffic to. In this example, all traffic is forwarded to the httpbin app that you set up as part of the get started guide. Note: The steps in this section use the Envoy-based kgateway data plane. The steps do not work with the agentgateway data plane.
-
Create an HTTPRoute resource for the route that you want to modify. Note that the example selects the http Gateway that you created before you began.
kubectl apply -f- <<EOF apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: httpbin-headers namespace: httpbin spec: parentRefs: - name: http namespace: kgateway-system hostnames: - headers.example rules: - backendRefs: - name: httpbin port: 8000 EOF -
Create a TrafficPolicy that sets the
my-responseheader to acustomvalue on a response. The following example attaches the TrafficPolicy to the http Gateway.kubectl apply -f- <<EOF apiVersion: gateway.kgateway.dev/v1alpha1 kind: TrafficPolicy metadata: name: httpbin-headers namespace: kgateway-system spec: targetRefs: - group: gateway.networking.k8s.io kind: Gateway name: http headerModifiers: response: set: - name: my-response value: custom EOF
-
-
Send a request to the httpbin app on the
headers.exampledomain. Verify that you get back a 200 HTTP response code and that themy-response: customheader was set.curl -vi http://$INGRESS_GW_ADDRESS:8080/response-headers -H "host: headers.example:8080"curl -vi localhost:8080/response-headers -H "host: headers.example"Example output:
1 2 3 4 5 6 7 8 9 10 11 12 13... * Request completely sent off < HTTP/1.1 200 OK HTTP/1.1 200 OK < access-control-allow-credentials: true access-control-allow-credentials: true < access-control-allow-origin: * access-control-allow-origin: * ... < my-response: custom my-response: custom < server: envoy server: envoy -
Optional: Remove the resources that you created.
kubectl delete httproute httpbin-headers -n httpbinkubectl delete httproute httpbin-headers -n httpbin kubectl delete TrafficPolicy httpbin-headers -n kgateway-system
Remove response headers
You can remove HTTP headers from a response before the response is sent back to the client.
-
Send a request to the httpbin app and find the
content-lengthheader.curl -vi http://$INGRESS_GW_ADDRESS:8080/response-headers -H "host: www.example.com:8080"curl -vi localhost:8080/response-headers -H "host: www.example.com"Example output:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16... * Mark bundle as not supporting multiuse < HTTP/1.1 200 OK HTTP/1.1 200 OK < access-control-allow-credentials: true access-control-allow-credentials: true < access-control-allow-origin: * access-control-allow-origin: * < content-type: application/json; encoding=utf-8 content-type: application/json; encoding=utf-8 < content-length: 3 content-length: 3 < x-envoy-upstream-service-time: 0 x-envoy-upstream-service-time: 0 < server: envoy server: envoy -
Set up a header modifier that removes the
content-lengthheader from the response. Choose between the HTTPRoute for a Gateway API-native way, or TrafficPolicy for more flexible attachment options such as a gateway-level policy.kubectl apply -f- <<EOF apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: httpbin-headers namespace: httpbin spec: parentRefs: - name: http namespace: kgateway-system hostnames: - headers.example rules: - filters: - type: ResponseHeaderModifier responseHeaderModifier: remove: - content-length backendRefs: - name: httpbin port: 8000 EOFSetting Description spec.parentRefsThe name and namespace of the gateway that serves this HTTPRoute. In this example, you use the httpgateway that was created as part of the get started guide.spec.rules.filters.typeThe type of filter that you want to apply. In this example, the ResponseHeaderModifierfilter is used.spec.rules.filters.responseHeaderModifier.removeThe name of the response header that you want to remove. spec.rules.backendRefsThe backend destination you want to forward traffic to. In this example, all traffic is forwarded to the httpbin app that you set up as part of the get started guide. -
Create an HTTPRoute resource for the route that you want to modify. Note that the example selects the http Gateway that you created before you began.
kubectl apply -f- <<EOF apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: httpbin-headers namespace: httpbin spec: parentRefs: - name: http namespace: kgateway-system hostnames: - headers.example rules: - backendRefs: - name: httpbin port: 8000 EOF -
Create a TrafficPolicy that removes the
content-lengthheader from a response. The following example attaches the TrafficPolicy to the http Gateway.kubectl apply -f- <<EOF apiVersion: gateway.kgateway.dev/v1alpha1 kind: TrafficPolicy metadata: name: httpbin-headers namespace: kgateway-system spec: targetRefs: - group: gateway.networking.k8s.io kind: Gateway name: http headerModifiers: response: remove: - content-length EOF
-
-
Send a request to the httpbin app on the
headers.exampledomain . Verify that thecontent-lengthresponse header is removed.curl -vi http://$INGRESS_GW_ADDRESS:8080/response-headers -H "host: headers.example:8080"curl -vi localhost:8080/response-headers -H "host: headers.example"Example output:
* Mark bundle as not supporting multiuse < HTTP/1.1 200 OK HTTP/1.1 200 OK < access-control-allow-credentials: true access-control-allow-credentials: true < access-control-allow-origin: * access-control-allow-origin: * < content-type: application/json; encoding=utf-8 content-type: application/json; encoding=utf-8 < x-envoy-upstream-service-time: 0 x-envoy-upstream-service-time: 0 < server: envoy server: envoy < transfer-encoding: chunked transfer-encoding: chunked -
Optional: Remove the resources that you created.
kubectl delete httproute httpbin-headers -n httpbinkubectl delete httproute httpbin-headers -n httpbin kubectl delete TrafficPolicy httpbin-headers -n kgateway-system
Source a header value from a Secret
If a response header value is sensitive, such as an audit signing key or a service-to-service token destined for a trusted client, you might not want to commit it to a manifest in plain text. You can source a response header value from a Kubernetes Secret by replacing value with secretRef on a set or add entry in a TrafficPolicy. kgateway resolves the Secret at translation time, so the value never appears in the policy spec. If the Secret changes later, kgateway re-translates the affected policies automatically.
HTTPRoute ResponseHeaderModifier filter does not support secretRef.The same defaulting rules and cross-namespace ReferenceGrant requirement that apply to request headers from a Secret also apply here. The following example shows the basic flow for responses.
-
Create a Secret that holds the value you want to inject. The data keys do not need to match the eventual header names.
kubectl apply -f- <<EOF apiVersion: v1 kind: Secret metadata: name: response-signing namespace: kgateway-system type: Opaque stringData: signing-key: my-response-signing-key EOF -
Create an HTTPRoute for the httpbin app. The example selects the http Gateway that you created before you began.
kubectl apply -f- <<EOF apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: httpbin-headers namespace: httpbin spec: parentRefs: - name: http namespace: kgateway-system hostnames: - headers.example rules: - backendRefs: - name: httpbin port: 8000 EOF -
Create a TrafficPolicy that sets a response header from the
response-signingSecret. The following example attaches the TrafficPolicy to the http Gateway.kubectl apply -f- <<EOF apiVersion: gateway.kgateway.dev/v1alpha1 kind: TrafficPolicy metadata: name: httpbin-secret-response-headers namespace: kgateway-system spec: targetRefs: - group: gateway.networking.k8s.io kind: Gateway name: http headerModifiers: response: set: - name: X-Response-Signature secretRef: name: response-signing key: signing-key EOFThe TrafficPolicy
targetRefsfield does not accept anamespace. The policy must live in the same namespace as the resource it targets. To scope this example to a single HTTPRoute instead of the whole Gateway, changekindtoHTTPRouteandnameto the route’s name, and create the TrafficPolicy (and the Secret) in the route’s namespace.Setting Description headerModifiers.response.set.nameThe HTTP header name that the client receives. headerModifiers.response.set.secretRef.nameThe name of the Kubernetes Secret to read the value from. If the Secret does not exist when the policy is applied, the policy reports Accepted=Falseand the affected route returns a 500 response.headerModifiers.response.set.secretRef.keyThe key in the Secret’s datato use as the header value. Optional. Ifkeyis omitted, it defaults to the value ofheaderModifiers.response.set.name.headerModifiers.response.set.secretRef.namespaceThe namespace of the Secret. Optional. If namespaceis omitted, it defaults to the namespace of the TrafficPolicy. Cross-namespace references require aReferenceGrant. -
Send a request to the httpbin app on the
headers.exampledomain and confirm that the response includes theX-Response-Signatureheader with the value from the Secret.
curl -vi http://$INGRESS_GW_ADDRESS:8080/response-headers -H "host: headers.example:8080"curl -vi localhost:8080/response-headers -H "host: headers.example"Example output:
HTTP/1.1 200 OK
access-control-allow-credentials: true
access-control-allow-origin: *
content-type: application/json; encoding=utf-8
x-envoy-upstream-service-time: 0
x-response-signature: my-response-signing-key
server: envoy- Optional: When you are finished, clean up the resources that you created.
kubectl delete httproute httpbin-headers -n httpbin
kubectl delete TrafficPolicy httpbin-secret-response-headers -n kgateway-system
kubectl delete secret response-signing -n kgateway-systemField defaulting
The name field on a set or add entry and the key field on secretRef are both optional. How kgateway resolves a header value depends on which combination of fields you provide. If the Secret does not contain the key or name data, the policy reports Accepted=False and the affected route returns a 500 response.
name and secretRef.key both set
kgateway sets the name header to the value of the secretRef.key data key in the Secret.
headerModifiers:
response:
set:
- name: X-Response-Signature
secretRef:
name: response-signing
key: signing-keysecretRef.key omitted
kgateway sets the name header to the value of the Secret data key that matches name.
headerModifiers:
response:
set:
- name: X-Response-Signature
secretRef:
name: response-signingname omitted
kgateway sets a header named after secretRef.key to the value of that data key in the Secret.
headerModifiers:
response:
set:
- secretRef:
name: response-signing
key: signing-keyBoth name and secretRef.key omitted
kgateway injects every entry in the Secret as a response header. Each data key becomes a header name. Use this combination to mirror an entire Secret into headers without listing each entry individually.
headerModifiers:
response:
set:
- secretRef:
name: response-signingDynamic response headers
You can return dynamic information about the response in the response header. For more information, see the Envoy docs for Custom request/response headers.
Keep in mind that some variables are available only at certain times. For example, response codes (%RESPONSE_CODE%) are only available after the response has been sent to the client. If you set a response code in a request header, the value is empty.
You might use some of the following common values in your request or response headers.
Request and response information:
- %REQ(:METHOD)% - HTTP method
- %REQ(:PATH)% - Request path
- %REQ(:AUTHORITY)% - Host header
- %REQ(HEADER_NAME)% - Any request header
- %RESP(HEADER_NAME)% - Any response header
- %RESPONSE_CODE% - HTTP response code
- %RESPONSE_FLAGS% - Response flags
Connection information:
- %DOWNSTREAM_REMOTE_ADDRESS% - Client IP address with port
- %DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT% - Client IP address without port
- %DOWNSTREAM_LOCAL_ADDRESS% - Local address
- %DOWNSTREAM_CONNECTION_ID% - Connection ID
Timing information:
- %START_TIME% - Request start time
- %DURATION% - Request duration
Upstream information:
- %UPSTREAM_HOST% - Upstream host
- %UPSTREAM_CLUSTER% - Upstream Envoy cluster
- %UPSTREAM_LOCAL_ADDRESS% - Upstream local address
Data transfer:
- %BYTES_RECEIVED% - Bytes received
- %BYTES_SENT% - Bytes sent
For more potential values, see Command operators in the Envoy docs.
-
Set up a header modifier that sets the
X-Response-Codeheader with the value of the HTTP response code. Choose between the HTTPRoute for a Gateway API-native way, or TrafficPolicy for more flexible attachment options such as a gateway-level policy.kubectl apply -f- <<EOF apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: httpbin-headers namespace: httpbin spec: parentRefs: - name: http namespace: kgateway-system hostnames: - headers.example rules: - filters: - type: ResponseHeaderModifier responseHeaderModifier: set: - name: x-response-code value: "%RESPONSE_CODE%" backendRefs: - name: httpbin port: 8000 EOFSetting Description spec.parentRefsThe name and namespace of the gateway that serves this HTTPRoute. In this example, you use the httpGateway that was created as part of the get started guide.spec.rules.filters.typeThe type of filter that you want to apply to responses. In this example, the ResponseHeaderModifierfilter is used.spec.rules.filters.responseHeaderModifier.setThe response header that you want to set. In this example, the x-response-codeheader is set to the HTTP response code. For more potential values, see Command operators in the Envoy docs.spec.rules.backendRefsThe backend destination you want to forward traffic to. In this example, all traffic is forwarded to the httpbin app that you set up as part of the get started guide. -
Create an HTTPRoute resource for the route that you want to modify. Note that the example selects the http Gateway that you created before you began.
kubectl apply -f- <<EOF apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: httpbin-headers namespace: httpbin spec: parentRefs: - name: http namespace: kgateway-system hostnames: - headers.example rules: - backendRefs: - name: httpbin port: 8000 EOF -
Create a TrafficPolicy that sets the
x-response-codeheader to the HTTP response code. For more potential values, see Command operators in the Envoy docs. The following example attaches the TrafficPolicy to the http Gateway.kubectl apply -f- <<EOF apiVersion: gateway.kgateway.dev/v1alpha1 kind: TrafficPolicy metadata: name: httpbin-headers namespace: kgateway-system spec: targetRefs: - group: gateway.networking.k8s.io kind: Gateway name: http headerModifiers: response: set: - name: x-response-code value: "%RESPONSE_CODE%" EOF
-
-
Send a request to the httpbin app on the
headers.exampledomain. Verify that thex-response-coderesponse header is set to the HTTP response code.curl -vi http://$INGRESS_GW_ADDRESS:8080/response-headers -H "host: headers.example:8080"curl -vi localhost:8080/response-headers -H "host: headers.example"Example output:
HTTP/1.1 200 OK access-control-allow-credentials: true access-control-allow-origin: * content-type: application/json; encoding=utf-8 date: Tue, 23 Sep 2025 20:05:29 GMT content-length: 479 x-envoy-upstream-service-time: 0 x-response-code: 200 server: envoy -
Optional: Clean up the resources that you created.
kubectl delete httproute httpbin-headers -n httpbinkubectl delete httproute httpbin-headers -n httpbin kubectl delete TrafficPolicy httpbin-headers -n kgateway-system