Repository: envoyproxy/gateway
Pull request #5945 (2025-05-07 18:08:28)

fix: Fixed typo in error message.
**What type of PR is this?**
<!--
Your PR title should be descriptive, and generally start with type that contains a subsystem name with `()` if necessary
and summary followed by a colon. format `chore/docs/api/feat/fix/refactor/style/test: summary`.
Examples:
* "docs: fix grammar error"
* "feat(translator): add new feature"
* "fix: fix xx bug"
* "chore: change ci & build tools etc"
* "api: add xxx fields in ClientTrafficPolicy"

Before raising a PR, please go through this section of the developer guide, https://gateway.envoyproxy.io/contributions/develop/#raising-a-pr
-->
fix: Fixed typo in error message.

<!--
NOTE: If your PR contains any API changes (changes under `/api`), we recommend you to separate these API changes into
a new PR, and we will review the API part first. It will save you lots of implementation time if the API get accepted.
-->

**What this PR does / why we need it**:
I found and fixed a typo in an error message.

**Which issue(s) this PR fixes**:
<!--
*Automatically closes linked issue when PR is merged.
Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.
-->
Fixes #

<!--
For any non-trivial changes, you need to provide a brief description of the changes in the release notes.
Please add the description to the release-notes/current.yaml file and include this file in the PR.
-->
Release Notes: No


codecov[bot]: ## [Codecov](https://app.codecov.io/gh/envoyproxy/gateway/pull/5945?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=envoyproxy) Report
Attention: Patch coverage is `0%` with `1 line` in your changes missing coverage. Please review.
> Project coverage is 65.80%. Comparing base [(`ef50718`)](https://app.codecov.io/gh/envoyproxy/gateway/commit/ef507181e89219c04faf7fbe67e4bcce898b751e?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=envoyproxy) to head [(`77b75ec`)](https://app.codecov.io/gh/envoyproxy/gateway/commit/77b75ec1460e2620fd578604dd3bc9f7b3da3ea1?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=envoyproxy).

| [Files with missing lines](https://app.codecov.io/gh/envoyproxy/gateway/pull/5945?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=envoyproxy) | Patch % | Lines |
|---|---|---|
| [internal/provider/kubernetes/kubernetes.go](https://app.codecov.io/gh/envoyproxy/gateway/pull/5945?src=pr&el=tree&filepath=internal%2Fprovider%2Fkubernetes%2Fkubernetes.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=envoyproxy#diff-aW50ZXJuYWwvcHJvdmlkZXIva3ViZXJuZXRlcy9rdWJlcm5ldGVzLmdv) | 0.00% | [1 Missing :warning: ](https://app.codecov.io/gh/envoyproxy/gateway/pull/5945?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=envoyproxy) |

:x: Your patch check has failed because the patch coverage (0.00%) is below the target coverage (60.00%). You can increase the patch coverage or adjust the [target](https://docs.codecov.com/docs/commit-status#target) coverage.

<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##             main    #5945   +/-   ##
=======================================
  Coverage   65.80%   65.80%           
=======================================
  Files         217      217           
  Lines       35978    35978           
=======================================
+ Hits        23674    23676    +2     
- Misses      10824    10825    +1     
+ Partials     1480     1477    -3     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/envoyproxy/gateway/pull/5945?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=envoyproxy).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=envoyproxy).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>

Patch:
diff --git a/internal/gatewayapi/clienttrafficpolicy.go b/internal/gatewayapi/clienttrafficpolicy.go
index f2585a7dd..843109838 100644
--- a/internal/gatewayapi/clienttrafficpolicy.go
+++ b/internal/gatewayapi/clienttrafficpolicy.go
@@ -952,7 +952,12 @@ func translateEarlyRequestHeaders(headerModifier *gwapiv1.HTTPHeaderFilter) ([]i
 			}
 			// Per Gateway API specification on HTTPHeaderName, : and / are invalid characters in header names
 			if strings.ContainsAny(string(addHeader.Name), "/:") {
-				errs = errors.Join(errs, fmt.Errorf("EarlyRequestHeaders Filter cannot set headers with a '/' or ':' character in them. Header: %q", string(addHeader.Name)))
+				errs = errors.Join(errs, fmt.Errorf("EarlyRequestHeaders cannot add a header with a '/' or ':' character in them. Header: '%q'", string(addHeader.Name)))
+				continue
+			}
+			// Gateway API specification allows only valid value as defined by RFC 7230
+			if !HeaderValueRegexp.MatchString(addHeader.Value) {
+				errs = errors.Join(errs, fmt.Errorf("EarlyRequestHeaders cannot add a header with an invalid value. Header: '%q'", string(addHeader.Name)))
 				continue
 			}
 			// Check if the header is a duplicate
@@ -992,7 +997,12 @@ func translateEarlyRequestHeaders(headerModifier *gwapiv1.HTTPHeaderFilter) ([]i
 			}
 			// Per Gateway API specification on HTTPHeaderName, : and / are invalid characters in header names
 			if strings.ContainsAny(string(setHeader.Name), "/:") {
-				errs = errors.Join(errs, fmt.Errorf("EarlyRequestHeaders cannot set headers with a '/' or ':' character in them. Header: '%s'", string(setHeader.Name)))
+				errs = errors.Join(errs, fmt.Errorf("EarlyRequestHeaders cannot set a header with a '/' or ':' character in them. Header: '%q'", string(setHeader.Name)))
+				continue
+			}
+			// Gateway API specification allows only valid value as defined by RFC 7230
+			if !HeaderValueRegexp.MatchString(setHeader.Value) {
+				errs = errors.Join(errs, fmt.Errorf("EarlyRequestHeaders cannot set a header with an invalid value. Header: '%q'", string(setHeader.Name)))
 				continue
 			}
 
diff --git a/internal/gatewayapi/filters.go b/internal/gatewayapi/filters.go
index 0ca0dcd6f..27c4a93ea 100644
--- a/internal/gatewayapi/filters.go
+++ b/internal/gatewayapi/filters.go
@@ -66,6 +66,9 @@ type HTTPFilterIR struct {
 	ExtensionRefs []*ir.UnstructuredRef
 }
 
+// Header value pattern according to RFC 7230
+var HeaderValueRegexp = regexp.MustCompile(`^[!-~]+([\t ]?[!-~]+)*$`)
+
 // ProcessHTTPFilters translates gateway api http filters to IRs.
 func (t *Translator) ProcessHTTPFilters(parentRef *RouteParentContext,
 	route RouteContext,
@@ -376,6 +379,7 @@ func (t *Translator) processRequestHeaderModifierFilter(
 			emptyFilterConfig = false
 		}
 		for _, addHeader := range headersToAdd {
+
 			emptyFilterConfig = false
 			if addHeader.Name == "" {
 				updateRouteStatusForFilter(
@@ -384,16 +388,27 @@ func (t *Translator) processRequestHeaderModifierFilter(
 				// try to process the rest of the headers and produce a valid config.
 				continue
 			}
+
 			if !isModifiableHeader(string(addHeader.Name)) {
 				updateRouteStatusForFilter(
 					filterContext,
 					fmt.Sprintf(
-						"Header: %q. The RequestHeaderModifier filter cannot set the Host header or headers with a '/' "+
+						"Header: %q. The RequestHeaderModifier filter cannot add the Host header or headers with a '/' "+
 							"or ':' character in them. To modify the Host header use the URLRewrite or the HTTPRouteFilter filter.",
 						string(addHeader.Name)),
 				)
 				continue
 			}
+
+			if !HeaderValueRegexp.MatchString(addHeader.Value) {
+				updateRouteStatusForFilter(
+					filterContext,
+					fmt.Sprintf(
+						"Header: %q. RequestHeaderModifier Filter cannot add a header with an invalid value.",
+						string(addHeader.Name)))
+				continue
+			}
+
 			// Check if the header is a duplicate
 			headerKey := string(addHeader.Name)
 			canAddHeader := true
@@ -443,6 +458,15 @@ func (t *Translator) processRequestHeaderModifierFilter(
 				continue
 			}
 
+			if !HeaderValueRegexp.MatchString(setHeader.Value) {
+				updateRouteStatusForFilter(
+					filterContext,
+					fmt.Sprintf(
+						"Header: %q. RequestHeaderModifier Filter cannot set a header with an invalid value.",
+						string(setHeader.Name)))
+				continue
+			}
+
 			// Check if the header to be set has already been configured
 			headerKey := string(setHeader.Name)
 			canAddHeader := true
@@ -556,6 +580,7 @@ func (t *Translator) processResponseHeaderModifierFilter(
 				// try to process the rest of the headers and produce a valid config.
 				continue
 			}
+
 			if !isModifiableHeader(string(addHeader.Name)) {
 				updateRouteStatusForFilter(
 					filterContext,
@@ -565,6 +590,16 @@ func (t *Translator) processResponseHeaderModifierFilter(
 						string(addHeader.Name)))
 				continue
 			}
+
+			if !HeaderValueRegexp.MatchString(addHeader.Value) {
+				updateRouteStatusForFilter(
+					filterContext,
+					fmt.Sprintf(
+						"Header: %q. ResponseHeaderModifier Filter cannot add a header with an invalid value.",
+						string(addHeader.Name)))
+				continue
+			}
+
 			// Check if the header is a duplicate
 			headerKey := string(addHeader.Name)
 			canAddHeader := true
@@ -613,6 +648,15 @@ func (t *Translator) processResponseHeaderModifierFilter(
 				continue
 			}
 
+			if !HeaderValueRegexp.MatchString(setHeader.Value) {
+				updateRouteStatusForFilter(
+					filterContext,
+					fmt.Sprintf(
+						"Header: %q. ResponseHeaderModifier Filter cannot set a header with an invalid value.",
+						string(setHeader.Name)))
+				continue
+			}
+
 			// Check if the header to be set has already been configured
 			headerKey := string(setHeader.Name)
 			canAddHeader := true
diff --git a/internal/provider/kubernetes/kubernetes.go b/internal/provider/kubernetes/kubernetes.go
index 334671436..ac19c145a 100644
--- a/internal/provider/kubernetes/kubernetes.go
+++ b/internal/provider/kubernetes/kubernetes.go
@@ -138,7 +138,7 @@ func New(ctx context.Context, restCfg *rest.Config, svrCfg *ec.Server, resources
 
 	// Create and register the controllers with the manager.
 	if err := newGatewayAPIController(ctx, mgr, svrCfg, updateHandler.Writer(), resources); err != nil {
-		return nil, fmt.Errorf("failted to create gatewayapi controller: %w", err)
+		return nil, fmt.Errorf("failed to create gatewayapi controller: %w", err)
 	}
 
 	// Add health check health probes.
diff --git a/release-notes/current.yaml b/release-notes/current.yaml
index e4a3d75cd..ea823a6d3 100644
--- a/release-notes/current.yaml
+++ b/release-notes/current.yaml
@@ -11,6 +11,7 @@ new features: |
 
 bug fixes: |
   Fix reference grant from SecurityPolicy to referenced remoteJWKS backend not respected.
+  Added validation for header values.
 
 # Enhancements that improve performance.
 performance improvements: |


Test patch:
diff --git a/internal/gatewayapi/testdata/clienttrafficpolicy-headers-error.in.yaml b/internal/gatewayapi/testdata/clienttrafficpolicy-headers-error.in.yaml
index 3b2331bba..5fb1efb47 100644
--- a/internal/gatewayapi/testdata/clienttrafficpolicy-headers-error.in.yaml
+++ b/internal/gatewayapi/testdata/clienttrafficpolicy-headers-error.in.yaml
@@ -13,13 +13,18 @@ clientTrafficPolicies:
         add:
         - name: ""
           value: "empty"
-        - name: "invalid"
+        - name: "Example/Header/1"
           value: ":/"
+        - name: "example-header-2"
+          value: |
+            multi-line-header-value
         set:
         - name: ""
           value: "empty"
-        - name: "invalid"
+        - name: "Example:Header:3"
           value: ":/"
+        - name: "example-header-4"
+          value: "  invalid"
         remove:
         - ""
     targetRef:
diff --git a/internal/gatewayapi/testdata/clienttrafficpolicy-headers-error.out.yaml b/internal/gatewayapi/testdata/clienttrafficpolicy-headers-error.out.yaml
index abab24926..4a12476ea 100644
--- a/internal/gatewayapi/testdata/clienttrafficpolicy-headers-error.out.yaml
+++ b/internal/gatewayapi/testdata/clienttrafficpolicy-headers-error.out.yaml
@@ -11,15 +11,20 @@ clientTrafficPolicies:
         add:
         - name: ""
           value: empty
-        - name: invalid
+        - name: Example/Header/1
           value: :/
+        - name: example-header-2
+          value: |
+            multi-line-header-value
         remove:
         - ""
         set:
         - name: ""
           value: empty
-        - name: invalid
+        - name: Example:Header:3
           value: :/
+        - name: example-header-4
+          value: '  invalid'
       enableEnvoyHeaders: true
       preserveXRequestID: true
       withUnderscoresAction: Allow
@@ -38,8 +43,13 @@ clientTrafficPolicies:
       - lastTransitionTime: null
         message: |-
           Headers: EarlyRequestHeaders cannot add a header with an empty name
+          EarlyRequestHeaders cannot add a header with a '/' or ':' character in them. Header: '"Example/Header/1"'
+          EarlyRequestHeaders cannot add a header with an invalid value. Header: '"example-header-2"'
           EarlyRequestHeaders cannot set a header with an empty name
-          EarlyRequestHeaders cannot remove a header with an empty name.
+          EarlyRequestHeaders cannot set a header with a '/' or ':' character in them. Header: '"Example:Header:3"'
+          EarlyRequestHeaders cannot set a header with an invalid value. Header: '"example-header-4"'
+          EarlyRequestHeaders cannot remove a header with an empty name
+          EarlyRequestHeaders did not provide valid configuration to add/set/remove any headers.
         reason: Invalid
         status: "False"
         type: Accepted
diff --git a/internal/gatewayapi/testdata/httproute-with-header-filter-empty-header-values.in.yaml b/internal/gatewayapi/testdata/httproute-with-header-filter-empty-header-values.in.yaml
index c965d3296..0037ee554 100644
--- a/internal/gatewayapi/testdata/httproute-with-header-filter-empty-header-values.in.yaml
+++ b/internal/gatewayapi/testdata/httproute-with-header-filter-empty-header-values.in.yaml
@@ -39,7 +39,7 @@ httpRoutes:
         requestHeaderModifier:
           set:
           - name: "example-header-1"
-            value: ""
+            value: "dummy"
           add:
           - name: "example-header-2"
             value: ""
diff --git a/internal/gatewayapi/testdata/httproute-with-header-filter-empty-header-values.out.yaml b/internal/gatewayapi/testdata/httproute-with-header-filter-empty-header-values.out.yaml
index 98c6a40af..ba290a7ff 100644
--- a/internal/gatewayapi/testdata/httproute-with-header-filter-empty-header-values.out.yaml
+++ b/internal/gatewayapi/testdata/httproute-with-header-filter-empty-header-values.out.yaml
@@ -65,7 +65,7 @@ httpRoutes:
             value: ""
           set:
           - name: example-header-1
-            value: ""
+            value: dummy
         type: RequestHeaderModifier
       matches:
       - path:
@@ -74,9 +74,10 @@ httpRoutes:
     parents:
     - conditions:
       - lastTransitionTime: null
-        message: Route is accepted
-        reason: Accepted
-        status: "True"
+        message: 'Header: "example-header-2". RequestHeaderModifier Filter cannot
+          add a header with an invalid value.'
+        reason: UnsupportedValue
+        status: "False"
         type: Accepted
       - lastTransitionTime: null
         message: Resolved all the Object references for the Route
@@ -125,37 +126,6 @@ xdsIR:
         escapedSlashesAction: UnescapeAndRedirect
         mergeSlashes: true
       port: 10080
-      routes:
-      - addRequestHeaders:
-        - append: true
-          name: example-header-2
-          value:
-          - ""
-        - append: false
-          name: example-header-1
-          value:
-          - ""
-        destination:
-          name: httproute/default/httproute-1/rule/0
-          settings:
-          - addressType: IP
-            endpoints:
-            - host: 7.7.7.7
-              port: 8080
-            name: httproute/default/httproute-1/rule/0/backend/0
-            protocol: HTTP
-            weight: 1
-        hostname: gateway.envoyproxy.io
-        isHTTP2: false
-        metadata:
-          kind: HTTPRoute
-          name: httproute-1
-          namespace: default
-        name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io
-        pathMatch:
-          distinct: false
-          name: ""
-          prefix: /
     readyListener:
       address: 0.0.0.0
       ipFamily: IPv4
diff --git a/internal/gatewayapi/testdata/httproute-with-header-filter-invalid-header-values.in.yaml b/internal/gatewayapi/testdata/httproute-with-header-filter-invalid-header-values.in.yaml
new file mode 100644
index 000000000..75d1c1669
--- /dev/null
+++ b/internal/gatewayapi/testdata/httproute-with-header-filter-invalid-header-values.in.yaml
@@ -0,0 +1,47 @@
+gateways:
+- apiVersion: gateway.networking.k8s.io/v1
+  kind: Gateway
+  metadata:
+    namespace: envoy-gateway
+    name: gateway-1
+  spec:
+    gatewayClassName: envoy-gateway-class
+    listeners:
+    - name: http
+      protocol: HTTP
+      port: 80
+      hostname: "*.envoyproxy.io"
+      allowedRoutes:
+        namespaces:
+          from: All
+httpRoutes:
+- apiVersion: gateway.networking.k8s.io/v1
+  kind: HTTPRoute
+  metadata:
+    namespace: default
+    name: httproute-1
+  spec:
+    hostnames:
+    - gateway.envoyproxy.io
+    parentRefs:
+    - namespace: envoy-gateway
+      name: gateway-1
+      sectionName: http
+    rules:
+    - matches:
+      - path:
+          value: "/"
+      backendRefs:
+      - name: service-1
+        port: 8080
+      filters:
+      - type: RequestHeaderModifier
+        requestHeaderModifier:
+          set:
+          - name: "example-header-1"
+            value: |
+              multi-line-header-value
+          add:
+          - name: "example-header-2"
+            value: "dummy"
+
diff --git a/internal/gatewayapi/testdata/httproute-with-header-filter-invalid-header-values.out.yaml b/internal/gatewayapi/testdata/httproute-with-header-filter-invalid-header-values.out.yaml
new file mode 100644
index 000000000..fe02e7a8c
--- /dev/null
+++ b/internal/gatewayapi/testdata/httproute-with-header-filter-invalid-header-values.out.yaml
@@ -0,0 +1,134 @@
+gateways:
+- apiVersion: gateway.networking.k8s.io/v1
+  kind: Gateway
+  metadata:
+    creationTimestamp: null
+    name: gateway-1
+    namespace: envoy-gateway
+  spec:
+    gatewayClassName: envoy-gateway-class
+    listeners:
+    - allowedRoutes:
+        namespaces:
+          from: All
+      hostname: '*.envoyproxy.io'
+      name: http
+      port: 80
+      protocol: HTTP
+  status:
+    listeners:
+    - attachedRoutes: 1
+      conditions:
+      - lastTransitionTime: null
+        message: Sending translated listener configuration to the data plane
+        reason: Programmed
+        status: "True"
+        type: Programmed
+      - lastTransitionTime: null
+        message: Listener has been successfully translated
+        reason: Accepted
+        status: "True"
+        type: Accepted
+      - lastTransitionTime: null
+        message: Listener references have been resolved
+        reason: ResolvedRefs
+        status: "True"
+        type: ResolvedRefs
+      name: http
+      supportedKinds:
+      - group: gateway.networking.k8s.io
+        kind: HTTPRoute
+      - group: gateway.networking.k8s.io
+        kind: GRPCRoute
+httpRoutes:
+- apiVersion: gateway.networking.k8s.io/v1
+  kind: HTTPRoute
+  metadata:
+    creationTimestamp: null
+    name: httproute-1
+    namespace: default
+  spec:
+    hostnames:
+    - gateway.envoyproxy.io
+    parentRefs:
+    - name: gateway-1
+      namespace: envoy-gateway
+      sectionName: http
+    rules:
+    - backendRefs:
+      - name: service-1
+        port: 8080
+      filters:
+      - requestHeaderModifier:
+          add:
+          - name: example-header-2
+            value: dummy
+          set:
+          - name: example-header-1
+            value: |
+              multi-line-header-value
+        type: RequestHeaderModifier
+      matches:
+      - path:
+          value: /
+  status:
+    parents:
+    - conditions:
+      - lastTransitionTime: null
+        message: 'Header: "example-header-1". RequestHeaderModifier Filter cannot
+          set a header with an invalid value.'
+        reason: UnsupportedValue
+        status: "False"
+        type: Accepted
+      - lastTransitionTime: null
+        message: Resolved all the Object references for the Route
+        reason: ResolvedRefs
+        status: "True"
+        type: ResolvedRefs
+      controllerName: gateway.envoyproxy.io/gatewayclass-controller
+      parentRef:
+        name: gateway-1
+        namespace: envoy-gateway
+        sectionName: http
+infraIR:
+  envoy-gateway/gateway-1:
+    proxy:
+      listeners:
+      - address: null
+        name: envoy-gateway/gateway-1/http
+        ports:
+        - containerPort: 10080
+          name: http-80
+          protocol: HTTP
+          servicePort: 80
+      metadata:
+        labels:
+          gateway.envoyproxy.io/owning-gateway-name: gateway-1
+          gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway
+      name: envoy-gateway/gateway-1
+      namespace: envoy-gateway-system
+xdsIR:
+  envoy-gateway/gateway-1:
+    accessLog:
+      json:
+      - path: /dev/stdout
+    http:
+    - address: 0.0.0.0
+      hostnames:
+      - '*.envoyproxy.io'
+      isHTTP2: false
+      metadata:
+        kind: Gateway
+        name: gateway-1
+        namespace: envoy-gateway
+        sectionName: http
+      name: envoy-gateway/gateway-1/http
+      path:
+        escapedSlashesAction: UnescapeAndRedirect
+        mergeSlashes: true
+      port: 10080
+    readyListener:
+      address: 0.0.0.0
+      ipFamily: IPv4
+      path: /ready
+      port: 19003
diff --git a/internal/gatewayapi/testdata/httproute-with-response-header-filter-empty-header-values.in.yaml b/internal/gatewayapi/testdata/httproute-with-response-header-filter-empty-header-values.in.yaml
index 1c0016800..b84f703b6 100644
--- a/internal/gatewayapi/testdata/httproute-with-response-header-filter-empty-header-values.in.yaml
+++ b/internal/gatewayapi/testdata/httproute-with-response-header-filter-empty-header-values.in.yaml
@@ -39,8 +39,7 @@ httpRoutes:
         responseHeaderModifier:
           set:
           - name: "example-header-1"
-            value: ""
+            value: "dummy"
           add:
           - name: "example-header-2"
             value: ""
-
diff --git a/internal/gatewayapi/testdata/httproute-with-response-header-filter-empty-header-values.out.yaml b/internal/gatewayapi/testdata/httproute-with-response-header-filter-empty-header-values.out.yaml
index f89eab6f3..94dae9d73 100644
--- a/internal/gatewayapi/testdata/httproute-with-response-header-filter-empty-header-values.out.yaml
+++ b/internal/gatewayapi/testdata/httproute-with-response-header-filter-empty-header-values.out.yaml
@@ -65,7 +65,7 @@ httpRoutes:
             value: ""
           set:
           - name: example-header-1
-            value: ""
+            value: dummy
         type: ResponseHeaderModifier
       matches:
       - path:
@@ -74,9 +74,10 @@ httpRoutes:
     parents:
     - conditions:
       - lastTransitionTime: null
-        message: Route is accepted
-        reason: Accepted
-        status: "True"
+        message: 'Header: "example-header-2". ResponseHeaderModifier Filter cannot
+          add a header with an invalid value.'
+        reason: UnsupportedValue
+        status: "False"
         type: Accepted
       - lastTransitionTime: null
         message: Resolved all the Object references for the Route
@@ -125,37 +126,6 @@ xdsIR:
         escapedSlashesAction: UnescapeAndRedirect
         mergeSlashes: true
       port: 10080
-      routes:
-      - addResponseHeaders:
-        - append: true
-          name: example-header-2
-          value:
-          - ""
-        - append: false
-          name: example-header-1
-          value:
-          - ""
-        destination:
-          name: httproute/default/httproute-1/rule/0
-          settings:
-          - addressType: IP
-            endpoints:
-            - host: 7.7.7.7
-              port: 8080
-            name: httproute/default/httproute-1/rule/0/backend/0
-            protocol: HTTP
-            weight: 1
-        hostname: gateway.envoyproxy.io
-        isHTTP2: false
-        metadata:
-          kind: HTTPRoute
-          name: httproute-1
-          namespace: default
-        name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io
-        pathMatch:
-          distinct: false
-          name: ""
-          prefix: /
     readyListener:
       address: 0.0.0.0
       ipFamily: IPv4
diff --git a/internal/gatewayapi/testdata/httproute-with-response-header-filter-invalid-header-values.in.yaml b/internal/gatewayapi/testdata/httproute-with-response-header-filter-invalid-header-values.in.yaml
new file mode 100644
index 000000000..802391150
--- /dev/null
+++ b/internal/gatewayapi/testdata/httproute-with-response-header-filter-invalid-header-values.in.yaml
@@ -0,0 +1,46 @@
+gateways:
+- apiVersion: gateway.networking.k8s.io/v1
+  kind: Gateway
+  metadata:
+    namespace: envoy-gateway
+    name: gateway-1
+  spec:
+    gatewayClassName: envoy-gateway-class
+    listeners:
+    - name: http
+      protocol: HTTP
+      port: 80
+      hostname: "*.envoyproxy.io"
+      allowedRoutes:
+        namespaces:
+          from: All
+httpRoutes:
+- apiVersion: gateway.networking.k8s.io/v1
+  kind: HTTPRoute
+  metadata:
+    namespace: default
+    name: httproute-1
+  spec:
+    hostnames:
+    - gateway.envoyproxy.io
+    parentRefs:
+    - namespace: envoy-gateway
+      name: gateway-1
+      sectionName: http
+    rules:
+    - matches:
+      - path:
+          value: "/"
+      backendRefs:
+      - name: service-1
+        port: 8080
+      filters:
+      - type: ResponseHeaderModifier
+        responseHeaderModifier:
+          set:
+          - name: "example-header-1"
+            value: |
+              multi-line-header-value
+          add:
+          - name: "example-header-2"
+            value: "dummy"
diff --git a/internal/gatewayapi/testdata/httproute-with-response-header-filter-invalid-header-values.out.yaml b/internal/gatewayapi/testdata/httproute-with-response-header-filter-invalid-header-values.out.yaml
new file mode 100644
index 000000000..2ceb5619c
--- /dev/null
+++ b/internal/gatewayapi/testdata/httproute-with-response-header-filter-invalid-header-values.out.yaml
@@ -0,0 +1,134 @@
+gateways:
+- apiVersion: gateway.networking.k8s.io/v1
+  kind: Gateway
+  metadata:
+    creationTimestamp: null
+    name: gateway-1
+    namespace: envoy-gateway
+  spec:
+    gatewayClassName: envoy-gateway-class
+    listeners:
+    - allowedRoutes:
+        namespaces:
+          from: All
+      hostname: '*.envoyproxy.io'
+      name: http
+      port: 80
+      protocol: HTTP
+  status:
+    listeners:
+    - attachedRoutes: 1
+      conditions:
+      - lastTransitionTime: null
+        message: Sending translated listener configuration to the data plane
+        reason: Programmed
+        status: "True"
+        type: Programmed
+      - lastTransitionTime: null
+        message: Listener has been successfully translated
+        reason: Accepted
+        status: "True"
+        type: Accepted
+      - lastTransitionTime: null
+        message: Listener references have been resolved
+        reason: ResolvedRefs
+        status: "True"
+        type: ResolvedRefs
+      name: http
+      supportedKinds:
+      - group: gateway.networking.k8s.io
+        kind: HTTPRoute
+      - group: gateway.networking.k8s.io
+        kind: GRPCRoute
+httpRoutes:
+- apiVersion: gateway.networking.k8s.io/v1
+  kind: HTTPRoute
+  metadata:
+    creationTimestamp: null
+    name: httproute-1
+    namespace: default
+  spec:
+    hostnames:
+    - gateway.envoyproxy.io
+    parentRefs:
+    - name: gateway-1
+      namespace: envoy-gateway
+      sectionName: http
+    rules:
+    - backendRefs:
+      - name: service-1
+        port: 8080
+      filters:
+      - responseHeaderModifier:
+          add:
+          - name: example-header-2
+            value: dummy
+          set:
+          - name: example-header-1
+            value: |
+              multi-line-header-value
+        type: ResponseHeaderModifier
+      matches:
+      - path:
+          value: /
+  status:
+    parents:
+    - conditions:
+      - lastTransitionTime: null
+        message: 'Header: "example-header-1". ResponseHeaderModifier Filter cannot
+          set a header with an invalid value.'
+        reason: UnsupportedValue
+        status: "False"
+        type: Accepted
+      - lastTransitionTime: null
+        message: Resolved all the Object references for the Route
+        reason: ResolvedRefs
+        status: "True"
+        type: ResolvedRefs
+      controllerName: gateway.envoyproxy.io/gatewayclass-controller
+      parentRef:
+        name: gateway-1
+        namespace: envoy-gateway
+        sectionName: http
+infraIR:
+  envoy-gateway/gateway-1:
+    proxy:
+      listeners:
+      - address: null
+        name: envoy-gateway/gateway-1/http
+        ports:
+        - containerPort: 10080
+          name: http-80
+          protocol: HTTP
+          servicePort: 80
+      metadata:
+        labels:
+          gateway.envoyproxy.io/owning-gateway-name: gateway-1
+          gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway
+      name: envoy-gateway/gateway-1
+      namespace: envoy-gateway-system
+xdsIR:
+  envoy-gateway/gateway-1:
+    accessLog:
+      json:
+      - path: /dev/stdout
+    http:
+    - address: 0.0.0.0
+      hostnames:
+      - '*.envoyproxy.io'
+      isHTTP2: false
+      metadata:
+        kind: Gateway
+        name: gateway-1
+        namespace: envoy-gateway
+        sectionName: http
+      name: envoy-gateway/gateway-1/http
+      path:
+        escapedSlashesAction: UnescapeAndRedirect
+        mergeSlashes: true
+      port: 10080
+    readyListener:
+      address: 0.0.0.0
+      ipFamily: IPv4
+      path: /ready
+      port: 19003