sigs.k8s.io/gateway-api@v1.0.0/geps/gep-1323.md (about) 1 # GEP 1323: Response Header Filter 2 3 * Issue: [#1323](https://github.com/kubernetes-sigs/gateway-api/issues/1323) 4 * Status: Standard 5 6 > **Note**: This GEP is exempt from the [Probationary Period][expprob] rules of 7 > our GEP overview as it existed before those rules did, and so it has been 8 > explicitly grandfathered in. 9 10 [expprob]:https://gateway-api.sigs.k8s.io/geps/overview/#probationary-period 11 12 ## TLDR 13 Similar to how we have `RequestHeaderModifier` in `HTTPRouteFilter`, which lets users modify request headers before the request is forwarded to a backend (or a group of backends), it’d be helpful to have a `ResponseHeaderModifier` field which would let users modify response headers before they are returned to the client. 14 15 ## Goals 16 * Provide a way to modify HTTP response headers in a `HTTPRoute`. 17 * Reuse existing types as much as possible to reduce boilerplate code. 18 19 ## Non Goals 20 * Provide a way to modify other parts of a HTTP response like status code. 21 * Add fields specifically for standard headers such as `Cookie`. 22 23 ## Introduction 24 Currently, the `HTTPRouteFilter` API provides a way for request headers to be modified through the `RequestHeaderModifier` field of type `HTTPRequestHeaderModifier`. But, a similar API to modify response headers does not exist. This proposal intends to introduce a new field in `HTTPRouteFilter` named `ResponseHeaderModifier`. 25 26 ## API 27 We could introduce a new API named `HTTPResponseHeaderModifier` which would look exactly like the existing `HTTPRequestHeaderModifier` API. But since HTTP headers have the same semantics for both requests and responses, it makes more sense to rename `HTTPRequestHeaderModifier` to `HTTPHeaderModifier` and use this for both `RequestHeaderModifier` and `ResponseHeaderModifier`. 28 29 ```golang 30 // HTTPHeaderModifier defines a filter that modifies the headers of a HTTP 31 // request or response. 32 type HTTPHeaderModifier struct { 33 // Set overwrites the request with the given header (name, value) 34 // before the action. 35 // +optional 36 // +listType=map 37 // +listMapKey=name 38 // +kubebuilder:validation:MaxItems=16 39 Set []HTTPHeader `json:"set,omitempty"` 40 41 // Add adds the given header(s) (name, value) to the request 42 // before the action. It appends to any existing values associated 43 // with the header name. 44 45 // +optional 46 // +listType=map 47 // +listMapKey=name 48 // +kubebuilder:validation:MaxItems=16 49 Add []HTTPHeader `json:"add,omitempty"` 50 51 // Remove the given header(s) from the HTTP request before the action. The 52 // value of Remove is a list of HTTP header names. Note that the header 53 // names are case-insensitive (see 54 // https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). 55 // +optional 56 // +kubebuilder:validation:MaxItems=16 57 Remove []string `json:"remove,omitempty"` 58 } 59 ``` 60 61 Given the fact that this functionality is offered by only a few projects that currently implement Gateway API when using their own traffic routing CRDs, it’s better to support `ResponseHeaderModifier` as an _Extended_ feature, unlike `RequestHeaderModifier` which is a _Core_ feature. This will also not increase the difficulty of implementing Gateway API for any future ingress or service mesh. 62 63 This feature can be further extended via [Policy Attachment](../reference/policy-attachment.md). The mechanism and use cases of this may be explored in a future GEP. 64 65 ## Usage 66 Adding support for this unlocks a lot of real world use cases. Let’s review a couple of them: 67 68 * A team has a frontend web app, along with two different versions of their backends exposed as Kubernetes services. If, the frontend needs to know which backend it’s talking to, this can be easily achieved without any modifications to the application code. 69 70 ```yaml 71 apiVersion: gateway.networking.k8s.io/v1beta1 72 kind: HTTPRoute 73 metadata: 74 name: http-response-header 75 spec: 76 hostnames: 77 - response.header.example 78 rules: 79 - backendRefs: 80 - name: example-svc-beta 81 weight: 50 82 port: 80 83 # set a custom header for all responses being sent from the beta build of the backend server. 84 filters: 85 - type: ResponseHeaderModifier 86 responseHeaderModifier: 87 add: 88 name: build 89 value: beta 90 - name: example-svc-stable 91 weight: 50 92 port: 80 93 ``` 94 95 * Cookies can be automatically injected into the response of services. This can enable services to identify users that were redirected to a certain backend. 96 97 ```yaml 98 apiVersion: gateway.networking.k8s.io/v1beta1 99 kind: HTTPRoute 100 metadata: 101 name: http-response-header 102 spec: 103 hostnames: 104 - response.header.example 105 rules: 106 # match against any requests that has the cookie set due to the below rule 107 - matches: 108 - headers: 109 type: Exact 110 name: Cookie 111 value: user=insider 112 backendRefs: 113 - name: foo-svc 114 port: 8080 115 116 - filters: 117 - type: ResponseHeaderModifier 118 # set cookies for all requests being forwarded to this service 119 responseHeaderModifier: 120 set: 121 name: Set-Cookie 122 value: user=insider 123 backendRefs: 124 - name: example-svc 125 weight: 1 126 port: 80 127 ``` 128 129 > Note: Some projects like Envoy support interpolating a few predefined [variables into header values](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_man/headers#custom-request-response-headers). Similar functionality might be supported by other implementations but its unlikely to be portable and thus has been excluded from the API for the time being. 130 131 ## Prior Art 132 A few projects that implement Gateway API already have support for similar functionality (in their custom CRDs), like: 133 * Istio’s `VirtualService`: 134 135 ```yaml 136 apiVersion: networking.istio.io/v1alpha3 137 kind: VirtualService 138 metadata: 139 name: reviews-route 140 spec: 141 hosts: 142 - reviews.prod.svc.cluster.local 143 http: 144 - headers: 145 request: 146 set: 147 test: "true" 148 route: 149 - destination: 150 host: reviews.prod.svc.cluster.local 151 subset: v2 152 weight: 25 153 - destination: 154 host: reviews.prod.svc.cluster.local 155 subset: v1 156 headers: 157 response: 158 remove: 159 - foo 160 weight: 75 161 ``` 162 163 * Contour’s `HTTPProxy`: 164 165 ```yaml 166 apiVersion: projectcontour.io/v1 167 kind: HTTPProxy 168 metadata: 169 name: basic 170 spec: 171 virtualhost: 172 fqdn: foo-basic.bar.com 173 routes: 174 - conditions: 175 - prefix: / 176 services: 177 - name: s1 178 port: 80 179 responseHeadersPolicy: 180 set: 181 name: test 182 value: true 183 ``` 184 185 * Ingress NGINX: 186 187 ```yaml 188 apiVersion: networking.k8s.io/v1 189 kind: Ingress 190 metadata: 191 name: nginx-headers 192 annotations: 193 nginx.ingress.kubernetes.io/configuration-snippet: | 194 add_header ingress nginx; 195 spec: 196 ingressClassName: nginx 197 rules: 198 - host: custom.configuration.com 199 http: 200 paths: 201 - path: / 202 pathType: Prefix 203 backend: 204 service: 205 name: http-svc 206 port: 207 number: 80 208 ``` 209 210 ## External Links 211 * [Contour `HeaderValue`](https://projectcontour.io/docs/v1.22.0/config/api/#projectcontour.io/v1.HeaderValue) 212 * [Istio `Headers`](https://istio.io/latest/docs/reference/config/networking/virtual-service/#Headers) 213 * [Ingress NGINX Configuration Snippets](https://kubernetes.github.io/ingress-nginx/examples/customization/configuration-snippets/) 214 * [NGINX `add_header` directive](https://nginx.org/en/docs/http/ngx_http_headers_module.html#add_header) 215