Repository: envoyproxy/gateway
Pull request #5576 (2025-03-21 21:42:27)

[release/v1.3] Release v1.3.2 cherry-pick
**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
-->

<!--
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**:

**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: Yes/No


codecov[bot]: ## [Codecov](https://app.codecov.io/gh/envoyproxy/gateway/pull/5576?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 `75.73099%` with `83 lines` in your changes missing coverage. Please review.
> Project coverage is 68.08%. Comparing base [(`5847250`)](https://app.codecov.io/gh/envoyproxy/gateway/commit/58472507902d4d8bd65e3143774bc5b809b4cfd5?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=envoyproxy) to head [(`d4bf356`)](https://app.codecov.io/gh/envoyproxy/gateway/commit/d4bf3561cd8449cd5ea002dafaa0b33233926217?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/5576?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/gatewayapi/resource/load.go](https://app.codecov.io/gh/envoyproxy/gateway/pull/5576?src=pr&el=tree&filepath=internal%2Fgatewayapi%2Fresource%2Fload.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=envoyproxy#diff-aW50ZXJuYWwvZ2F0ZXdheWFwaS9yZXNvdXJjZS9sb2FkLmdv) | 0.00% | [26 Missing :warning: ](https://app.codecov.io/gh/envoyproxy/gateway/pull/5576?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=envoyproxy) |
| [internal/gatewayapi/filters.go](https://app.codecov.io/gh/envoyproxy/gateway/pull/5576?src=pr&el=tree&filepath=internal%2Fgatewayapi%2Ffilters.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=envoyproxy#diff-aW50ZXJuYWwvZ2F0ZXdheWFwaS9maWx0ZXJzLmdv) | 81.95% | [24 Missing :warning: ](https://app.codecov.io/gh/envoyproxy/gateway/pull/5576?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=envoyproxy) |
| [internal/wasm/cache.go](https://app.codecov.io/gh/envoyproxy/gateway/pull/5576?src=pr&el=tree&filepath=internal%2Fwasm%2Fcache.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=envoyproxy#diff-aW50ZXJuYWwvd2FzbS9jYWNoZS5nbw==) | 38.46% | [14 Missing and 2 partials :warning: ](https://app.codecov.io/gh/envoyproxy/gateway/pull/5576?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=envoyproxy) |
| [internal/wasm/premissioncache.go](https://app.codecov.io/gh/envoyproxy/gateway/pull/5576?src=pr&el=tree&filepath=internal%2Fwasm%2Fpremissioncache.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=envoyproxy#diff-aW50ZXJuYWwvd2FzbS9wcmVtaXNzaW9uY2FjaGUuZ28=) | 86.88% | [14 Missing and 2 partials :warning: ](https://app.codecov.io/gh/envoyproxy/gateway/pull/5576?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=envoyproxy) |
| [internal/provider/kubernetes/controller.go](https://app.codecov.io/gh/envoyproxy/gateway/pull/5576?src=pr&el=tree&filepath=internal%2Fprovider%2Fkubernetes%2Fcontroller.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=envoyproxy#diff-aW50ZXJuYWwvcHJvdmlkZXIva3ViZXJuZXRlcy9jb250cm9sbGVyLmdv) | 0.00% | [1 Missing :warning: ](https://app.codecov.io/gh/envoyproxy/gateway/pull/5576?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=envoyproxy) |

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


```diff
@@               Coverage Diff                @@
##           release/v1.3    #5576      +/-   ##
================================================
+ Coverage         67.94%   68.08%   +0.13%     
================================================
  Files               210      211       +1     
  Lines             33135    33164      +29     
================================================
+ Hits              22515    22580      +65     
+ Misses             9264     9233      -31     
+ Partials           1356     1351       -5     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/envoyproxy/gateway/pull/5576?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/VERSION b/VERSION
index 757407982..968e750a6 100644
--- a/VERSION
+++ b/VERSION
@@ -1,1 +1,1 @@
-v1.3.1
+v1.3.2
diff --git a/api/v1alpha1/shared_types.go b/api/v1alpha1/shared_types.go
index 21774dd38..b1ee8047e 100644
--- a/api/v1alpha1/shared_types.go
+++ b/api/v1alpha1/shared_types.go
@@ -22,7 +22,7 @@ const (
 	// DefaultDeploymentMemoryResourceRequests for deployment memory resource
 	DefaultDeploymentMemoryResourceRequests = "512Mi"
 	// DefaultEnvoyProxyImage is the default image used by envoyproxy
-	DefaultEnvoyProxyImage = "docker.io/envoyproxy/envoy:distroless-v1.33.0"
+	DefaultEnvoyProxyImage = "docker.io/envoyproxy/envoy:distroless-v1.33.1"
 	// DefaultShutdownManagerCPUResourceRequests for shutdown manager cpu resource
 	DefaultShutdownManagerCPUResourceRequests = "10m"
 	// DefaultShutdownManagerMemoryResourceRequests for shutdown manager memory resource
@@ -30,7 +30,7 @@ const (
 	// DefaultShutdownManagerImage is the default image used for the shutdown manager.
 	DefaultShutdownManagerImage = "docker.io/envoyproxy/gateway-dev:latest"
 	// DefaultRateLimitImage is the default image used by ratelimit.
-	DefaultRateLimitImage = "docker.io/envoyproxy/ratelimit:ae4cee11"
+	DefaultRateLimitImage = "docker.io/envoyproxy/ratelimit:0141a24f"
 	// HTTPProtocol is the common-used http protocol.
 	HTTPProtocol = "http"
 	// GRPCProtocol is the common-used grpc protocol.
diff --git a/charts/gateway-helm/README.md b/charts/gateway-helm/README.md
index 909b1d764..b9ff059da 100644
--- a/charts/gateway-helm/README.md
+++ b/charts/gateway-helm/README.md
@@ -102,7 +102,7 @@ To uninstall the chart:
 | global.images.envoyGateway.image | string | `nil` |  |
 | global.images.envoyGateway.pullPolicy | string | `nil` |  |
 | global.images.envoyGateway.pullSecrets | list | `[]` |  |
-| global.images.ratelimit.image | string | `"docker.io/envoyproxy/ratelimit:ae4cee11"` |  |
+| global.images.ratelimit.image | string | `"docker.io/envoyproxy/ratelimit:0141a24f"` |  |
 | global.images.ratelimit.pullPolicy | string | `"IfNotPresent"` |  |
 | global.images.ratelimit.pullSecrets | list | `[]` |  |
 | kubernetesClusterDomain | string | `"cluster.local"` |  |
diff --git a/charts/gateway-helm/values.tmpl.yaml b/charts/gateway-helm/values.tmpl.yaml
index a07560fc1..90ffd4492 100644
--- a/charts/gateway-helm/values.tmpl.yaml
+++ b/charts/gateway-helm/values.tmpl.yaml
@@ -12,7 +12,7 @@ global:
       pullSecrets: []
     ratelimit:
       # This is the full image name including the hub, repo, and tag.
-      image: "docker.io/envoyproxy/ratelimit:ae4cee11"
+      image: "docker.io/envoyproxy/ratelimit:0141a24f"
       # Specify image pull policy if default behavior isn't desired.
       # Default behavior: latest images will be Always else IfNotPresent.
       pullPolicy: IfNotPresent
diff --git a/go.mod b/go.mod
index bbb1ff92c..ff70a2a48 100644
--- a/go.mod
+++ b/go.mod
@@ -9,6 +9,7 @@ require (
 	fortio.org/log v1.17.1
 	github.com/Masterminds/semver/v3 v3.3.1
 	github.com/andybalholm/brotli v1.1.1
+	github.com/avast/retry-go v3.0.0+incompatible
 	github.com/cenkalti/backoff/v4 v4.3.0
 	github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78
 	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
diff --git a/go.sum b/go.sum
index ffb75d668..d23202d25 100644
--- a/go.sum
+++ b/go.sum
@@ -99,6 +99,8 @@ github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:l
 github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
 github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
 github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
+github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0=
+github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY=
 github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
 github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
diff --git a/internal/gatewayapi/filters.go b/internal/gatewayapi/filters.go
index 06e1ce3cd..655a133b7 100644
--- a/internal/gatewayapi/filters.go
+++ b/internal/gatewayapi/filters.go
@@ -192,15 +192,9 @@ func (t *Translator) processURLRewriteFilter(
 	if filterContext.URLRewrite != nil {
 		if hasMultipleCoreRewrites(rewrite, filterContext.URLRewrite) ||
 			hasConflictingCoreAndExtensionRewrites(rewrite, filterContext.URLRewrite) {
-			routeStatus := GetRouteStatus(filterContext.Route)
-			status.SetRouteStatusCondition(routeStatus,
-				filterContext.ParentRef.routeParentStatusIdx,
-				filterContext.Route.GetGeneration(),
-				gwapiv1.RouteConditionAccepted,
-				metav1.ConditionFalse,
-				gwapiv1.RouteReasonUnsupportedValue,
-				"Cannot configure multiple urlRewrite filters for a single HTTPRouteRule",
-			)
+			updateRouteStatusForFilter(
+				filterContext,
+				"Cannot configure multiple urlRewrite filters for a single HTTPRouteRule")
 			return
 		}
 	}
@@ -213,15 +207,7 @@ func (t *Translator) processURLRewriteFilter(
 
 	if rewrite.Hostname != nil {
 		if err := t.validateHostname(string(*rewrite.Hostname)); err != nil {
-			routeStatus := GetRouteStatus(filterContext.Route)
-			status.SetRouteStatusCondition(routeStatus,
-				filterContext.ParentRef.routeParentStatusIdx,
-				filterContext.Route.GetGeneration(),
-				gwapiv1.RouteConditionAccepted,
-				metav1.ConditionFalse,
-				gwapiv1.RouteReasonUnsupportedValue,
-				err.Error(),
-			)
+			updateRouteStatusForFilter(filterContext, err.Error())
 			return
 		}
 		redirectHost := string(*rewrite.Hostname)
@@ -234,29 +220,15 @@ func (t *Translator) processURLRewriteFilter(
 		switch rewrite.Path.Type {
 		case gwapiv1.FullPathHTTPPathModifier:
 			if rewrite.Path.ReplacePrefixMatch != nil {
-				errMsg := "ReplacePrefixMatch cannot be set when rewrite path type is \"ReplaceFullPath\""
-				routeStatus := GetRouteStatus(filterContext.Route)
-				status.SetRouteStatusCondition(routeStatus,
-					filterContext.ParentRef.routeParentStatusIdx,
-					filterContext.Route.GetGeneration(),
-					gwapiv1.RouteConditionAccepted,
-					metav1.ConditionFalse,
-					gwapiv1.RouteReasonUnsupportedValue,
-					errMsg,
-				)
+				updateRouteStatusForFilter(
+					filterContext,
+					"ReplacePrefixMatch cannot be set when rewrite path type is \"ReplaceFullPath\"")
 				return
 			}
 			if rewrite.Path.ReplaceFullPath == nil {
-				errMsg := "ReplaceFullPath must be set when rewrite path type is \"ReplaceFullPath\""
-				routeStatus := GetRouteStatus(filterContext.Route)
-				status.SetRouteStatusCondition(routeStatus,
-					filterContext.ParentRef.routeParentStatusIdx,
-					filterContext.Route.GetGeneration(),
-					gwapiv1.RouteConditionAccepted,
-					metav1.ConditionFalse,
-					gwapiv1.RouteReasonUnsupportedValue,
-					errMsg,
-				)
+				updateRouteStatusForFilter(
+					filterContext,
+					"ReplaceFullPath must be set when rewrite path type is \"ReplaceFullPath\"")
 				return
 			}
 			if rewrite.Path.ReplaceFullPath != nil {
@@ -268,29 +240,15 @@ func (t *Translator) processURLRewriteFilter(
 			}
 		case gwapiv1.PrefixMatchHTTPPathModifier:
 			if rewrite.Path.ReplaceFullPath != nil {
-				errMsg := "ReplaceFullPath cannot be set when rewrite path type is \"ReplacePrefixMatch\""
-				routeStatus := GetRouteStatus(filterContext.Route)
-				status.SetRouteStatusCondition(routeStatus,
-					filterContext.ParentRef.routeParentStatusIdx,
-					filterContext.Route.GetGeneration(),
-					gwapiv1.RouteConditionAccepted,
-					metav1.ConditionFalse,
-					gwapiv1.RouteReasonUnsupportedValue,
-					errMsg,
-				)
+				updateRouteStatusForFilter(
+					filterContext,
+					"ReplaceFullPath cannot be set when rewrite path type is \"ReplacePrefixMatch\"")
 				return
 			}
 			if rewrite.Path.ReplacePrefixMatch == nil {
-				errMsg := "ReplacePrefixMatch must be set when rewrite path type is \"ReplacePrefixMatch\""
-				routeStatus := GetRouteStatus(filterContext.Route)
-				status.SetRouteStatusCondition(routeStatus,
-					filterContext.ParentRef.routeParentStatusIdx,
-					filterContext.Route.GetGeneration(),
-					gwapiv1.RouteConditionAccepted,
-					metav1.ConditionFalse,
-					gwapiv1.RouteReasonUnsupportedValue,
-					errMsg,
-				)
+				updateRouteStatusForFilter(
+					filterContext,
+					"ReplacePrefixMatch must be set when rewrite path type is \"ReplacePrefixMatch\"")
 				return
 			}
 			if rewrite.Path.ReplacePrefixMatch != nil {
@@ -301,16 +259,11 @@ func (t *Translator) processURLRewriteFilter(
 				}
 			}
 		default:
-			errMsg := fmt.Sprintf("Rewrite path type: %s is invalid, only \"ReplaceFullPath\" and \"ReplacePrefixMatch\" are supported", rewrite.Path.Type)
-			routeStatus := GetRouteStatus(filterContext.Route)
-			status.SetRouteStatusCondition(routeStatus,
-				filterContext.ParentRef.routeParentStatusIdx,
-				filterContext.Route.GetGeneration(),
-				gwapiv1.RouteConditionAccepted,
-				metav1.ConditionFalse,
-				gwapiv1.RouteReasonUnsupportedValue,
-				errMsg,
-			)
+			updateRouteStatusForFilter(
+				filterContext,
+				fmt.Sprintf(
+					"Rewrite path type: %s is invalid, only \"ReplaceFullPath\" and \"ReplacePrefixMatch\" are supported",
+					rewrite.Path.Type))
 			return
 		}
 	}
@@ -324,15 +277,9 @@ func (t *Translator) processRedirectFilter(
 ) {
 	// Can't have two redirects for the same route
 	if filterContext.RedirectResponse != nil {
-		routeStatus := GetRouteStatus(filterContext.Route)
-		status.SetRouteStatusCondition(routeStatus,
-			filterContext.ParentRef.routeParentStatusIdx,
-			filterContext.Route.GetGeneration(),
-			gwapiv1.RouteConditionAccepted,
-			metav1.ConditionFalse,
-			gwapiv1.RouteReasonUnsupportedValue,
-			"Cannot configure multiple requestRedirect filters for a single HTTPRouteRule",
-		)
+		updateRouteStatusForFilter(
+			filterContext,
+			"Cannot configure multiple requestRedirect filters for a single HTTPRouteRule")
 		return
 	}
 
@@ -347,31 +294,16 @@ func (t *Translator) processRedirectFilter(
 		if *redirect.Scheme == "http" || *redirect.Scheme == "https" {
 			redir.Scheme = redirect.Scheme
 		} else {
-			errMsg := fmt.Sprintf("Scheme: %s is unsupported, only 'https' and 'http' are supported", *redirect.Scheme)
-			routeStatus := GetRouteStatus(filterContext.Route)
-			status.SetRouteStatusCondition(routeStatus,
-				filterContext.ParentRef.routeParentStatusIdx,
-				filterContext.Route.GetGeneration(),
-				gwapiv1.RouteConditionAccepted,
-				metav1.ConditionFalse,
-				gwapiv1.RouteReasonUnsupportedValue,
-				errMsg,
-			)
+			updateRouteStatusForFilter(
+				filterContext,
+				fmt.Sprintf("Scheme: %s is unsupported, only 'https' and 'http' are supported", *redirect.Scheme))
 			return
 		}
 	}
 
 	if redirect.Hostname != nil {
 		if err := t.validateHostname(string(*redirect.Hostname)); err != nil {
-			routeStatus := GetRouteStatus(filterContext.Route)
-			status.SetRouteStatusCondition(routeStatus,
-				filterContext.ParentRef.routeParentStatusIdx,
-				filterContext.Route.GetGeneration(),
-				gwapiv1.RouteConditionAccepted,
-				metav1.ConditionFalse,
-				gwapiv1.RouteReasonUnsupportedValue,
-				err.Error(),
-			)
+			updateRouteStatusForFilter(filterContext, err.Error())
 		} else {
 			redirectHost := string(*redirect.Hostname)
 			redir.Hostname = &redirectHost
@@ -393,16 +325,11 @@ func (t *Translator) processRedirectFilter(
 				}
 			}
 		default:
-			errMsg := fmt.Sprintf("Redirect path type: %s is invalid, only \"ReplaceFullPath\" and \"ReplacePrefixMatch\" are supported", redirect.Path.Type)
-			routeStatus := GetRouteStatus(filterContext.Route)
-			status.SetRouteStatusCondition(routeStatus,
-				filterContext.ParentRef.routeParentStatusIdx,
-				filterContext.Route.GetGeneration(),
-				gwapiv1.RouteConditionAccepted,
-				metav1.ConditionFalse,
-				gwapiv1.RouteReasonUnsupportedValue,
-				errMsg,
-			)
+			updateRouteStatusForFilter(
+				filterContext,
+				fmt.Sprintf(
+					"Redirect path type: %s is invalid, only \"ReplaceFullPath\" and \"ReplacePrefixMatch\" are supported",
+					redirect.Path.Type))
 			return
 		}
 	}
@@ -414,15 +341,7 @@ func (t *Translator) processRedirectFilter(
 			redir.StatusCode = &redirectCode
 		} else {
 			errMsg := fmt.Sprintf("Status code %d is invalid, only 302 and 301 are supported", redirectCode)
-			routeStatus := GetRouteStatus(filterContext.Route)
-			status.SetRouteStatusCondition(routeStatus,
-				filterContext.ParentRef.routeParentStatusIdx,
-				filterContext.Route.GetGeneration(),
-				gwapiv1.RouteConditionAccepted,
-				metav1.ConditionFalse,
-				gwapiv1.RouteReasonUnsupportedValue,
-				errMsg,
-			)
+			updateRouteStatusForFilter(filterContext, errMsg)
 			return
 		}
 	}
@@ -453,28 +372,19 @@ func (t *Translator) processRequestHeaderModifierFilter(
 		for _, addHeader := range headersToAdd {
 			emptyFilterConfig = false
 			if addHeader.Name == "" {
-				routeStatus := GetRouteStatus(filterContext.Route)
-				status.SetRouteStatusCondition(routeStatus,
-					filterContext.ParentRef.routeParentStatusIdx,
-					filterContext.Route.GetGeneration(),
-					gwapiv1.RouteConditionAccepted,
-					metav1.ConditionFalse,
-					gwapiv1.RouteReasonUnsupportedValue,
-					"RequestHeaderModifier Filter cannot add a header with an empty name",
-				)
+				updateRouteStatusForFilter(
+					filterContext,
+					"RequestHeaderModifier Filter cannot add a header with an empty name")
 				// try to process the rest of the headers and produce a valid config.
 				continue
 			}
-			// Per Gateway API specification on HTTPHeaderName, : and / are invalid characters in header names
-			if strings.Contains(string(addHeader.Name), "/") || strings.Contains(string(addHeader.Name), ":") {
-				routeStatus := GetRouteStatus(filterContext.Route)
-				status.SetRouteStatusCondition(routeStatus,
-					filterContext.ParentRef.routeParentStatusIdx,
-					filterContext.Route.GetGeneration(),
-					gwapiv1.RouteConditionAccepted,
-					metav1.ConditionFalse,
-					gwapiv1.RouteReasonUnsupportedValue,
-					fmt.Sprintf("RequestHeaderModifier Filter cannot set headers with a '/' or ':' character in them. Header: %q", string(addHeader.Name)),
+			if !isModifiableHeader(string(addHeader.Name)) {
+				updateRouteStatusForFilter(
+					filterContext,
+					fmt.Sprintf(
+						"Header: %q. The RequestHeaderModifier filter cannot set 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
 			}
@@ -510,27 +420,19 @@ func (t *Translator) processRequestHeaderModifierFilter(
 		for _, setHeader := range headersToSet {
 
 			if setHeader.Name == "" {
-				routeStatus := GetRouteStatus(filterContext.Route)
-				status.SetRouteStatusCondition(routeStatus,
-					filterContext.ParentRef.routeParentStatusIdx,
-					filterContext.Route.GetGeneration(),
-					gwapiv1.RouteConditionAccepted,
-					metav1.ConditionFalse,
-					gwapiv1.RouteReasonUnsupportedValue,
-					"RequestHeaderModifier Filter cannot set a header with an empty name",
-				)
+				updateRouteStatusForFilter(
+					filterContext,
+					"RequestHeaderModifier Filter cannot set a header with an empty name")
 				continue
 			}
-			// Per Gateway API specification on HTTPHeaderName, : and / are invalid characters in header names
-			if strings.Contains(string(setHeader.Name), "/") || strings.Contains(string(setHeader.Name), ":") {
-				routeStatus := GetRouteStatus(filterContext.Route)
-				status.SetRouteStatusCondition(routeStatus,
-					filterContext.ParentRef.routeParentStatusIdx,
-					filterContext.Route.GetGeneration(),
-					gwapiv1.RouteConditionAccepted,
-					metav1.ConditionFalse,
-					gwapiv1.RouteReasonUnsupportedValue,
-					fmt.Sprintf("RequestHeaderModifier Filter cannot set headers with a '/' or ':' character in them. Header: '%s'", string(setHeader.Name)),
+
+			if !isModifiableHeader(string(setHeader.Name)) {
+				updateRouteStatusForFilter(
+					filterContext,
+					fmt.Sprintf(
+						"Header: %q. The RequestHeaderModifier filter cannot set the Host header or headers with a '/' "+
+							"or ':' character in them. To modify the Host header use the URLRewrite or the HTTPRouteFilter filter.",
+						string(setHeader.Name)),
 				)
 				continue
 			}
@@ -566,14 +468,19 @@ func (t *Translator) processRequestHeaderModifierFilter(
 		}
 		for _, removedHeader := range headersToRemove {
 			if removedHeader == "" {
-				routeStatus := GetRouteStatus(filterContext.Route)
-				status.SetRouteStatusCondition(routeStatus,
-					filterContext.ParentRef.routeParentStatusIdx,
-					filterContext.Route.GetGeneration(),
-					gwapiv1.RouteConditionAccepted,
-					metav1.ConditionFalse,
-					gwapiv1.RouteReasonUnsupportedValue,
-					"RequestHeaderModifier Filter cannot remove a header with an empty name",
+				updateRouteStatusForFilter(
+					filterContext,
+					"RequestHeaderModifier Filter cannot remove a header with an empty name")
+				continue
+			}
+
+			if !isModifiableHeader(removedHeader) {
+				updateRouteStatusForFilter(
+					filterContext,
+					fmt.Sprintf(
+						"Header: %q. The RequestHeaderModifier filter cannot remove the Host header or headers with a '/' "+
+							"or ':' character in them.",
+						removedHeader),
 				)
 				continue
 			}
@@ -595,18 +502,30 @@ func (t *Translator) processRequestHeaderModifierFilter(
 
 	// Update the status if the filter failed to configure any valid headers to add/remove
 	if len(filterContext.AddRequestHeaders) == 0 && len(filterContext.RemoveRequestHeaders) == 0 && !emptyFilterConfig {
-		routeStatus := GetRouteStatus(filterContext.Route)
-		status.SetRouteStatusCondition(routeStatus,
-			filterContext.ParentRef.routeParentStatusIdx,
-			filterContext.Route.GetGeneration(),
-			gwapiv1.RouteConditionAccepted,
-			metav1.ConditionFalse,
-			gwapiv1.RouteReasonUnsupportedValue,
-			"RequestHeaderModifier Filter did not provide valid configuration to add/set/remove any headers",
-		)
+		updateRouteStatusForFilter(
+			filterContext,
+			"RequestHeaderModifier Filter did not provide valid configuration to add/set/remove any headers")
 	}
 }
 
+func updateRouteStatusForFilter(filterContext *HTTPFiltersContext, message string) {
+	routeStatus := GetRouteStatus(filterContext.Route)
+	status.SetRouteStatusCondition(routeStatus,
+		filterContext.ParentRef.routeParentStatusIdx,
+		filterContext.Route.GetGeneration(),
+		gwapiv1.RouteConditionAccepted,
+		metav1.ConditionFalse,
+		gwapiv1.RouteReasonUnsupportedValue,
+		message,
+	)
+}
+
+func isModifiableHeader(headerName string) bool {
+	// Per Gateway API specification on HTTPHeaderName, : and / are invalid characters in header names
+	// And Envoy does not allow modification the pseudo headers and the host header
+	return !strings.Contains(headerName, "/") && !strings.Contains(headerName, ":") && !strings.EqualFold(headerName, "host")
+}
+
 func (t *Translator) processResponseHeaderModifierFilter(
 	headerModifier *gwapiv1.HTTPHeaderFilter,
 	filterContext *HTTPFiltersContext,
@@ -625,29 +544,19 @@ func (t *Translator) processResponseHeaderModifierFilter(
 		for _, addHeader := range headersToAdd {
 			emptyFilterConfig = false
 			if addHeader.Name == "" {
-				routeStatus := GetRouteStatus(filterContext.Route)
-				status.SetRouteStatusCondition(routeStatus,
-					filterContext.ParentRef.routeParentStatusIdx,
-					filterContext.Route.GetGeneration(),
-					gwapiv1.RouteConditionAccepted,
-					metav1.ConditionFalse,
-					gwapiv1.RouteReasonUnsupportedValue,
-					"ResponseHeaderModifier Filter cannot add a header with an empty name",
-				)
+				updateRouteStatusForFilter(
+					filterContext,
+					"ResponseHeaderModifier Filter cannot add a header with an empty name")
 				// try to process the rest of the headers and produce a valid config.
 				continue
 			}
-			// Per Gateway API specification on HTTPHeaderName, : and / are invalid characters in header names
-			if strings.Contains(string(addHeader.Name), "/") || strings.Contains(string(addHeader.Name), ":") {
-				routeStatus := GetRouteStatus(filterContext.Route)
-				status.SetRouteStatusCondition(routeStatus,
-					filterContext.ParentRef.routeParentStatusIdx,
-					filterContext.Route.GetGeneration(),
-					gwapiv1.RouteConditionAccepted,
-					metav1.ConditionFalse,
-					gwapiv1.RouteReasonUnsupportedValue,
-					fmt.Sprintf("ResponseHeaderModifier Filter cannot set headers with a '/' or ':' character in them. Header: %q", string(addHeader.Name)),
-				)
+			if !isModifiableHeader(string(addHeader.Name)) {
+				updateRouteStatusForFilter(
+					filterContext,
+					fmt.Sprintf(
+						"Header: %q. The ResponseHeaderModifier filter cannot set the Host header or headers with a '/' "+
+							"or ':' character in them.",
+						string(addHeader.Name)))
 				continue
 			}
 			// Check if the header is a duplicate
@@ -682,28 +591,19 @@ func (t *Translator) processResponseHeaderModifierFilter(
 		for _, setHeader := range headersToSet {
 
 			if setHeader.Name == "" {
-				routeStatus := GetRouteStatus(filterContext.Route)
-				status.SetRouteStatusCondition(routeStatus,
-					filterContext.ParentRef.routeParentStatusIdx,
-					filterContext.Route.GetGeneration(),
-					gwapiv1.RouteConditionAccepted,
-					metav1.ConditionFalse,
-					gwapiv1.RouteReasonUnsupportedValue,
-					"ResponseHeaderModifier Filter cannot set a header with an empty name",
-				)
+				updateRouteStatusForFilter(
+					filterContext,
+					"ResponseHeaderModifier Filter cannot set a header with an empty name")
 				continue
 			}
-			// Per Gateway API specification on HTTPHeaderName, : and / are invalid characters in header names
-			if strings.Contains(string(setHeader.Name), "/") || strings.Contains(string(setHeader.Name), ":") {
-				routeStatus := GetRouteStatus(filterContext.Route)
-				status.SetRouteStatusCondition(routeStatus,
-					filterContext.ParentRef.routeParentStatusIdx,
-					filterContext.Route.GetGeneration(),
-					gwapiv1.RouteConditionAccepted,
-					metav1.ConditionFalse,
-					gwapiv1.RouteReasonUnsupportedValue,
-					fmt.Sprintf("ResponseHeaderModifier Filter cannot set headers with a '/' or ':' character in them. Header: '%s'", string(setHeader.Name)),
-				)
+
+			if !isModifiableHeader(string(setHeader.Name)) {
+				updateRouteStatusForFilter(
+					filterContext,
+					fmt.Sprintf(
+						"Header: %q. The ResponseHeaderModifier filter cannot set the Host header or headers with a '/' "+
+							"or ':' character in them.",
+						string(setHeader.Name)))
 				continue
 			}
 
@@ -738,15 +638,18 @@ func (t *Translator) processResponseHeaderModifierFilter(
 		}
 		for _, removedHeader := range headersToRemove {
 			if removedHeader == "" {
-				routeStatus := GetRouteStatus(filterContext.Route)
-				status.SetRouteStatusCondition(routeStatus,
-					filterContext.ParentRef.routeParentStatusIdx,
-					filterContext.Route.GetGeneration(),
-					gwapiv1.RouteConditionAccepted,
-					metav1.ConditionFalse,
-					gwapiv1.RouteReasonUnsupportedValue,
-					"ResponseHeaderModifier Filter cannot remove a header with an empty name",
-				)
+				updateRouteStatusForFilter(
+					filterContext,
+					"ResponseHeaderModifier Filter cannot remove a header with an empty name")
+				continue
+			}
+			if !isModifiableHeader(removedHeader) {
+				updateRouteStatusForFilter(
+					filterContext,
+					fmt.Sprintf(
+						"Header: %q. The ResponseHeaderModifier filter cannot remove the Host header or headers with a '/' "+
+							"or ':' character in them.",
+						removedHeader))
 				continue
 			}
 
@@ -768,15 +671,9 @@ func (t *Translator) processResponseHeaderModifierFilter(
 
 	// Update the status if the filter failed to configure any valid headers to add/remove
 	if len(filterContext.AddResponseHeaders) == 0 && len(filterContext.RemoveResponseHeaders) == 0 && !emptyFilterConfig {
-		routeStatus := GetRouteStatus(filterContext.Route)
-		status.SetRouteStatusCondition(routeStatus,
-			filterContext.ParentRef.routeParentStatusIdx,
-			filterContext.Route.GetGeneration(),
-			gwapiv1.RouteConditionAccepted,
-			metav1.ConditionFalse,
-			gwapiv1.RouteReasonUnsupportedValue,
-			"ResponseHeaderModifier Filter did not provide valid configuration to add/set/remove any headers",
-		)
+		updateRouteStatusForFilter(
+			filterContext,
+			"ResponseHeaderModifier Filter did not provide valid configuration to add/set/remove any headers")
 	}
 }
 
@@ -798,15 +695,9 @@ func (t *Translator) processExtensionRefHTTPFilter(extFilter *gwapiv1.LocalObjec
 					if filterContext.URLRewrite != nil {
 						if hasMultipleExtensionRewrites(hrf.Spec.URLRewrite, filterContext.URLRewrite) ||
 							hasConflictingExtensionAndCoreRewrites(hrf.Spec.URLRewrite, filterContext.URLRewrite) {
-							routeStatus := GetRouteStatus(filterContext.Route)
-							status.SetRouteStatusCondition(routeStatus,
-								filterContext.ParentRef.routeParentStatusIdx,
-								filterContext.Route.GetGeneration(),
-								gwapiv1.RouteConditionAccepted,
-								metav1.ConditionFalse,
-								gwapiv1.RouteReasonUnsupportedValue,
-								"Cannot configure multiple urlRewrite filters for a single HTTPRouteRule",
-							)
+							updateRouteStatusForFilter(
+								filterContext,
+								"Cannot configure multiple urlRewrite filters for a single HTTPRouteRule")
 							return
 						}
 					}
@@ -815,30 +706,16 @@ func (t *Translator) processExtensionRefHTTPFilter(extFilter *gwapiv1.LocalObjec
 						if hrf.Spec.URLRewrite.Path.Type == egv1a1.RegexHTTPPathModifier {
 							if hrf.Spec.URLRewrite.Path.ReplaceRegexMatch == nil ||
 								hrf.Spec.URLRewrite.Path.ReplaceRegexMatch.Pattern == "" {
-								errMsg := "ReplaceRegexMatch Pattern must be set when rewrite path type is \"ReplaceRegexMatch\""
-								routeStatus := GetRouteStatus(filterContext.Route)
-								status.SetRouteStatusCondition(routeStatus,
-									filterContext.ParentRef.routeParentStatusIdx,
-									filterContext.Route.GetGeneration(),
-									gwapiv1.RouteConditionAccepted,
-									metav1.ConditionFalse,
-									gwapiv1.RouteReasonUnsupportedValue,
-									errMsg,
-								)
+								updateRouteStatusForFilter(
+									filterContext,
+									"ReplaceRegexMatch Pattern must be set when rewrite path type is \"ReplaceRegexMatch\"")
 								return
 							} else if _, err := regexp.Compile(hrf.Spec.URLRewrite.Path.ReplaceRegexMatch.Pattern); err != nil {
 								// Avoid envoy NACKs due to invalid regex.
 								// Golang's regexp is almost identical to RE2: https://pkg.go.dev/regexp/syntax
-								errMsg := "ReplaceRegexMatch must be a valid RE2 regular expression"
-								routeStatus := GetRouteStatus(filterContext.Route)
-								status.SetRouteStatusCondition(routeStatus,
-									filterContext.ParentRef.routeParentStatusIdx,
-									filterContext.Route.GetGeneration(),
-									gwapiv1.RouteConditionAccepted,
-									metav1.ConditionFalse,
-									gwapiv1.RouteReasonUnsupportedValue,
-									errMsg,
-								)
+								updateRouteStatusForFilter(
+									filterContext,
+									"ReplaceRegexMatch must be a valid RE2 regular expression")
 								return
 							}
 
@@ -867,16 +744,9 @@ func (t *Translator) processExtensionRefHTTPFilter(extFilter *gwapiv1.LocalObjec
 						var hm *ir.HTTPHostModifier
 						if hrf.Spec.URLRewrite.Hostname.Type == egv1a1.HeaderHTTPHostnameModifier {
 							if hrf.Spec.URLRewrite.Hostname.Header == nil {
-								errMsg := "Header must be set when rewrite path type is \"Header\""
-								routeStatus := GetRouteStatus(filterContext.Route)
-								status.SetRouteStatusCondition(routeStatus,
-									filterContext.ParentRef.routeParentStatusIdx,
-									filterContext.Route.GetGeneration(),
-									gwapiv1.RouteConditionAccepted,
-									metav1.ConditionFalse,
-									gwapiv1.RouteReasonUnsupportedValue,
-									errMsg,
-								)
+								updateRouteStatusForFilter(
+									filterContext,
+									"Header must be set when rewrite path type is \"Header\"")
 								return
 							}
 							hm = &ir.HTTPHostModifier{
@@ -1021,14 +891,7 @@ func (t *Translator) processUnresolvedHTTPFilter(errMsg string, filterContext *H
 		gwapiv1.RouteReasonBackendNotFound,
 		errMsg,
 	)
-	status.SetRouteStatusCondition(routeStatus,
-		filterContext.ParentRef.routeParentStatusIdx,
-		filterContext.Route.GetGeneration(),
-		gwapiv1.RouteConditionAccepted,
-		metav1.ConditionFalse,
-		gwapiv1.RouteReasonUnsupportedValue,
-		errMsg,
-	)
+	updateRouteStatusForFilter(filterContext, errMsg)
 	filterContext.DirectResponse = &ir.CustomResponse{
 		StatusCode: ptr.To(uint32(500)),
 	}
@@ -1036,31 +899,16 @@ func (t *Translator) processUnresolvedHTTPFilter(errMsg string, filterContext *H
 
 func (t *Translator) processUnsupportedHTTPFilter(filterType string, filterContext *HTTPFiltersContext) {
 	errMsg := fmt.Sprintf("Unsupported filter type: %s", filterType)
-	routeStatus := GetRouteStatus(filterContext.Route)
-	status.SetRouteStatusCondition(routeStatus,
-		filterContext.ParentRef.routeParentStatusIdx,
-		filterContext.Route.GetGeneration(),
-		gwapiv1.RouteConditionAccepted,
-		metav1.ConditionFalse,
-		gwapiv1.RouteReasonUnsupportedValue,
-		errMsg,
-	)
+	updateRouteStatusForFilter(filterContext, errMsg)
 	filterContext.DirectResponse = &ir.CustomResponse{
 		StatusCode: ptr.To(uint32(500)),
 	}
 }
 
 func (t *Translator) processInvalidHTTPFilter(filterType string, filterContext *HTTPFiltersContext, err error) {
-	errMsg := fmt.Sprintf("Invalid filter %s: %v", filterType, err)
-	routeStatus := GetRouteStatus(filterContext.Route)
-	status.SetRouteStatusCondition(routeStatus,
-		filterContext.ParentRef.routeParentStatusIdx,
-		filterContext.Route.GetGeneration(),
-		gwapiv1.RouteConditionAccepted,
-		metav1.ConditionFalse,
-		gwapiv1.RouteReasonUnsupportedValue,
-		errMsg,
-	)
+	updateRouteStatusForFilter(
+		filterContext,
+		fmt.Sprintf("Invalid filter %s: %v", filterType, err))
 	filterContext.DirectResponse = &ir.CustomResponse{
 		StatusCode: ptr.To(uint32(500)),
 	}
diff --git a/internal/gatewayapi/resource/load.go b/internal/gatewayapi/resource/load.go
index e3357b0b2..23f64c96a 100644
--- a/internal/gatewayapi/resource/load.go
+++ b/internal/gatewayapi/resource/load.go
@@ -21,6 +21,7 @@ import (
 	utilyaml "k8s.io/apimachinery/pkg/util/yaml"
 	gwapiv1 "sigs.k8s.io/gateway-api/apis/v1"
 	gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
+	gwapiv1a3 "sigs.k8s.io/gateway-api/apis/v1alpha3"
 	"sigs.k8s.io/yaml"
 
 	egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1"
@@ -45,9 +46,7 @@ func LoadResourcesFromYAMLBytes(yamlBytes []byte, addMissingResources bool) (*Re
 
 // loadKubernetesYAMLToResources converts a Kubernetes YAML string into GatewayAPI Resources.
 // TODO: add support for kind:
-//   - EnvoyExtensionPolicy (gateway.envoyproxy.io/v1alpha1)
 //   - BackendLPPolicy (gateway.networking.k8s.io/v1alpha2)
-//   - BackendTLSPolicy (gateway.networking.k8s.io/v1alpha3)
 //   - ReferenceGrant (gateway.networking.k8s.io/v1alpha2)
 func loadKubernetesYAMLToResources(input []byte, addMissingResources bool) (*Resources, error) {
 	resources := NewResources()
@@ -341,6 +340,32 @@ func loadKubernetesYAMLToResources(input []byte, addMissingResources bool) (*Res
 				Data: typedData.(map[string]string),
 			}
 			resources.ConfigMaps = append(resources.ConfigMaps, configMap)
+		case KindBackendTLSPolicy:
+			typedSpec := spec.Interface()
+			backendTLSPolicy := &gwapiv1a3.BackendTLSPolicy{
+				TypeMeta: metav1.TypeMeta{
+					Kind: KindBackendTLSPolicy,
+				},
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      name,
+					Namespace: namespace,
+				},
+				Spec: typedSpec.(gwapiv1a3.BackendTLSPolicySpec),
+			}
+			resources.BackendTLSPolicies = append(resources.BackendTLSPolicies, backendTLSPolicy)
+		case KindEnvoyExtensionPolicy:
+			typedSpec := spec.Interface()
+			envoyExtensionPolicy := &egv1a1.EnvoyExtensionPolicy{
+				TypeMeta: metav1.TypeMeta{
+					Kind: KindEnvoyExtensionPolicy,
+				},
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      name,
+					Namespace: namespace,
+				},
+				Spec: typedSpec.(egv1a1.EnvoyExtensionPolicySpec),
+			}
+			resources.EnvoyExtensionPolicies = append(resources.EnvoyExtensionPolicies, envoyExtensionPolicy)
 		}
 
 		return nil
diff --git a/internal/provider/kubernetes/controller.go b/internal/provider/kubernetes/controller.go
index bc7b5ba8c..d655e8f08 100644
--- a/internal/provider/kubernetes/controller.go
+++ b/internal/provider/kubernetes/controller.go
@@ -215,8 +215,7 @@ func (r *gatewayAPIReconciler) Reconcile(ctx context.Context, _ reconcile.Reques
 					string(gwapiv1.GatewayClassReasonInvalidParameters),
 					msg)
 				r.resources.GatewayClassStatuses.Store(utils.NamespacedName(gc), &gc.Status)
-				r.log.Error(err, "failed to process parametersRef for gatewayclass", "name", managedGC.Name)
-				return reconcile.Result{}, err
+				return reconcile.Result{}, nil
 			}
 		}
 
diff --git a/internal/provider/kubernetes/indexers.go b/internal/provider/kubernetes/indexers.go
index 7afe71278..9866f6d73 100644
--- a/internal/provider/kubernetes/indexers.go
+++ b/internal/provider/kubernetes/indexers.go
@@ -111,7 +111,7 @@ func backendHTTPRouteIndexFunc(rawObj client.Object) []string {
 	var backendRefs []string
 	for _, rule := range httproute.Spec.Rules {
 		for _, backend := range rule.BackendRefs {
-			if backend.Kind == nil || string(*backend.Kind) == resource.KindService || string(*backend.Kind) == egv1a1.KindBackend {
+			if backend.Kind == nil || string(*backend.Kind) == resource.KindService || string(*backend.Kind) == resource.KindServiceImport || string(*backend.Kind) == egv1a1.KindBackend {
 				backendRefs = append(backendRefs,
 					types.NamespacedName{
 						Namespace: gatewayapi.NamespaceDerefOr(backend.Namespace, httproute.Namespace),
@@ -120,6 +120,22 @@ func backendHTTPRouteIndexFunc(rawObj client.Object) []string {
 				)
 			}
 		}
+
+		// Check for a RequestMirror filter to also include the backendRef from that filter
+		for _, filter := range rule.Filters {
+			if filter.Type != gwapiv1.HTTPRouteFilterRequestMirror {
+				continue
+			}
+
+			mirrorBackendRef := filter.RequestMirror.BackendRef
+
+			backendRefs = append(backendRefs,
+				types.NamespacedName{
+					Namespace: gatewayapi.NamespaceDerefOr(mirrorBackendRef.Namespace, httproute.Namespace),
+					Name:      string(mirrorBackendRef.Name),
+				}.String(),
+			)
+		}
 	}
 	return backendRefs
 }
diff --git a/internal/wasm/cache.go b/internal/wasm/cache.go
index 785d8d370..7a045038f 100644
--- a/internal/wasm/cache.go
+++ b/internal/wasm/cache.go
@@ -67,11 +67,16 @@ type localFileCache struct {
 	// option sets for configuring the cache.
 	CacheOptions
 
+	// permissionCheckCache is the cache for permission check for private OCI images.
+	// The permission check is run periodically by a background goroutine and the result is cached.
+	permissionCheckCache *permissionCache
+
 	// logger
 	logger logging.Logger
 }
 
 func (c *localFileCache) Start(ctx context.Context) {
+	c.permissionCheckCache.Start(ctx)
 	go c.purge(ctx)
 }
 
@@ -131,9 +136,12 @@ type cacheEntry struct {
 func newLocalFileCache(options CacheOptions, logger logging.Logger) *localFileCache {
 	options = options.sanitize()
 	cache := &localFileCache{
-		httpFetcher:  NewHTTPFetcher(options.HTTPRequestTimeout, options.HTTPRequestMaxRetries, logger),
-		modules:      make(map[moduleKey]*cacheEntry),
-		checksums:    make(map[string]*checksumEntry),
+		httpFetcher: NewHTTPFetcher(options.HTTPRequestTimeout, options.HTTPRequestMaxRetries, logger),
+		modules:     make(map[moduleKey]*cacheEntry),
+		checksums:   make(map[string]*checksumEntry),
+		permissionCheckCache: newPermissionCache(
+			permissionCacheOptions{},
+			logger),
 		CacheOptions: options,
 		logger:       logger,
 	}
@@ -220,7 +228,7 @@ func (c *localFileCache) getOrFetch(key cacheKey, opts GetOptions) (*cacheEntry,
 	if ce != nil {
 		// We still need to check if the pull secret is correct if it is a private OCI image.
 		if u.Scheme == "oci" && ce.isPrivate {
-			if err = c.checkPermission(ctx, u, insecure, opts); err != nil {
+			if _, err := c.permissionCheckCache.IsAllowed(ctx, u, opts.PullSecret, insecure); err != nil {
 				return nil, err
 			}
 		}
@@ -250,7 +258,24 @@ func (c *localFileCache) getOrFetch(key cacheKey, opts GetOptions) (*cacheEntry,
 		if len(opts.PullSecret) > 0 {
 			isPrivate = true
 		}
-		if imageBinaryFetcher, dChecksum, err = c.prepareFetch(ctx, u, insecure, opts); err != nil {
+
+		imageBinaryFetcher, dChecksum, err = c.prepareFetch(ctx, u, insecure, opts.PullSecret)
+
+		if isPrivate {
+			e := &permissionCacheEntry{
+				image: u,
+				fetcherOption: &ImageFetcherOption{
+					Insecure:   insecure,
+					PullSecret: opts.PullSecret,
+				},
+				lastCheck:  time.Now(),
+				lastAccess: time.Now(),
+				checkError: err,
+			}
+			c.permissionCheckCache.Put(e)
+		}
+
+		if err != nil {
 			wasmRemoteFetchTotal.WithFailure(reasonManifestError).Increment()
 			return nil, fmt.Errorf("could not fetch Wasm OCI image: %w", err)
 		}
@@ -287,24 +312,16 @@ func (c *localFileCache) getOrFetch(key cacheKey, opts GetOptions) (*cacheEntry,
 	return c.addEntry(key, b, isPrivate)
 }
 
-func (c *localFileCache) checkPermission(ctx context.Context, u *url.URL, insecure bool, opts GetOptions) error {
-	// Try to get the image metadata to check if the pull secret is correct.
-	if _, _, err := c.prepareFetch(ctx, u, insecure, opts); err != nil {
-		return fmt.Errorf("failed to login to private registry: %w", err)
-	}
-	return nil
-}
-
 // prepareFetch won't fetch the binary, but it will prepare the binaryFetcher and actualDigest.
 func (c *localFileCache) prepareFetch(
-	ctx context.Context, url *url.URL, insecure bool, opts GetOptions) (
+	ctx context.Context, url *url.URL, insecure bool, pullSecret []byte) (
 	binaryFetcher func() ([]byte, error), actualDigest string, err error,
 ) {
 	imgFetcherOps := ImageFetcherOption{
 		Insecure: insecure,
 	}
-	if len(opts.PullSecret) > 0 {
-		imgFetcherOps.PullSecret = opts.PullSecret
+	if len(pullSecret) > 0 {
+		imgFetcherOps.PullSecret = pullSecret
 	}
 	fetcher := NewImageFetcher(ctx, imgFetcherOps, c.logger)
 	if binaryFetcher, actualDigest, err = fetcher.PrepareFetch(url.Host + url.Path); err != nil {
diff --git a/internal/wasm/premissioncache.go b/internal/wasm/premissioncache.go
new file mode 100644
index 000000000..e680574ad
--- /dev/null
+++ b/internal/wasm/premissioncache.go
@@ -0,0 +1,266 @@
+// Copyright Envoy Gateway Authors
+// SPDX-License-Identifier: Apache-2.0
+// The full text of the Apache license is available in the LICENSE file at
+// the root of the repo.
+
+// Copyright Istio Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package wasm
+
+import (
+	"context"
+	"encoding/hex"
+	"errors"
+	"fmt"
+	"net/http"
+	"net/url"
+	"sync"
+	"time"
+
+	"github.com/avast/retry-go"
+	"github.com/google/go-containerregistry/pkg/v1/remote/transport"
+
+	"github.com/envoyproxy/gateway/internal/logging"
+)
+
+type permissionCacheOptions struct {
+	// checkInterval is the interval to recheck the permission for the cached permission entries.
+	checkInterval time.Duration
+
+	// permissionExpiry is the expiry time for permission cache entry.
+	// The permission cache entry will be updated by rechecking the OCI image permission against the pull secret.
+	permissionExpiry time.Duration
+
+	// cacheExpiry is the expiry time for the permission cache.
+	// The permission cache will be removed if it is not accessed for the specified expiry time.
+	// This is used to purge the cache.
+	cacheExpiry time.Duration
+}
+
+// sanitize validates and sets the default values for the permission cache options.
+func (o *permissionCacheOptions) sanitize() {
+	if o.checkInterval == 0 {
+		o.checkInterval = 5 * time.Minute
+	}
+	if o.permissionExpiry == 0 {
+		o.permissionExpiry = 1 * time.Hour
+	}
+	if o.cacheExpiry == 0 {
+		o.cacheExpiry = 24 * time.Hour
+	}
+}
+
+// permissionCache is a cache for permission check for private OCI images.
+// After a new permission is put into the cache, it will be checked periodically by a background goroutine.
+// It is used to avoid blocking the translator due to the permission check.
+type permissionCache struct {
+	sync.Mutex
+	permissionCacheOptions
+
+	cache  map[string]*permissionCacheEntry
+	logger logging.Logger
+}
+
+// permissionCacheEntry is an entry in the permission cache.
+type permissionCacheEntry struct {
+	// The oci image URL.
+	image *url.URL
+	// fetcherOption contains the pull secret for the image.
+	fetcherOption *ImageFetcherOption
+	// The last time the pull secret is checked against the image.
+	lastCheck time.Time
+	// The error returned by the OCI registry when checking the permission.
+	// If error is not nil, the permission is not allowed.
+	// If it's a permission error, it's represented by a transport.Error with 401 or 403 HTTP status code.
+	// But it's not necessarily a permission error, it could be other errors like network error, non-exist image, etc.
+	// In this case, the permission is also not allowed.
+	checkError error
+	// The last time the cache entry is accessed.
+	lastAccess time.Time
+}
+
+func (e *permissionCacheEntry) key() string {
+	return permissionCacheKey(e.image, e.fetcherOption.PullSecret)
+}
+
+// isPermissionExpired returns true if the permission check is older
+// than the specified expiry duration. If this is true, the entry
+// should be rechecked.
+func (e *permissionCacheEntry) isPermissionExpired(expiry time.Duration) bool {
+	return time.Now().After(e.lastCheck.Add(expiry))
+}
+
+// isCacheExpired returns true if the cache entry has not been accessed
+// for the specified expiry duration. If this is true, the entry
+// should be removed.
+func (e *permissionCacheEntry) isCacheExpired(expiry time.Duration) bool {
+	return time.Now().After(e.lastAccess.Add(expiry))
+}
+
+// permissionCacheKey generates a key for a permission cache entry.
+// The key is the hex encoded of concatenation of the image URL and the pull secret.
+func permissionCacheKey(image *url.URL, pullSecret []byte) string {
+	b := make([]byte, len(image.String())+len(pullSecret))
+	copy(b, image.String())
+	copy(b[len(image.String()):], pullSecret)
+	return hex.EncodeToString(b)
+}
+
+// newPermissionCache creates a new permission cache with a given TTL.
+func newPermissionCache(options permissionCacheOptions, logger logging.Logger) *permissionCache {
+	options.sanitize()
+	return &permissionCache{
+		cache:                  make(map[string]*permissionCacheEntry),
+		permissionCacheOptions: options,
+		logger:                 logger,
+	}
+}
+
+// checkAndUpdatePermission checks the permission of the image against the pull secret and updates the cache entry.
+func (p *permissionCache) checkAndUpdatePermission(ctx context.Context, e *permissionCacheEntry) error {
+	fetcher := NewImageFetcher(ctx, *e.fetcherOption, p.logger)
+	_, _, err := fetcher.PrepareFetch(e.image.Host + e.image.Path)
+	e.checkError = err
+	e.lastCheck = time.Now()
+	return err
+}
+
+// start starts a background goroutine to periodically check the permission for the cached permission entries.
+func (p *permissionCache) Start(ctx context.Context) {
+	go func() {
+		ticker := time.NewTicker(p.checkInterval)
+		defer ticker.Stop()
+		for {
+			select {
+			case <-ticker.C:
+				func() {
+					p.Lock()
+					defer p.Unlock()
+					for _, e := range p.cache {
+						if e.isCacheExpired(p.cacheExpiry) {
+							p.logger.Info("removing permission cache entry", "image", e.image.String())
+							delete(p.cache, e.key())
+							continue
+						}
+						if e.isPermissionExpired(p.permissionExpiry) {
+							const retryAttempts = 3
+							const retryDelay = 1 * time.Second
+							p.logger.Info("rechecking permission for image", "image", e.image.String())
+							err := retry.Do(
+								func() error {
+									err := p.checkAndUpdatePermission(ctx, e)
+									if err != nil && isRetriableError(err) {
+										p.logger.Error(
+											err,
+											"failed to check permission for image, will retry again",
+											"image",
+											e.image.String())
+										return err
+									}
+									return nil
+								},
+								retry.Attempts(retryAttempts),
+								retry.DelayType(retry.BackOffDelay),
+								retry.Delay(retryDelay),
+								retry.Context(ctx),
+							)
+							if err != nil {
+								p.logger.Error(
+									err,
+									fmt.Sprintf("failed to recheck permission for image after %d attempts", retryAttempts),
+									"image",
+									e.image.String())
+							}
+						}
+					}
+				}()
+			case <-ctx.Done():
+				return
+			}
+		}
+	}()
+}
+
+// isRetriableError checks if the error is retriable.
+// If the error is a permission error, it's not retriable. For example, 401 and 403 HTTP status code.
+func isRetriableError(err error) bool {
+	var terr *transport.Error
+	if errors.As(err, &terr) {
+		if terr.StatusCode == http.StatusUnauthorized || terr.StatusCode == http.StatusForbidden {
+			return false
+		}
+	}
+	return true
+}
+
+// put puts a new permission cache entry into the cache.
+func (p *permissionCache) Put(e *permissionCacheEntry) {
+	p.Lock()
+	defer p.Unlock()
+	e.lastAccess = time.Now()
+	e.lastCheck = time.Now()
+	p.cache[e.key()] = e
+}
+
+// IsAllowed checks if the given image is allowed to be accessed with the provided pull secret.
+// If the permission is not found in the cache, this method will block until the permission is checked and cached.
+// This blocking won't be too long as it's only for the first time permission check and won't retry. Subsequent
+// permission checks will be done in a background goroutine by the permission cache.
+//
+// If any error occurs, the permission is considered not allowed.
+// The error can be a permission error or other errors like network error, non-exist image, etc.
+func (p *permissionCache) IsAllowed(ctx context.Context, image *url.URL, pullSecret []byte, insecure bool) (bool, error) {
+	p.Lock()
+	defer p.Unlock()
+	key := permissionCacheKey(image, pullSecret)
+	if e, ok := p.cache[key]; ok {
+		e.lastAccess = time.Now()
+		return e.checkError == nil, e.checkError
+	}
+
+	e := &permissionCacheEntry{
+		image: image,
+		fetcherOption: &ImageFetcherOption{
+			Insecure:   insecure,
+			PullSecret: pullSecret,
+		},
+	}
+	// Do not retry if the permission check fails because we don't want to block the translator for too long.
+	// The permission check will be retried in the background goroutine by the permission cache.
+	if err := p.checkAndUpdatePermission(ctx, e); err != nil {
+		p.logger.Error(err, "failed to check permission for image", "image", e.image.String())
+	}
+	e.lastAccess = time.Now()
+	p.cache[key] = e
+	return e.checkError == nil, e.checkError
+}
+
+// getForTest is a test helper to get a permission cache entry from the cache.
+func (p *permissionCache) getForTest(key string) (permissionCacheEntry, bool) {
+	p.Lock()
+	defer p.Unlock()
+	entry, ok := p.cache[key]
+	if !ok {
+		return permissionCacheEntry{}, false
+	}
+	return *entry, true
+}
+
+// deleteForTest is a test helper to delete a permission cache entry from the cache.
+func (p *permissionCache) deleteForTest(key string) {
+	p.Lock()
+	defer p.Unlock()
+	delete(p.cache, key)
+}
diff --git a/internal/xds/translator/accesslog.go b/internal/xds/translator/accesslog.go
index 991c8e342..304f22da4 100644
--- a/internal/xds/translator/accesslog.go
+++ b/internal/xds/translator/accesslog.go
@@ -336,10 +336,10 @@ func buildXdsAccessLog(al *ir.AccessLog, accessLogType ir.ProxyAccessLogType) ([
 			ResourceAttributes: convertToKeyValueList(otel.Resources, false),
 		}
 
-		if otel.Text == nil {
-			return nil, errors.New("otel.Text is nil")
+		var format string
+		if otel.Text != nil && *otel.Text != "" {
+			format = *otel.Text
 		}
-		format := *otel.Text
 
 		if format != "" {
 			al.Body = &otlpcommonv1.AnyValue{
@@ -349,9 +349,17 @@ func buildXdsAccessLog(al *ir.AccessLog, accessLogType ir.ProxyAccessLogType) ([
 			}
 		}
 
-		al.Attributes = convertToKeyValueList(otel.Attributes, true)
+		var attrs map[string]string
+		if len(otel.Attributes) != 0 {
+			attrs = otel.Attributes
+		} else if len(otel.Attributes) == 0 && format == "" {
+			// if there are no attributes and text format is unset, use the default EnvoyJSONLogFields
+			attrs = EnvoyJSONLogFields
+		}
+
+		al.Attributes = convertToKeyValueList(attrs, true)
+		formatters := accessLogOpenTelemetryFormatters(format, attrs)
 
-		formatters := accessLogOpenTelemetryFormatters(format, otel.Attributes)
 		if len(formatters) != 0 {
 			al.Formatters = formatters
 		}
diff --git a/release-notes/current.yaml b/release-notes/current.yaml
index 288804f88..6c69e1673 100644
--- a/release-notes/current.yaml
+++ b/release-notes/current.yaml
@@ -10,13 +10,22 @@ security updates: |
 new features: |
 
 bug fixes: |
+  Added support for BackendTLSPolicy and EnvoyExtensionPolicy parsing in Standalone mode.
+  Fixed updates of endpoints when mirrored backend Pod IPs are changed.
+  Fix not logging an error and returning it in the K8s Reconcile method when a GatewayClass is not accepted.
+  Fix allowing empty text field for opentelemetry sink when using JSON format.
+  Fixed validation of host header in RequestHeaderModifier filter.
+  Retrigger reconciliation when backendRef of type ServiceImport is updated or when EndpointSlice(s) for a ServiceImport are updated.
 
 # Enhancements that improve performance.
 performance improvements: |
+  Added a cache for the Wasm OCI image permission checks and check the pullSecrets against the OCI image registry in
+  a background goroutine.
 
 # Deprecated features or APIs.
 deprecations: |
 
 # Other notable changes not covered by the above sections.
 Other changes: |
-
+  Bumped envoy to v1.33.1.
+  Bumpled ratelimit to 0141a24f.
diff --git a/release-notes/v1.3.2.yaml b/release-notes/v1.3.2.yaml
new file mode 100644
index 000000000..3422655b4
--- /dev/null
+++ b/release-notes/v1.3.2.yaml
@@ -0,0 +1,19 @@
+date: March 24, 2025
+
+bug fixes: |
+  Added support for BackendTLSPolicy and EnvoyExtensionPolicy parsing in Standalone mode.
+  Fixed updates of endpoints when mirrored backend Pod IPs are changed.
+  Fix not logging an error and returning it in the K8s Reconcile method when a GatewayClass is not accepted.
+  Fix allowing empty text field for opentelemetry sink when using JSON format.
+  Fixed validation of host header in RequestHeaderModifier filter.
+  Retrigger reconciliation when backendRef of type ServiceImport is updated or when EndpointSlice(s) for a ServiceImport are updated.
+
+# Enhancements that improve performance.
+performance improvements: |
+  Added a cache for the Wasm OCI image permission checks and check the pullSecrets against the OCI image registry in
+  a background goroutine.
+
+# Other notable changes not covered by the above sections.
+Other changes: |
+  Bumped envoy to v1.33.1.
+  Bumped ratelimit to 0141a24f.
diff --git a/site/content/en/news/releases/notes/v1.3.2.md b/site/content/en/news/releases/notes/v1.3.2.md
new file mode 100644
index 000000000..73760e606
--- /dev/null
+++ b/site/content/en/news/releases/notes/v1.3.2.md
@@ -0,0 +1,23 @@
+---
+title: "v1.3.2"
+publishdate: 2025-03-24
+---
+
+Date: March 24, 2025
+
+## Bug fixes
+- Added support for BackendTLSPolicy and EnvoyExtensionPolicy parsing in Standalone mode.
+- Fixed updates of endpoints when mirrored backend Pod IPs are changed.
+- Fix not logging an error and returning it in the K8s Reconcile method when a GatewayClass is not accepted.
+- Fix allowing empty text field for opentelemetry sink when using JSON format.
+- Fixed validation of host header in RequestHeaderModifier filter.
+- Retrigger reconciliation when backendRef of type ServiceImport is updated or when EndpointSlice(s) for a ServiceImport are updated.
+
+## Performance improvements
+- Added a cache for the Wasm OCI image permission checks and check the pullSecrets against the OCI image registry in
+- a background goroutine.
+
+## Other changes
+- Bumped envoy to v1.33.1.
+- Bumped ratelimit to 0141a24f.
+


Test patch:
diff --git a/internal/cmd/egctl/testdata/translate/in/backend-endpoint.yaml b/internal/cmd/egctl/testdata/translate/in/backend-endpoint.yaml
index d2aa0f78f..f845c0941 100644
--- a/internal/cmd/egctl/testdata/translate/in/backend-endpoint.yaml
+++ b/internal/cmd/egctl/testdata/translate/in/backend-endpoint.yaml
@@ -44,3 +44,34 @@ spec:
     - ip:
         address: 0.0.0.0
         port: 3000
+---
+apiVersion: gateway.networking.k8s.io/v1alpha3
+kind: BackendTLSPolicy
+metadata:
+  name: example-com-tls-policy
+spec:
+  targetRefs:
+    - group: 'gateway.envoyproxy.io'
+      kind: Backend
+      name: backend
+  validation:
+    wellKnownCACertificates: "System"
+    hostname: www.example.com
+---
+apiVersion: gateway.envoyproxy.io/v1alpha1
+kind: EnvoyExtensionPolicy
+metadata:
+  name: ext-proc-example
+spec:
+  targetRefs:
+    - group: gateway.networking.k8s.io
+      kind: HTTPRoute
+      name: backend
+  extProc:
+    - backendRefs:
+        - name: grpc-ext-proc
+          port: 9002
+      processingMode:
+        request: {}
+        response:
+          body: Streamed
diff --git a/internal/cmd/egctl/testdata/translate/out/backend-endpoint.all.yaml b/internal/cmd/egctl/testdata/translate/out/backend-endpoint.all.yaml
index 0841f8487..1085a7aa2 100644
--- a/internal/cmd/egctl/testdata/translate/out/backend-endpoint.all.yaml
+++ b/internal/cmd/egctl/testdata/translate/out/backend-endpoint.all.yaml
@@ -1,3 +1,30 @@
+backendTLSPolicies:
+- kind: BackendTLSPolicy
+  metadata:
+    creationTimestamp: null
+    name: example-com-tls-policy
+    namespace: envoy-gateway-system
+  spec:
+    targetRefs:
+    - group: gateway.envoyproxy.io
+      kind: Backend
+      name: backend
+    validation:
+      hostname: www.example.com
+      wellKnownCACertificates: System
+  status:
+    ancestors:
+    - ancestorRef:
+        group: gateway.networking.k8s.io
+        kind: Gateway
+        name: eg
+      conditions:
+      - lastTransitionTime: null
+        message: Policy has been accepted.
+        reason: Accepted
+        status: "True"
+        type: Accepted
+      controllerName: gateway.envoyproxy.io/gatewayclass-controller
 backends:
 - kind: Backend
   metadata:
@@ -16,6 +43,43 @@ backends:
       reason: Accepted
       status: "True"
       type: Accepted
+envoyExtensionPolicies:
+- kind: EnvoyExtensionPolicy
+  metadata:
+    creationTimestamp: null
+    name: ext-proc-example
+    namespace: envoy-gateway-system
+  spec:
+    extProc:
+    - backendRefs:
+      - group: ""
+        kind: Service
+        name: grpc-ext-proc
+        port: 9002
+      processingMode:
+        request: {}
+        response:
+          body: Streamed
+    targetRefs:
+    - group: gateway.networking.k8s.io
+      kind: HTTPRoute
+      name: backend
+  status:
+    ancestors:
+    - ancestorRef:
+        group: gateway.networking.k8s.io
+        kind: Gateway
+        name: eg
+        namespace: envoy-gateway-system
+      conditions:
+      - lastTransitionTime: null
+        message: |-
+          Wasm: wasm cache is not initialized
+          ExtProc: service envoy-gateway-system/grpc-ext-proc not found.
+        reason: Invalid
+        status: "False"
+        type: Accepted
+      controllerName: gateway.envoyproxy.io/gatewayclass-controller
 gatewayClass:
   kind: GatewayClass
   metadata:
diff --git a/internal/gatewayapi/testdata/httproute-with-header-filter-invalid-headers.in.yaml b/internal/gatewayapi/testdata/httproute-with-header-filter-invalid-headers.in.yaml
index aaf5bad87..869f9122c 100644
--- a/internal/gatewayapi/testdata/httproute-with-header-filter-invalid-headers.in.yaml
+++ b/internal/gatewayapi/testdata/httproute-with-header-filter-invalid-headers.in.yaml
@@ -30,7 +30,7 @@ httpRoutes:
     rules:
     - matches:
       - path:
-          value: "/"
+          value: "/foo"
       backendRefs:
       - name: service-1
         port: 8080
@@ -42,7 +42,57 @@ httpRoutes:
             value: "some-value"
           - name: "good-header"
             value: "some-value"
-          add:
-          - name: "example/2"
+- apiVersion: gateway.networking.k8s.io/v1
+  kind: HTTPRoute
+  metadata:
+    namespace: default
+    name: httproute-2
+  spec:
+    hostnames:
+    - gateway.envoyproxy.io
+    parentRefs:
+    - namespace: envoy-gateway
+      name: gateway-1
+      sectionName: http
+    rules:
+    - matches:
+      - path:
+          value: "/bar"
+      backendRefs:
+      - name: service-1
+        port: 8080
+      filters:
+      - type: RequestHeaderModifier
+        requestHeaderModifier:
+          remove:
+          - "example/2"
+          set:
+          - name: "good-header"
+            value: "some-value"
+- apiVersion: gateway.networking.k8s.io/v1
+  kind: HTTPRoute
+  metadata:
+    namespace: default
+    name: httproute-3
+  spec:
+    hostnames:
+    - gateway.envoyproxy.io
+    parentRefs:
+    - namespace: envoy-gateway
+      name: gateway-1
+      sectionName: http
+    rules:
+    - matches:
+      - path:
+          value: "/baz"
+      backendRefs:
+      - name: service-1
+        port: 8080
+      filters:
+      - type: RequestHeaderModifier
+        requestHeaderModifier:
+          set:
+          - name: "host"
+            value: "example.com"
+          - name: "good-header"
             value: "some-value"
-
diff --git a/internal/gatewayapi/testdata/httproute-with-header-filter-invalid-headers.out.yaml b/internal/gatewayapi/testdata/httproute-with-header-filter-invalid-headers.out.yaml
index 339e6a1f4..b1428fee3 100644
--- a/internal/gatewayapi/testdata/httproute-with-header-filter-invalid-headers.out.yaml
+++ b/internal/gatewayapi/testdata/httproute-with-header-filter-invalid-headers.out.yaml
@@ -17,7 +17,7 @@ gateways:
       protocol: HTTP
   status:
     listeners:
-    - attachedRoutes: 1
+    - attachedRoutes: 3
       conditions:
       - lastTransitionTime: null
         message: Sending translated listener configuration to the data plane
@@ -60,9 +60,6 @@ httpRoutes:
         port: 8080
       filters:
       - requestHeaderModifier:
-          add:
-          - name: example/2
-            value: some-value
           set:
           - name: example:1
             value: some-value
@@ -71,13 +68,109 @@ httpRoutes:
         type: RequestHeaderModifier
       matches:
       - path:
-          value: /
+          value: /foo
+  status:
+    parents:
+    - conditions:
+      - lastTransitionTime: null
+        message: 'Header: "example:1". The RequestHeaderModifier filter cannot set
+          the Host header or headers with a ''/'' or '':'' character in them. To modify
+          the Host header use the URLRewrite or the HTTPRouteFilter filter.'
+        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
+- apiVersion: gateway.networking.k8s.io/v1
+  kind: HTTPRoute
+  metadata:
+    creationTimestamp: null
+    name: httproute-2
+    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:
+          remove:
+          - example/2
+          set:
+          - name: good-header
+            value: some-value
+        type: RequestHeaderModifier
+      matches:
+      - path:
+          value: /bar
+  status:
+    parents:
+    - conditions:
+      - lastTransitionTime: null
+        message: 'Header: "example/2". The RequestHeaderModifier filter cannot remove
+          the Host header or headers with a ''/'' or '':'' character in them.'
+        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
+- apiVersion: gateway.networking.k8s.io/v1
+  kind: HTTPRoute
+  metadata:
+    creationTimestamp: null
+    name: httproute-3
+    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:
+          set:
+          - name: host
+            value: example.com
+          - name: good-header
+            value: some-value
+        type: RequestHeaderModifier
+      matches:
+      - path:
+          value: /baz
   status:
     parents:
     - conditions:
       - lastTransitionTime: null
-        message: 'RequestHeaderModifier Filter cannot set headers with a ''/'' or
-          '':'' character in them. Header: ''example:1'''
+        message: 'Header: "host". The RequestHeaderModifier filter cannot set the
+          Host header or headers with a ''/'' or '':'' character in them. To modify
+          the Host header use the URLRewrite or the HTTPRouteFilter filter.'
         reason: UnsupportedValue
         status: "False"
         type: Accepted
diff --git a/internal/gatewayapi/testdata/httproute-with-response-header-filter-invalid-headers.in.yaml b/internal/gatewayapi/testdata/httproute-with-response-header-filter-invalid-headers.in.yaml
index 6e1e57425..95da71440 100644
--- a/internal/gatewayapi/testdata/httproute-with-response-header-filter-invalid-headers.in.yaml
+++ b/internal/gatewayapi/testdata/httproute-with-response-header-filter-invalid-headers.in.yaml
@@ -30,7 +30,7 @@ httpRoutes:
     rules:
     - matches:
       - path:
-          value: "/"
+          value: "/foo"
       backendRefs:
       - name: service-1
         port: 8080
@@ -42,7 +42,57 @@ httpRoutes:
             value: "some-value"
           - name: "good-header"
             value: "some-value"
-          add:
-          - name: "example/2"
+- apiVersion: gateway.networking.k8s.io/v1
+  kind: HTTPRoute
+  metadata:
+    namespace: default
+    name: httproute-2
+  spec:
+    hostnames:
+    - gateway.envoyproxy.io
+    parentRefs:
+    - namespace: envoy-gateway
+      name: gateway-1
+      sectionName: http
+    rules:
+    - matches:
+      - path:
+          value: "/bar"
+      backendRefs:
+      - name: service-1
+        port: 8080
+      filters:
+      - type: ResponseHeaderModifier
+        responseHeaderModifier:
+          remove:
+          - "example/2"
+          set:
+          - name: "good-header"
+            value: "some-value"
+- apiVersion: gateway.networking.k8s.io/v1
+  kind: HTTPRoute
+  metadata:
+    namespace: default
+    name: httproute-3
+  spec:
+    hostnames:
+    - gateway.envoyproxy.io
+    parentRefs:
+    - namespace: envoy-gateway
+      name: gateway-1
+      sectionName: http
+    rules:
+    - matches:
+      - path:
+          value: "/baz"
+      backendRefs:
+      - name: service-1
+        port: 8080
+      filters:
+      - type: ResponseHeaderModifier
+        responseHeaderModifier:
+          set:
+          - name: "host"
+            value: "example.com"
+          - name: "good-header"
             value: "some-value"
-
diff --git a/internal/gatewayapi/testdata/httproute-with-response-header-filter-invalid-headers.out.yaml b/internal/gatewayapi/testdata/httproute-with-response-header-filter-invalid-headers.out.yaml
index 077e6e866..3fee996d4 100644
--- a/internal/gatewayapi/testdata/httproute-with-response-header-filter-invalid-headers.out.yaml
+++ b/internal/gatewayapi/testdata/httproute-with-response-header-filter-invalid-headers.out.yaml
@@ -17,7 +17,7 @@ gateways:
       protocol: HTTP
   status:
     listeners:
-    - attachedRoutes: 1
+    - attachedRoutes: 3
       conditions:
       - lastTransitionTime: null
         message: Sending translated listener configuration to the data plane
@@ -60,9 +60,6 @@ httpRoutes:
         port: 8080
       filters:
       - responseHeaderModifier:
-          add:
-          - name: example/2
-            value: some-value
           set:
           - name: example:1
             value: some-value
@@ -71,13 +68,107 @@ httpRoutes:
         type: ResponseHeaderModifier
       matches:
       - path:
-          value: /
+          value: /foo
+  status:
+    parents:
+    - conditions:
+      - lastTransitionTime: null
+        message: 'Header: "example:1". The ResponseHeaderModifier filter cannot set
+          the Host header or headers with a ''/'' or '':'' character in them.'
+        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
+- apiVersion: gateway.networking.k8s.io/v1
+  kind: HTTPRoute
+  metadata:
+    creationTimestamp: null
+    name: httproute-2
+    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:
+          remove:
+          - example/2
+          set:
+          - name: good-header
+            value: some-value
+        type: ResponseHeaderModifier
+      matches:
+      - path:
+          value: /bar
+  status:
+    parents:
+    - conditions:
+      - lastTransitionTime: null
+        message: 'Header: "example/2". The ResponseHeaderModifier filter cannot remove
+          the Host header or headers with a ''/'' or '':'' character in them.'
+        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
+- apiVersion: gateway.networking.k8s.io/v1
+  kind: HTTPRoute
+  metadata:
+    creationTimestamp: null
+    name: httproute-3
+    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:
+          set:
+          - name: host
+            value: example.com
+          - name: good-header
+            value: some-value
+        type: ResponseHeaderModifier
+      matches:
+      - path:
+          value: /baz
   status:
     parents:
     - conditions:
       - lastTransitionTime: null
-        message: 'ResponseHeaderModifier Filter cannot set headers with a ''/'' or
-          '':'' character in them. Header: ''example:1'''
+        message: 'Header: "host". The ResponseHeaderModifier filter cannot set the
+          Host header or headers with a ''/'' or '':'' character in them.'
         reason: UnsupportedValue
         status: "False"
         type: Accepted
diff --git a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/component-level.yaml b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/component-level.yaml
index ae1a04b36..5be1c2ca2 100644
--- a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/component-level.yaml
+++ b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/component-level.yaml
@@ -56,7 +56,7 @@ spec:
             fieldRef:
               apiVersion: v1
               fieldPath: metadata.name
-        image: docker.io/envoyproxy/envoy:distroless-v1.33.0
+        image: docker.io/envoyproxy/envoy:distroless-v1.33.1
         imagePullPolicy: IfNotPresent
         lifecycle:
           preStop:
diff --git a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/default.yaml b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/default.yaml
index 5b7856ee5..6ece066ac 100644
--- a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/default.yaml
+++ b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/default.yaml
@@ -227,7 +227,7 @@ spec:
             fieldRef:
               apiVersion: v1
               fieldPath: metadata.name
-        image: docker.io/envoyproxy/envoy:distroless-v1.33.0
+        image: docker.io/envoyproxy/envoy:distroless-v1.33.1
         imagePullPolicy: IfNotPresent
         lifecycle:
           preStop:
diff --git a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/disable-prometheus.yaml b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/disable-prometheus.yaml
index a42575b16..ca8b77c67 100644
--- a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/disable-prometheus.yaml
+++ b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/disable-prometheus.yaml
@@ -198,7 +198,7 @@ spec:
             fieldRef:
               apiVersion: v1
               fieldPath: metadata.name
-        image: docker.io/envoyproxy/envoy:distroless-v1.33.0
+        image: docker.io/envoyproxy/envoy:distroless-v1.33.1
         imagePullPolicy: IfNotPresent
         lifecycle:
           preStop:
diff --git a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/override-labels-and-annotations.yaml b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/override-labels-and-annotations.yaml
index f1cf7e3b2..783810094 100644
--- a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/override-labels-and-annotations.yaml
+++ b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/override-labels-and-annotations.yaml
@@ -236,7 +236,7 @@ spec:
             fieldRef:
               apiVersion: v1
               fieldPath: metadata.name
-        image: docker.io/envoyproxy/envoy:distroless-v1.33.0
+        image: docker.io/envoyproxy/envoy:distroless-v1.33.1
         imagePullPolicy: IfNotPresent
         lifecycle:
           preStop:
diff --git a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/patch-daemonset.yaml b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/patch-daemonset.yaml
index 5fdb5e11a..068a6579b 100644
--- a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/patch-daemonset.yaml
+++ b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/patch-daemonset.yaml
@@ -227,7 +227,7 @@ spec:
             fieldRef:
               apiVersion: v1
               fieldPath: metadata.name
-        image: docker.io/envoyproxy/envoy:distroless-v1.33.0
+        image: docker.io/envoyproxy/envoy:distroless-v1.33.1
         imagePullPolicy: IfNotPresent
         lifecycle:
           preStop:
diff --git a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/shutdown-manager.yaml b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/shutdown-manager.yaml
index b96fe35ed..aef27b2bb 100644
--- a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/shutdown-manager.yaml
+++ b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/shutdown-manager.yaml
@@ -227,7 +227,7 @@ spec:
             fieldRef:
               apiVersion: v1
               fieldPath: metadata.name
-        image: docker.io/envoyproxy/envoy:distroless-v1.33.0
+        image: docker.io/envoyproxy/envoy:distroless-v1.33.1
         imagePullPolicy: IfNotPresent
         lifecycle:
           preStop:
diff --git a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-annotations.yaml b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-annotations.yaml
index b5396e729..b77221c26 100644
--- a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-annotations.yaml
+++ b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-annotations.yaml
@@ -232,7 +232,7 @@ spec:
             fieldRef:
               apiVersion: v1
               fieldPath: metadata.name
-        image: docker.io/envoyproxy/envoy:distroless-v1.33.0
+        image: docker.io/envoyproxy/envoy:distroless-v1.33.1
         imagePullPolicy: IfNotPresent
         lifecycle:
           preStop:
diff --git a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-concurrency.yaml b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-concurrency.yaml
index 40dc270d4..4f8f311d1 100644
--- a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-concurrency.yaml
+++ b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-concurrency.yaml
@@ -56,7 +56,7 @@ spec:
             fieldRef:
               apiVersion: v1
               fieldPath: metadata.name
-        image: docker.io/envoyproxy/envoy:distroless-v1.33.0
+        image: docker.io/envoyproxy/envoy:distroless-v1.33.1
         imagePullPolicy: IfNotPresent
         lifecycle:
           preStop:
diff --git a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-extra-args.yaml b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-extra-args.yaml
index 0c35ddf97..d9c734378 100644
--- a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-extra-args.yaml
+++ b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-extra-args.yaml
@@ -229,7 +229,7 @@ spec:
             fieldRef:
               apiVersion: v1
               fieldPath: metadata.name
-        image: docker.io/envoyproxy/envoy:distroless-v1.33.0
+        image: docker.io/envoyproxy/envoy:distroless-v1.33.1
         imagePullPolicy: IfNotPresent
         lifecycle:
           preStop:
diff --git a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-image-pull-secrets.yaml b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-image-pull-secrets.yaml
index 96f5adf00..c9769bc14 100644
--- a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-image-pull-secrets.yaml
+++ b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-image-pull-secrets.yaml
@@ -227,7 +227,7 @@ spec:
             fieldRef:
               apiVersion: v1
               fieldPath: metadata.name
-        image: docker.io/envoyproxy/envoy:distroless-v1.33.0
+        image: docker.io/envoyproxy/envoy:distroless-v1.33.1
         imagePullPolicy: IfNotPresent
         lifecycle:
           preStop:
diff --git a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-name.yaml b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-name.yaml
index f004ff20a..170705190 100644
--- a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-name.yaml
+++ b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-name.yaml
@@ -227,7 +227,7 @@ spec:
             fieldRef:
               apiVersion: v1
               fieldPath: metadata.name
-        image: docker.io/envoyproxy/envoy:distroless-v1.33.0
+        image: docker.io/envoyproxy/envoy:distroless-v1.33.1
         imagePullPolicy: IfNotPresent
         lifecycle:
           preStop:
diff --git a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-node-selector.yaml b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-node-selector.yaml
index 38747e0bc..05e327e58 100644
--- a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-node-selector.yaml
+++ b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-node-selector.yaml
@@ -227,7 +227,7 @@ spec:
             fieldRef:
               apiVersion: v1
               fieldPath: metadata.name
-        image: docker.io/envoyproxy/envoy:distroless-v1.33.0
+        image: docker.io/envoyproxy/envoy:distroless-v1.33.1
         imagePullPolicy: IfNotPresent
         lifecycle:
           preStop:
diff --git a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-topology-spread-constraints.yaml b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-topology-spread-constraints.yaml
index d5c936014..d8e1ec5c0 100644
--- a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-topology-spread-constraints.yaml
+++ b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-topology-spread-constraints.yaml
@@ -227,7 +227,7 @@ spec:
             fieldRef:
               apiVersion: v1
               fieldPath: metadata.name
-        image: docker.io/envoyproxy/envoy:distroless-v1.33.0
+        image: docker.io/envoyproxy/envoy:distroless-v1.33.1
         imagePullPolicy: IfNotPresent
         lifecycle:
           preStop:
diff --git a/internal/infrastructure/kubernetes/proxy/testdata/deployments/bootstrap.yaml b/internal/infrastructure/kubernetes/proxy/testdata/deployments/bootstrap.yaml
index 13f4c8669..08b968fce 100644
--- a/internal/infrastructure/kubernetes/proxy/testdata/deployments/bootstrap.yaml
+++ b/internal/infrastructure/kubernetes/proxy/testdata/deployments/bootstrap.yaml
@@ -59,7 +59,7 @@ spec:
             fieldRef:
               apiVersion: v1
               fieldPath: metadata.name
-        image: docker.io/envoyproxy/envoy:distroless-v1.33.0
+        image: docker.io/envoyproxy/envoy:distroless-v1.33.1
         imagePullPolicy: IfNotPresent
         lifecycle:
           preStop:
diff --git a/internal/infrastructure/kubernetes/proxy/testdata/deployments/component-level.yaml b/internal/infrastructure/kubernetes/proxy/testdata/deployments/component-level.yaml
index 13008ce63..553420ca2 100644
--- a/internal/infrastructure/kubernetes/proxy/testdata/deployments/component-level.yaml
+++ b/internal/infrastructure/kubernetes/proxy/testdata/deployments/component-level.yaml
@@ -60,7 +60,7 @@ spec:
             fieldRef:
               apiVersion: v1
               fieldPath: metadata.name
-        image: docker.io/envoyproxy/envoy:distroless-v1.33.0
+        image: docker.io/envoyproxy/envoy:distroless-v1.33.1
         imagePullPolicy: IfNotPresent
         lifecycle:
           preStop:
diff --git a/internal/infrastructure/kubernetes/proxy/testdata/deployments/default.yaml b/internal/infrastructure/kubernetes/proxy/testdata/deployments/default.yaml
index 6d7bf9bad..6cff30836 100644
--- a/internal/infrastructure/kubernetes/proxy/testdata/deployments/default.yaml
+++ b/internal/infrastructure/kubernetes/proxy/testdata/deployments/default.yaml
@@ -231,7 +231,7 @@ spec:
             fieldRef:
               apiVersion: v1
               fieldPath: metadata.name
-        image: docker.io/envoyproxy/envoy:distroless-v1.33.0
+        image: docker.io/envoyproxy/envoy:distroless-v1.33.1
         imagePullPolicy: IfNotPresent
         lifecycle:
           preStop:
diff --git a/internal/infrastructure/kubernetes/proxy/testdata/deployments/disable-prometheus.yaml b/internal/infrastructure/kubernetes/proxy/testdata/deployments/disable-prometheus.yaml
index d84719122..452b3a95b 100644
--- a/internal/infrastructure/kubernetes/proxy/testdata/deployments/disable-prometheus.yaml
+++ b/internal/infrastructure/kubernetes/proxy/testdata/deployments/disable-prometheus.yaml
@@ -202,7 +202,7 @@ spec:
             fieldRef:
               apiVersion: v1
               fieldPath: metadata.name
-        image: docker.io/envoyproxy/envoy:distroless-v1.33.0
+        image: docker.io/envoyproxy/envoy:distroless-v1.33.1
         imagePullPolicy: IfNotPresent
         lifecycle:
           preStop:
diff --git a/internal/infrastructure/kubernetes/proxy/testdata/deployments/dual-stack.yaml b/internal/infrastructure/kubernetes/proxy/testdata/deployments/dual-stack.yaml
index 5802fc619..999be154d 100644
--- a/internal/infrastructure/kubernetes/proxy/testdata/deployments/dual-stack.yaml
+++ b/internal/infrastructure/kubernetes/proxy/testdata/deployments/dual-stack.yaml
@@ -232,7 +232,7 @@ spec:
             fieldRef:
               apiVersion: v1
               fieldPath: metadata.name
-        image: docker.io/envoyproxy/envoy:distroless-v1.33.0
+        image: docker.io/envoyproxy/envoy:distroless-v1.33.1
         imagePullPolicy: IfNotPresent
         lifecycle:
           preStop:
diff --git a/internal/infrastructure/kubernetes/proxy/testdata/deployments/ipv6.yaml b/internal/infrastructure/kubernetes/proxy/testdata/deployments/ipv6.yaml
index 15ec06ab5..128272808 100644
--- a/internal/infrastructure/kubernetes/proxy/testdata/deployments/ipv6.yaml
+++ b/internal/infrastructure/kubernetes/proxy/testdata/deployments/ipv6.yaml
@@ -232,7 +232,7 @@ spec:
             fieldRef:
               apiVersion: v1
               fieldPath: metadata.name
-        image: docker.io/envoyproxy/envoy:distroless-v1.33.0
+        image: docker.io/envoyproxy/envoy:distroless-v1.33.1
         imagePullPolicy: IfNotPresent
         lifecycle:
           preStop:
diff --git a/internal/infrastructure/kubernetes/proxy/testdata/deployments/override-labels-and-annotations.yaml b/internal/infrastructure/kubernetes/proxy/testdata/deployments/override-labels-and-annotations.yaml
index c8b74296d..4bc8d3f68 100644
--- a/internal/infrastructure/kubernetes/proxy/testdata/deployments/override-labels-and-annotations.yaml
+++ b/internal/infrastructure/kubernetes/proxy/testdata/deployments/override-labels-and-annotations.yaml
@@ -240,7 +240,7 @@ spec:
             fieldRef:
               apiVersion: v1
               fieldPath: metadata.name
-        image: docker.io/envoyproxy/envoy:distroless-v1.33.0
+        image: docker.io/envoyproxy/envoy:distroless-v1.33.1
         imagePullPolicy: IfNotPresent
         lifecycle:
           preStop:
diff --git a/internal/infrastructure/kubernetes/proxy/testdata/deployments/patch-deployment.yaml b/internal/infrastructure/kubernetes/proxy/testdata/deployments/patch-deployment.yaml
index bbf82b8fc..03cc3d284 100644
--- a/internal/infrastructure/kubernetes/proxy/testdata/deployments/patch-deployment.yaml
+++ b/internal/infrastructure/kubernetes/proxy/testdata/deployments/patch-deployment.yaml
@@ -231,7 +231,7 @@ spec:
             fieldRef:
               apiVersion: v1
               fieldPath: metadata.name
-        image: docker.io/envoyproxy/envoy:distroless-v1.33.0
+        image: docker.io/envoyproxy/envoy:distroless-v1.33.1
         imagePullPolicy: IfNotPresent
         lifecycle:
           preStop:
diff --git a/internal/infrastructure/kubernetes/proxy/testdata/deployments/shutdown-manager.yaml b/internal/infrastructure/kubernetes/proxy/testdata/deployments/shutdown-manager.yaml
index 0f44f5ad4..3e2778cd8 100644
--- a/internal/infrastructure/kubernetes/proxy/testdata/deployments/shutdown-manager.yaml
+++ b/internal/infrastructure/kubernetes/proxy/testdata/deployments/shutdown-manager.yaml
@@ -231,7 +231,7 @@ spec:
             fieldRef:
               apiVersion: v1
               fieldPath: metadata.name
-        image: docker.io/envoyproxy/envoy:distroless-v1.33.0
+        image: docker.io/envoyproxy/envoy:distroless-v1.33.1
         imagePullPolicy: IfNotPresent
         lifecycle:
           preStop:
diff --git a/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-annotations.yaml b/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-annotations.yaml
index 029b1a0ff..dcd6873ed 100644
--- a/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-annotations.yaml
+++ b/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-annotations.yaml
@@ -236,7 +236,7 @@ spec:
             fieldRef:
               apiVersion: v1
               fieldPath: metadata.name
-        image: docker.io/envoyproxy/envoy:distroless-v1.33.0
+        image: docker.io/envoyproxy/envoy:distroless-v1.33.1
         imagePullPolicy: IfNotPresent
         lifecycle:
           preStop:
diff --git a/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-concurrency.yaml b/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-concurrency.yaml
index f39bbdf46..0c787d268 100644
--- a/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-concurrency.yaml
+++ b/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-concurrency.yaml
@@ -60,7 +60,7 @@ spec:
             fieldRef:
               apiVersion: v1
               fieldPath: metadata.name
-        image: docker.io/envoyproxy/envoy:distroless-v1.33.0
+        image: docker.io/envoyproxy/envoy:distroless-v1.33.1
         imagePullPolicy: IfNotPresent
         lifecycle:
           preStop:
diff --git a/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-empty-memory-limits.yaml b/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-empty-memory-limits.yaml
index b2082c4fe..dce47dfd7 100644
--- a/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-empty-memory-limits.yaml
+++ b/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-empty-memory-limits.yaml
@@ -231,7 +231,7 @@ spec:
             fieldRef:
               apiVersion: v1
               fieldPath: metadata.name
-        image: docker.io/envoyproxy/envoy:distroless-v1.33.0
+        image: docker.io/envoyproxy/envoy:distroless-v1.33.1
         imagePullPolicy: IfNotPresent
         lifecycle:
           preStop:
diff --git a/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-extra-args.yaml b/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-extra-args.yaml
index 644719764..262570814 100644
--- a/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-extra-args.yaml
+++ b/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-extra-args.yaml
@@ -233,7 +233,7 @@ spec:
             fieldRef:
               apiVersion: v1
               fieldPath: metadata.name
-        image: docker.io/envoyproxy/envoy:distroless-v1.33.0
+        image: docker.io/envoyproxy/envoy:distroless-v1.33.1
         imagePullPolicy: IfNotPresent
         lifecycle:
           preStop:
diff --git a/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-image-pull-secrets.yaml b/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-image-pull-secrets.yaml
index 97069c09b..71920b776 100644
--- a/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-image-pull-secrets.yaml
+++ b/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-image-pull-secrets.yaml
@@ -231,7 +231,7 @@ spec:
             fieldRef:
               apiVersion: v1
               fieldPath: metadata.name
-        image: docker.io/envoyproxy/envoy:distroless-v1.33.0
+        image: docker.io/envoyproxy/envoy:distroless-v1.33.1
         imagePullPolicy: IfNotPresent
         lifecycle:
           preStop:
diff --git a/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-name.yaml b/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-name.yaml
index 9b531fbb0..d63face02 100644
--- a/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-name.yaml
+++ b/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-name.yaml
@@ -231,7 +231,7 @@ spec:
             fieldRef:
               apiVersion: v1
               fieldPath: metadata.name
-        image: docker.io/envoyproxy/envoy:distroless-v1.33.0
+        image: docker.io/envoyproxy/envoy:distroless-v1.33.1
         imagePullPolicy: IfNotPresent
         lifecycle:
           preStop:
diff --git a/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-node-selector.yaml b/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-node-selector.yaml
index 9354ff943..fec95a000 100644
--- a/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-node-selector.yaml
+++ b/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-node-selector.yaml
@@ -231,7 +231,7 @@ spec:
             fieldRef:
               apiVersion: v1
               fieldPath: metadata.name
-        image: docker.io/envoyproxy/envoy:distroless-v1.33.0
+        image: docker.io/envoyproxy/envoy:distroless-v1.33.1
         imagePullPolicy: IfNotPresent
         lifecycle:
           preStop:
diff --git a/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-topology-spread-constraints.yaml b/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-topology-spread-constraints.yaml
index 942324795..f8f2159cc 100644
--- a/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-topology-spread-constraints.yaml
+++ b/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-topology-spread-constraints.yaml
@@ -231,7 +231,7 @@ spec:
             fieldRef:
               apiVersion: v1
               fieldPath: metadata.name
-        image: docker.io/envoyproxy/envoy:distroless-v1.33.0
+        image: docker.io/envoyproxy/envoy:distroless-v1.33.1
         imagePullPolicy: IfNotPresent
         lifecycle:
           preStop:
diff --git a/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/default.yaml b/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/default.yaml
index 84af681d9..6af6849e3 100644
--- a/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/default.yaml
+++ b/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/default.yaml
@@ -86,7 +86,7 @@ spec:
           value: :19001
         - name: PROMETHEUS_MAPPER_YAML
           value: /etc/statsd-exporter/conf.yaml
-        image: docker.io/envoyproxy/ratelimit:ae4cee11
+        image: docker.io/envoyproxy/ratelimit:0141a24f
         imagePullPolicy: IfNotPresent
         name: envoy-ratelimit
         ports:
diff --git a/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/disable-prometheus.yaml b/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/disable-prometheus.yaml
index de4c83da5..017b8154e 100644
--- a/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/disable-prometheus.yaml
+++ b/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/disable-prometheus.yaml
@@ -76,7 +76,7 @@ spec:
           value: tcp
         - name: REDIS_URL
           value: redis.redis.svc:6379
-        image: docker.io/envoyproxy/ratelimit:ae4cee11
+        image: docker.io/envoyproxy/ratelimit:0141a24f
         imagePullPolicy: IfNotPresent
         name: envoy-ratelimit
         ports:
diff --git a/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/enable-tracing-custom.yaml b/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/enable-tracing-custom.yaml
index 6971dc186..b901fc37f 100644
--- a/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/enable-tracing-custom.yaml
+++ b/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/enable-tracing-custom.yaml
@@ -101,7 +101,7 @@ spec:
           value: "0.6"
         - name: OTEL_EXPORTER_OTLP_ENDPOINT
           value: http://trace-collector.envoy-gateway-system.svc.cluster.local:4317
-        image: docker.io/envoyproxy/ratelimit:ae4cee11
+        image: docker.io/envoyproxy/ratelimit:0141a24f
         imagePullPolicy: IfNotPresent
         name: envoy-ratelimit
         ports:
diff --git a/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/enable-tracing.yaml b/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/enable-tracing.yaml
index b78ec434d..386b108a3 100644
--- a/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/enable-tracing.yaml
+++ b/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/enable-tracing.yaml
@@ -101,7 +101,7 @@ spec:
           value: "1.0"
         - name: OTEL_EXPORTER_OTLP_ENDPOINT
           value: http://trace-collector.envoy-gateway-system.svc.cluster.local:4318
-        image: docker.io/envoyproxy/ratelimit:ae4cee11
+        image: docker.io/envoyproxy/ratelimit:0141a24f
         imagePullPolicy: IfNotPresent
         name: envoy-ratelimit
         ports:
diff --git a/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/merge-annotations.yaml b/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/merge-annotations.yaml
index 945e60e67..97ceb278f 100644
--- a/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/merge-annotations.yaml
+++ b/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/merge-annotations.yaml
@@ -88,7 +88,7 @@ spec:
           value: :19001
         - name: PROMETHEUS_MAPPER_YAML
           value: /etc/statsd-exporter/conf.yaml
-        image: docker.io/envoyproxy/ratelimit:ae4cee11
+        image: docker.io/envoyproxy/ratelimit:0141a24f
         imagePullPolicy: IfNotPresent
         name: envoy-ratelimit
         ports:
diff --git a/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/merge-labels.yaml b/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/merge-labels.yaml
index 9cd149a10..69a0153b6 100644
--- a/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/merge-labels.yaml
+++ b/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/merge-labels.yaml
@@ -88,7 +88,7 @@ spec:
           value: :19001
         - name: PROMETHEUS_MAPPER_YAML
           value: /etc/statsd-exporter/conf.yaml
-        image: docker.io/envoyproxy/ratelimit:ae4cee11
+        image: docker.io/envoyproxy/ratelimit:0141a24f
         imagePullPolicy: IfNotPresent
         name: envoy-ratelimit
         ports:
diff --git a/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/patch-deployment.yaml b/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/patch-deployment.yaml
index 21b6e9fbf..c93d833bd 100644
--- a/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/patch-deployment.yaml
+++ b/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/patch-deployment.yaml
@@ -86,7 +86,7 @@ spec:
           value: :19001
         - name: PROMETHEUS_MAPPER_YAML
           value: /etc/statsd-exporter/conf.yaml
-        image: docker.io/envoyproxy/ratelimit:ae4cee11
+        image: docker.io/envoyproxy/ratelimit:0141a24f
         imagePullPolicy: IfNotPresent
         name: envoy-ratelimit
         ports:
diff --git a/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/with-node-selector.yaml b/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/with-node-selector.yaml
index 4700c613c..8d979a863 100644
--- a/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/with-node-selector.yaml
+++ b/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/with-node-selector.yaml
@@ -86,7 +86,7 @@ spec:
           value: :19001
         - name: PROMETHEUS_MAPPER_YAML
           value: /etc/statsd-exporter/conf.yaml
-        image: docker.io/envoyproxy/ratelimit:ae4cee11
+        image: docker.io/envoyproxy/ratelimit:0141a24f
         imagePullPolicy: IfNotPresent
         name: envoy-ratelimit
         ports:
diff --git a/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/with-topology-spread-constraints.yaml b/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/with-topology-spread-constraints.yaml
index c784904d2..530bedf02 100644
--- a/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/with-topology-spread-constraints.yaml
+++ b/internal/infrastructure/kubernetes/ratelimit/testdata/deployments/with-topology-spread-constraints.yaml
@@ -86,7 +86,7 @@ spec:
           value: :19001
         - name: PROMETHEUS_MAPPER_YAML
           value: /etc/statsd-exporter/conf.yaml
-        image: docker.io/envoyproxy/ratelimit:ae4cee11
+        image: docker.io/envoyproxy/ratelimit:0141a24f
         imagePullPolicy: IfNotPresent
         name: envoy-ratelimit
         ports:
diff --git a/internal/provider/kubernetes/kubernetes_test.go b/internal/provider/kubernetes/kubernetes_test.go
index 541237c2f..4665f510e 100644
--- a/internal/provider/kubernetes/kubernetes_test.go
+++ b/internal/provider/kubernetes/kubernetes_test.go
@@ -1429,20 +1429,22 @@ func TestNamespaceSelectorProvider(t *testing.T) {
 		require.NoError(t, cli.Delete(ctx, nonWatchedSvc))
 	}()
 
+	watchedServiceBackendRef := test.GetServiceBackendRef(types.NamespacedName{Name: watchedSvc.Name}, 80)
 	watchedHTTPRoute := test.GetHTTPRoute(types.NamespacedName{
 		Namespace: watchedNS.Name,
 		Name:      "watched-http-route",
-	}, watchedGateway.Name, types.NamespacedName{Name: watchedSvc.Name}, 80, "")
+	}, watchedGateway.Name, watchedServiceBackendRef, "")
 
 	require.NoError(t, cli.Create(ctx, watchedHTTPRoute))
 	defer func() {
 		require.NoError(t, cli.Delete(ctx, watchedHTTPRoute))
 	}()
 
+	nonWatchedServiceBackendRef := test.GetServiceBackendRef(types.NamespacedName{Name: nonWatchedSvc.Name}, 8001)
 	nonWatchedHTTPRoute := test.GetHTTPRoute(types.NamespacedName{
 		Namespace: nonWatchedNS.Name,
 		Name:      "non-watched-http-route",
-	}, nonWatchedGateway.Name, types.NamespacedName{Name: nonWatchedSvc.Name}, 8001, "")
+	}, nonWatchedGateway.Name, nonWatchedServiceBackendRef, "")
 	require.NoError(t, cli.Create(ctx, nonWatchedHTTPRoute))
 	defer func() {
 		require.NoError(t, cli.Delete(ctx, nonWatchedHTTPRoute))
diff --git a/internal/provider/kubernetes/predicates_test.go b/internal/provider/kubernetes/predicates_test.go
index dc0d70cdc..5c9dbba1c 100644
--- a/internal/provider/kubernetes/predicates_test.go
+++ b/internal/provider/kubernetes/predicates_test.go
@@ -123,11 +123,12 @@ func TestGatewayClassHasMatchingNamespaceLabels(t *testing.T) {
 				Build(),
 		}
 		t.Run(tc.name, func(t *testing.T) {
+			sampleServiceBackendRef := test.GetServiceBackendRef(types.NamespacedName{Name: "service"}, 80)
 			res := r.hasMatchingNamespaceLabels(
 				test.GetHTTPRoute(types.NamespacedName{
 					Namespace: ns,
 					Name:      "httproute-test",
-				}, "scheduled-status-test", types.NamespacedName{Name: "service"}, 80, ""))
+				}, "scheduled-status-test", sampleServiceBackendRef, ""))
 			require.Equal(t, tc.expect, res)
 		})
 	}
@@ -412,7 +413,10 @@ func TestValidateSecretForReconcile(t *testing.T) {
 // TestValidateEndpointSliceForReconcile tests the validateEndpointSliceForReconcile
 // predicate function.
 func TestValidateEndpointSliceForReconcile(t *testing.T) {
+	sampleGatewayClass := test.GetGatewayClass("test-gc", egv1a1.GatewayControllerName, nil)
 	sampleGateway := test.GetGateway(types.NamespacedName{Namespace: "default", Name: "scheduled-status-test"}, "test-gc", 8080)
+	sampleServiceBackendRef := test.GetServiceBackendRef(types.NamespacedName{Name: "service"}, 80)
+	sampleServiceImportBackendRef := test.GetServiceImportBackendRef(types.NamespacedName{Name: "imported-service"}, 80)
 
 	testCases := []struct {
 		name          string
@@ -423,30 +427,86 @@ func TestValidateEndpointSliceForReconcile(t *testing.T) {
 		{
 			name: "route service but no routes exist",
 			configs: []client.Object{
-				test.GetGatewayClass("test-gc", egv1a1.GatewayControllerName, nil),
+				sampleGatewayClass,
 				sampleGateway,
 			},
-			endpointSlice: test.GetEndpointSlice(types.NamespacedName{Name: "endpointslice"}, "service"),
+			endpointSlice: test.GetEndpointSlice(types.NamespacedName{Name: "endpointslice"}, "service", false),
 			expect:        false,
 		},
 		{
 			name: "http route service routes exist, but endpointslice is associated with another service",
 			configs: []client.Object{
-				test.GetGatewayClass("test-gc", egv1a1.GatewayControllerName, nil),
+				sampleGatewayClass,
 				sampleGateway,
-				test.GetHTTPRoute(types.NamespacedName{Name: "httproute-test"}, "scheduled-status-test", types.NamespacedName{Name: "service"}, 80, ""),
+				test.GetHTTPRoute(types.NamespacedName{Name: "httproute-test"}, "scheduled-status-test", sampleServiceBackendRef, ""),
 			},
-			endpointSlice: test.GetEndpointSlice(types.NamespacedName{Name: "endpointslice"}, "other-service"),
+			endpointSlice: test.GetEndpointSlice(types.NamespacedName{Name: "endpointslice"}, "other-service", false),
 			expect:        false,
 		},
 		{
 			name: "http route service routes exist",
+			configs: []client.Object{
+				sampleGatewayClass,
+				sampleGateway,
+				test.GetHTTPRoute(types.NamespacedName{Name: "httproute-test"}, "scheduled-status-test", sampleServiceBackendRef, ""),
+			},
+			endpointSlice: test.GetEndpointSlice(types.NamespacedName{Name: "endpointslice"}, "service", false),
+			expect:        true,
+		},
+		{
+			name: "http route serviceimport routes exist",
+			configs: []client.Object{
+				sampleGatewayClass,
+				sampleGateway,
+				test.GetHTTPRoute(types.NamespacedName{Name: "httproute-test"}, "scheduled-status-test", sampleServiceImportBackendRef, ""),
+			},
+			endpointSlice: test.GetEndpointSlice(types.NamespacedName{Name: "endpointslice"}, "imported-service", true),
+			expect:        true,
+		},
+		{
+			name: "mirrored backend route exists",
 			configs: []client.Object{
 				test.GetGatewayClass("test-gc", egv1a1.GatewayControllerName, nil),
 				sampleGateway,
-				test.GetHTTPRoute(types.NamespacedName{Name: "httproute-test"}, "scheduled-status-test", types.NamespacedName{Name: "service"}, 80, ""),
+				&gwapiv1.HTTPRoute{
+					ObjectMeta: metav1.ObjectMeta{
+						Name: "httproute-test",
+					},
+					Spec: gwapiv1.HTTPRouteSpec{
+						CommonRouteSpec: gwapiv1.CommonRouteSpec{
+							ParentRefs: []gwapiv1.ParentReference{
+								{Name: gwapiv1.ObjectName("scheduled-status-test")},
+							},
+						},
+						Rules: []gwapiv1.HTTPRouteRule{
+							{
+								BackendRefs: []gwapiv1.HTTPBackendRef{
+									{
+										BackendRef: gwapiv1.BackendRef{
+											BackendObjectReference: gwapiv1.BackendObjectReference{
+												Name: gwapiv1.ObjectName("service"),
+												Port: ptr.To(gwapiv1.PortNumber(80)),
+											},
+										},
+									},
+								},
+								Filters: []gwapiv1.HTTPRouteFilter{
+									{
+										Type: gwapiv1.HTTPRouteFilterRequestMirror,
+										RequestMirror: &gwapiv1.HTTPRequestMirrorFilter{
+											BackendRef: gwapiv1.BackendObjectReference{
+												Name: "mirror-service",
+												Port: ptr.To(gwapiv1.PortNumber(80)),
+											},
+										},
+									},
+								},
+							},
+						},
+					},
+				},
 			},
-			endpointSlice: test.GetEndpointSlice(types.NamespacedName{Name: "endpointslice"}, "service"),
+			endpointSlice: test.GetEndpointSlice(types.NamespacedName{Name: "endpointslice"}, "mirror-service", false),
 			expect:        true,
 		},
 	}
@@ -551,6 +611,7 @@ func TestValidateServiceForReconcile(t *testing.T) {
 			},
 		},
 	}
+	sampleServiceBackendRef := test.GetServiceBackendRef(types.NamespacedName{Name: "service"}, 80)
 
 	testCases := []struct {
 		name    string
@@ -614,7 +675,7 @@ func TestValidateServiceForReconcile(t *testing.T) {
 			configs: []client.Object{
 				test.GetGatewayClass("test-gc", egv1a1.GatewayControllerName, nil),
 				sampleGateway,
-				test.GetHTTPRoute(types.NamespacedName{Name: "httproute-test"}, "scheduled-status-test", types.NamespacedName{Name: "service"}, 80, ""),
+				test.GetHTTPRoute(types.NamespacedName{Name: "httproute-test"}, "scheduled-status-test", sampleServiceBackendRef, ""),
 			},
 			service: test.GetService(types.NamespacedName{Name: "service"}, nil, nil),
 			expect:  true,
@@ -626,7 +687,7 @@ func TestValidateServiceForReconcile(t *testing.T) {
 			name: "route service routes exist but with non-existing gateway reference",
 			configs: []client.Object{
 				test.GetGatewayClass("test-gc", egv1a1.GatewayControllerName, nil),
-				test.GetHTTPRoute(types.NamespacedName{Name: "httproute-test"}, "scheduled-status-test", types.NamespacedName{Name: "service"}, 80, ""),
+				test.GetHTTPRoute(types.NamespacedName{Name: "httproute-test"}, "scheduled-status-test", sampleServiceBackendRef, ""),
 			},
 			service: test.GetService(types.NamespacedName{Name: "service"}, nil, nil),
 			expect:  true,
@@ -1036,13 +1097,17 @@ func TestCheckObjectNamespaceLabels(t *testing.T) {
 	}{
 		{
 			name: "matching labels of namespace of the object is a subset of namespaceLabels",
-			object: test.GetHTTPRoute(types.NamespacedName{
-				Name:      "foo-route",
-				Namespace: "foo",
-			}, "eg", types.NamespacedName{
-				Name:      "foo-svc",
-				Namespace: "foo",
-			}, 8080, ""),
+			object: test.GetHTTPRoute(
+				types.NamespacedName{
+					Name:      "foo-route",
+					Namespace: "foo",
+				},
+				"eg",
+				test.GetServiceBackendRef(types.NamespacedName{
+					Name:      "foo-svc",
+					Namespace: "foo",
+				}, 8080),
+				""),
 			ns: &corev1.Namespace{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: "foo",
@@ -1056,13 +1121,17 @@ func TestCheckObjectNamespaceLabels(t *testing.T) {
 		},
 		{
 			name: "non-matching labels of namespace of the object is a subset of namespaceLabels",
-			object: test.GetHTTPRoute(types.NamespacedName{
-				Name:      "bar-route",
-				Namespace: "bar",
-			}, "eg", types.NamespacedName{
-				Name:      "bar-svc",
-				Namespace: "bar",
-			}, 8080, ""),
+			object: test.GetHTTPRoute(
+				types.NamespacedName{
+					Name:      "bar-route",
+					Namespace: "bar",
+				},
+				"eg",
+				test.GetServiceBackendRef(types.NamespacedName{
+					Name:      "bar-svc",
+					Namespace: "bar",
+				}, 8080),
+				""),
 			ns: &corev1.Namespace{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: "bar",
@@ -1216,6 +1285,7 @@ func TestValidateHTTPRouteFilerForReconcile(t *testing.T) {
 	sampleGWC := test.GetGatewayClass("test-gc", egv1a1.GatewayControllerName, nil)
 	sampleGateway := test.GetGateway(types.NamespacedName{Namespace: "default", Name: "scheduled-status-test"}, "test-gc", 8080)
 	sampleService := test.GetService(types.NamespacedName{Name: "service"}, nil, nil)
+	sampleServiceBackendRef := test.GetServiceBackendRef(types.NamespacedName{Name: "service"}, 80)
 	sampleHTTPRouteFilter := test.GetHTTPRouteFilter(types.NamespacedName{Name: "httproutefilter"})
 
 	testCases := []struct {
@@ -1242,7 +1312,7 @@ func TestValidateHTTPRouteFilerForReconcile(t *testing.T) {
 				sampleGateway,
 				sampleService,
 				sampleHTTPRouteFilter,
-				test.GetHTTPRoute(types.NamespacedName{Name: "httproute-test"}, "scheduled-status-test", types.NamespacedName{Name: "service"}, 80, "httproutefilter"),
+				test.GetHTTPRoute(types.NamespacedName{Name: "httproute-test"}, "scheduled-status-test", sampleServiceBackendRef, "httproutefilter"),
 			},
 			httpRouteFilter: sampleHTTPRouteFilter,
 			expect:          true,
diff --git a/internal/provider/kubernetes/test/utils.go b/internal/provider/kubernetes/test/utils.go
index 77bc50c5e..a6b2ef325 100644
--- a/internal/provider/kubernetes/test/utils.go
+++ b/internal/provider/kubernetes/test/utils.go
@@ -15,8 +15,11 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	gwapiv1 "sigs.k8s.io/gateway-api/apis/v1"
 	gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
+	mcsapiv1a1 "sigs.k8s.io/mcs-api/pkg/apis/v1alpha1"
 
 	egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1"
+	"github.com/envoyproxy/gateway/internal/gatewayapi"
+	"github.com/envoyproxy/gateway/internal/gatewayapi/resource"
 )
 
 type GroupKindNamespacedName struct {
@@ -107,8 +110,24 @@ func GetSecret(nsName types.NamespacedName) *corev1.Secret {
 	}
 }
 
+func GetServiceBackendRef(name types.NamespacedName, port int32) gwapiv1.BackendObjectReference {
+	return gwapiv1.BackendObjectReference{
+		Name: gwapiv1.ObjectName(name.Name),
+		Port: ptr.To(gwapiv1.PortNumber(port)),
+	}
+}
+
+func GetServiceImportBackendRef(name types.NamespacedName, port int32) gwapiv1.BackendObjectReference {
+	return gwapiv1.BackendObjectReference{
+		Name:  gwapiv1.ObjectName(name.Name),
+		Port:  ptr.To(gwapiv1.PortNumber(port)),
+		Kind:  gatewayapi.KindPtr(resource.KindServiceImport),
+		Group: gatewayapi.GroupPtr(mcsapiv1a1.GroupName),
+	}
+}
+
 // GetHTTPRoute returns a sample HTTPRoute with a parent reference.
-func GetHTTPRoute(nsName types.NamespacedName, parent string, serviceName types.NamespacedName, port int32, httpRouteFilterName string) *gwapiv1.HTTPRoute {
+func GetHTTPRoute(nsName types.NamespacedName, parent string, backendRef gwapiv1.BackendObjectReference, httpRouteFilterName string) *gwapiv1.HTTPRoute {
 	httpRoute := &gwapiv1.HTTPRoute{
 		ObjectMeta: metav1.ObjectMeta{
 			Namespace: nsName.Namespace,
@@ -125,10 +144,7 @@ func GetHTTPRoute(nsName types.NamespacedName, parent string, serviceName types.
 					BackendRefs: []gwapiv1.HTTPBackendRef{
 						{
 							BackendRef: gwapiv1.BackendRef{
-								BackendObjectReference: gwapiv1.BackendObjectReference{
-									Name: gwapiv1.ObjectName(serviceName.Name),
-									Port: ptr.To(gwapiv1.PortNumber(port)),
-								},
+								BackendObjectReference: backendRef,
 							},
 						},
 					},
@@ -349,12 +365,19 @@ func GetService(nsName types.NamespacedName, labels map[string]string, ports map
 }
 
 // GetEndpointSlice returns a sample EndpointSlice.
-func GetEndpointSlice(nsName types.NamespacedName, svcName string) *discoveryv1.EndpointSlice {
+func GetEndpointSlice(nsName types.NamespacedName, svcName string, isServiceImport bool) *discoveryv1.EndpointSlice {
+	var labels map[string]string
+	if isServiceImport {
+		labels = map[string]string{mcsapiv1a1.LabelServiceName: svcName}
+	} else {
+		labels = map[string]string{discoveryv1.LabelServiceName: svcName}
+	}
+
 	return &discoveryv1.EndpointSlice{
 		ObjectMeta: metav1.ObjectMeta{
 			Name:      nsName.Name,
 			Namespace: nsName.Namespace,
-			Labels:    map[string]string{discoveryv1.LabelServiceName: svcName},
+			Labels:    labels,
 		},
 		Endpoints: []discoveryv1.Endpoint{
 			{
diff --git a/internal/wasm/premissioncache_test.go b/internal/wasm/premissioncache_test.go
new file mode 100644
index 000000000..2fdb95ff1
--- /dev/null
+++ b/internal/wasm/premissioncache_test.go
@@ -0,0 +1,282 @@
+// Copyright Envoy Gateway Authors
+// SPDX-License-Identifier: Apache-2.0
+// The full text of the Apache license is available in the LICENSE file at
+// the root of the repo.
+
+// Copyright Istio Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package wasm
+
+import (
+	"context"
+	"fmt"
+	"net/http"
+	"net/http/httptest"
+	"net/url"
+	"sync"
+	"testing"
+	"time"
+
+	"github.com/google/go-containerregistry/pkg/registry"
+	"github.com/stretchr/testify/require"
+
+	egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1"
+	"github.com/envoyproxy/gateway/internal/logging"
+)
+
+func TestPermissionCache(t *testing.T) {
+	lock := sync.Mutex{}
+	// Flag to control whether the permission check should fail.
+	failPermissionCheck := false
+
+	// Set up a fake registry for OCI images.
+	reg := registry.New()
+	tos := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		lock.Lock()
+		defer lock.Unlock()
+		if failPermissionCheck {
+			http.Error(w, "permission denied", http.StatusUnauthorized)
+			return
+		}
+		reg.ServeHTTP(w, r)
+	}))
+	defer tos.Close()
+	ou, err := url.Parse(tos.URL)
+	if err != nil {
+		t.Fatal(err)
+	}
+	_, _ = setupOCIRegistry(t, ou.Host)
+	ociURLWithTag := fmt.Sprintf("oci://%s/test/valid/docker:v0.1.0", ou.Host)
+	ociURLWithLatestTag := fmt.Sprintf("oci://%s/test/valid/docker:latest", ou.Host)
+	image, _ := url.Parse(ociURLWithTag)
+	latestImage, _ := url.Parse(ociURLWithLatestTag)
+	secret := []byte("")
+
+	t.Run("Cached permission should be updated", func(t *testing.T) {
+		lock.Lock()
+		failPermissionCheck = false
+		lock.Unlock()
+
+		ctx := context.Background()
+		defer ctx.Done()
+		cache, entry := setupTestPermissionCache(
+			permissionCacheOptions{
+				checkInterval:    10 * time.Nanosecond,
+				permissionExpiry: 10 * time.Nanosecond,
+			},
+			image,
+			latestImage,
+			secret)
+		cache.Start(ctx)
+
+		lastAccessTime := entry.lastAccess
+		lastCheckTime := entry.lastCheck
+
+		time.Sleep(1 * time.Millisecond)
+		allowed, err := cache.IsAllowed(context.Background(), image, secret, true)
+		require.True(
+			t,
+			allowed,
+			"permission should be rechecked and allowed after permission expired")
+		require.NoError(
+			t,
+			err,
+			"permission should be rechecked and allowed after permission expired")
+
+		entry, ok := cache.getForTest(entry.key())
+		require.True(t, ok, "cache entry should exist")
+		require.True(t, entry.lastAccess.After(lastAccessTime), "last access time should be updated")
+		require.True(t, entry.lastCheck.After(lastCheckTime), "last check time should be updated")
+	})
+
+	t.Run("Cached permission failed after recheck", func(t *testing.T) {
+		lock.Lock()
+		failPermissionCheck = true
+		lock.Unlock()
+
+		ctx := context.Background()
+		defer ctx.Done()
+		cache, entry := setupTestPermissionCache(
+			permissionCacheOptions{
+				checkInterval:    10 * time.Nanosecond,
+				permissionExpiry: 10 * time.Nanosecond,
+			},
+			image,
+			latestImage,
+			secret)
+		cache.Start(ctx)
+
+		lastAccessTime := entry.lastAccess
+		lastCheckTime := entry.lastCheck
+
+		time.Sleep(1 * time.Millisecond)
+		allowed, err := cache.IsAllowed(context.Background(), image, secret, true)
+		require.False(t, isRetriableError(err), "permission check error should not be retriable")
+		require.False(
+			t,
+			allowed,
+			"permission should be rechecked and denied after permission expired and secret is invalid")
+		require.Error(
+			t,
+			err,
+			"permission should be rechecked and denied after permission expired and secret is invalid")
+
+		entry, ok := cache.getForTest(entry.key())
+		require.True(t, ok, "cache entry should exist")
+		require.True(t, entry.lastAccess.After(lastAccessTime), "last access time should be updated")
+		require.True(t, entry.lastCheck.After(lastCheckTime), "last check time should be updated")
+	})
+
+	t.Run("Cached permission should be removed after expiry", func(t *testing.T) {
+		lock.Lock()
+		failPermissionCheck = false
+		lock.Unlock()
+
+		ctx := context.Background()
+		defer ctx.Done()
+		cache, entry := setupTestPermissionCache(
+			permissionCacheOptions{
+				checkInterval: 10 * time.Nanosecond,
+				cacheExpiry:   10 * time.Nanosecond,
+			},
+			image,
+			latestImage,
+			secret)
+		cache.Start(ctx)
+
+		lastAccessTime := entry.lastAccess
+		lastCheckTime := entry.lastCheck
+
+		time.Sleep(1 * time.Millisecond)
+		key := entry.key()
+		entry, ok := cache.getForTest(key)
+		require.False(t, ok, "cache entry should be removed after expiry")
+		allowed, err := cache.IsAllowed(context.Background(), image, secret, true)
+		require.True(t,
+			allowed,
+			"permission should be rechecked and allowed after cache removed")
+		require.NoError(t,
+			err,
+			"permission should be rechecked and allowed after cache removed")
+		entry, ok = cache.getForTest(key)
+		require.True(t, ok, "expired entry should be added after recheck")
+		require.True(t, entry.lastAccess.After(lastAccessTime), "last access time should be updated")
+		require.True(t, entry.lastCheck.After(lastCheckTime), "last check time should be updated")
+	})
+
+	t.Run("Non-exist permission should be checked and cached after first access for allowed permission", func(t *testing.T) {
+		lock.Lock()
+		failPermissionCheck = false
+		lock.Unlock()
+
+		ctx := context.Background()
+		defer ctx.Done()
+		cache, entry := setupTestPermissionCache(
+			permissionCacheOptions{
+				checkInterval: 10 * time.Nanosecond,
+			},
+			image,
+			latestImage,
+			secret)
+		key := entry.key()
+		// remove the cache entry
+		cache.deleteForTest(key)
+		cache.Start(ctx)
+
+		_, ok := cache.getForTest(key)
+		require.False(t, ok, "cache entry should not exist before access")
+
+		now := time.Now()
+		allowed, err := cache.IsAllowed(context.Background(), image, secret, true)
+		require.True(t,
+			allowed,
+			"non-exist permission should be checked and allowed at first access")
+		require.NoError(t,
+			err,
+			"non-exist permission should be checked and allowed at first access")
+
+		entry, ok = cache.getForTest(key)
+		require.True(t, ok, "non-exist permission should be added to the cache after first access ")
+		require.True(t, entry.lastAccess.After(now), "last access time should be updated after first access")
+		require.True(t, entry.lastCheck.After(now), "last check time should be updated after first access")
+	})
+
+	t.Run("Non-exist permission should be checked and cached after first access for denied permission", func(t *testing.T) {
+		lock.Lock()
+		failPermissionCheck = true
+		lock.Unlock()
+
+		ctx := context.Background()
+		defer ctx.Done()
+		cache, entry := setupTestPermissionCache(
+			permissionCacheOptions{
+				checkInterval: 10 * time.Nanosecond,
+			},
+			image,
+			latestImage,
+			secret)
+		key := entry.key()
+		// remove the cache entry
+		cache.deleteForTest(key)
+		cache.Start(ctx)
+
+		_, ok := cache.getForTest(key)
+		require.False(t, ok, "cache entry should not exist before access")
+
+		now := time.Now()
+		allowed, err := cache.IsAllowed(context.Background(), image, secret, true)
+		require.False(t,
+			allowed,
+			"non-exist permission should be checked and denied at first access if secret is invalid")
+		require.Error(t,
+			err,
+			"non-exist permission should be checked and denied at first access if secret is invalid")
+
+		entry, ok = cache.getForTest(key)
+		require.True(t, ok, "non-exist permission should be added to the cache after first access ")
+		require.True(t, entry.lastAccess.After(now), "last access time should be updated after first access")
+		require.True(t, entry.lastCheck.After(now), "last check time should be updated after first access")
+	})
+}
+
+// setupTestPermissionCache sets up a permission cache for testing.
+func setupTestPermissionCache(options permissionCacheOptions, image, latestImage *url.URL, secret []byte) (*permissionCache, permissionCacheEntry) {
+	// Setup the permission cache.
+	cache := newPermissionCache(
+		options,
+		logging.DefaultLogger(egv1a1.LogLevelInfo))
+
+	entry := &permissionCacheEntry{
+		image: image,
+		fetcherOption: &ImageFetcherOption{
+			PullSecret: secret,
+			Insecure:   true,
+		},
+		lastCheck: time.Now(),
+	}
+	cache.Put(entry)
+
+	// Add one more entry for the latest image to test the cache can handle multiple entries correctly.
+	cache.Put(&permissionCacheEntry{
+		image: latestImage,
+		fetcherOption: &ImageFetcherOption{
+			PullSecret: secret,
+			Insecure:   true,
+		},
+		lastCheck: time.Now(),
+	})
+
+	return cache, *entry
+}
diff --git a/site/content/en/latest/install/gateway-helm-api.md b/site/content/en/latest/install/gateway-helm-api.md
index 428286534..146ab24df 100644
--- a/site/content/en/latest/install/gateway-helm-api.md
+++ b/site/content/en/latest/install/gateway-helm-api.md
@@ -66,7 +66,7 @@ The Helm chart for Envoy Gateway
 | global.images.envoyGateway.image | string | `nil` |  |
 | global.images.envoyGateway.pullPolicy | string | `nil` |  |
 | global.images.envoyGateway.pullSecrets | list | `[]` |  |
-| global.images.ratelimit.image | string | `"docker.io/envoyproxy/ratelimit:ae4cee11"` |  |
+| global.images.ratelimit.image | string | `"docker.io/envoyproxy/ratelimit:0141a24f"` |  |
 | global.images.ratelimit.pullPolicy | string | `"IfNotPresent"` |  |
 | global.images.ratelimit.pullSecrets | list | `[]` |  |
 | kubernetesClusterDomain | string | `"cluster.local"` |  |
diff --git a/site/content/zh/latest/install/gateway-helm-api.md b/site/content/zh/latest/install/gateway-helm-api.md
index 428286534..146ab24df 100644
--- a/site/content/zh/latest/install/gateway-helm-api.md
+++ b/site/content/zh/latest/install/gateway-helm-api.md
@@ -66,7 +66,7 @@ The Helm chart for Envoy Gateway
 | global.images.envoyGateway.image | string | `nil` |  |
 | global.images.envoyGateway.pullPolicy | string | `nil` |  |
 | global.images.envoyGateway.pullSecrets | list | `[]` |  |
-| global.images.ratelimit.image | string | `"docker.io/envoyproxy/ratelimit:ae4cee11"` |  |
+| global.images.ratelimit.image | string | `"docker.io/envoyproxy/ratelimit:0141a24f"` |  |
 | global.images.ratelimit.pullPolicy | string | `"IfNotPresent"` |  |
 | global.images.ratelimit.pullSecrets | list | `[]` |  |
 | kubernetesClusterDomain | string | `"cluster.local"` |  |
diff --git a/test/e2e/testdata/accesslog-otel-default.yaml b/test/e2e/testdata/accesslog-otel-default.yaml
new file mode 100644
index 000000000..764e8e02e
--- /dev/null
+++ b/test/e2e/testdata/accesslog-otel-default.yaml
@@ -0,0 +1,61 @@
+apiVersion: gateway.networking.k8s.io/v1
+kind: Gateway
+metadata:
+  name: accesslog-gtw
+  namespace: gateway-conformance-infra
+spec:
+  gatewayClassName: {GATEWAY_CLASS_NAME}
+  listeners:
+    - name: http
+      port: 80
+      protocol: HTTP
+      allowedRoutes:
+        namespaces:
+          from: Same
+  infrastructure:
+    parametersRef:
+      group: gateway.envoyproxy.io
+      kind: EnvoyProxy
+      name: accesslog-otel
+---
+apiVersion: gateway.envoyproxy.io/v1alpha1
+kind: EnvoyProxy
+metadata:
+  name: accesslog-otel
+  namespace: gateway-conformance-infra
+spec:
+  ipFamily: IPv4
+  telemetry:
+    accessLog:
+      settings:
+        - matches:
+            - "'x-envoy-logged' in request.headers"
+          sinks:
+            - type: OpenTelemetry
+              openTelemetry:
+                backendRefs:
+                  - name: otel-collector
+                    namespace: monitoring
+                    port: 4317
+                resources:
+                  k8s.cluster.name: "envoy-gateway"
+  shutdown:
+    drainTimeout: 5s
+    minDrainDuration: 1s
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+  name: accesslog-otel
+  namespace: gateway-conformance-infra
+spec:
+  parentRefs:
+    - name: accesslog-gtw
+  rules:
+    - matches:
+        - path:
+            type: PathPrefix
+            value: /otel
+      backendRefs:
+        - name: infra-backend-v2
+          port: 8080
diff --git a/test/e2e/testdata/accesslog-otel-json.yaml b/test/e2e/testdata/accesslog-otel-json.yaml
new file mode 100644
index 000000000..219315112
--- /dev/null
+++ b/test/e2e/testdata/accesslog-otel-json.yaml
@@ -0,0 +1,79 @@
+apiVersion: gateway.networking.k8s.io/v1
+kind: Gateway
+metadata:
+  name: accesslog-gtw
+  namespace: gateway-conformance-infra
+spec:
+  gatewayClassName: {GATEWAY_CLASS_NAME}
+  listeners:
+    - name: http
+      port: 80
+      protocol: HTTP
+      allowedRoutes:
+        namespaces:
+          from: Same
+  infrastructure:
+    parametersRef:
+      group: gateway.envoyproxy.io
+      kind: EnvoyProxy
+      name: accesslog-otel
+---
+apiVersion: gateway.envoyproxy.io/v1alpha1
+kind: EnvoyProxy
+metadata:
+  name: accesslog-otel
+  namespace: gateway-conformance-infra
+spec:
+  ipFamily: IPv4
+  telemetry:
+    accessLog:
+      settings:
+        - format:
+            type: JSON
+            json:
+              time: "%START_TIME%"
+              method: "%REQ(:METHOD)%"
+              metadata: "%METADATA(ROUTE:envoy-gateway:resources)%"
+              x-envoy-original-path: "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%"
+              protocol: "%PROTOCOL%"
+              responseCode: "%RESPONSE_CODE%"
+              responseFlags: "%RESPONSE_FLAGS%"
+              bytesReceived: "%BYTES_RECEIVED%"
+              bytesSent: "%BYTES_SENT%"
+              duration: "%DURATION%"
+              x-forwarded-for: "%REQ(X-FORWARDED-FOR)%"
+              user-agent: "%REQ(USER-AGENT)%"
+              x-request-id: "%REQ(X-REQUEST-ID)%"
+              authority: "%REQ(:AUTHORITY)%"
+              upstreamHost: "%UPSTREAM_HOST%"
+          matches:
+            - "'x-envoy-logged' in request.headers"
+          sinks:
+            - type: OpenTelemetry
+              openTelemetry:
+                backendRefs:
+                  - name: otel-collector
+                    namespace: monitoring
+                    port: 4317
+                resources:
+                  k8s.cluster.name: "envoy-gateway"
+  shutdown:
+    drainTimeout: 5s
+    minDrainDuration: 1s
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+  name: accesslog-otel
+  namespace: gateway-conformance-infra
+spec:
+  parentRefs:
+    - name: accesslog-gtw
+  rules:
+    - matches:
+        - path:
+            type: PathPrefix
+            value: /otel
+      backendRefs:
+        - name: infra-backend-v2
+          port: 8080
diff --git a/test/e2e/tests/accesslog.go b/test/e2e/tests/accesslog.go
index 26b5caa4a..f346228b1 100644
--- a/test/e2e/tests/accesslog.go
+++ b/test/e2e/tests/accesslog.go
@@ -21,7 +21,7 @@ import (
 )
 
 func init() {
-	ConformanceTests = append(ConformanceTests, FileAccessLogTest, OpenTelemetryTest, ALSTest)
+	ConformanceTests = append(ConformanceTests, FileAccessLogTest, OpenTelemetryTestText, OpenTelemetryTestJSON, ALSTest, OpenTelemetryTestJSONAsDefault)
 }
 
 var FileAccessLogTest = suite.ConformanceTest{
@@ -116,9 +116,9 @@ var FileAccessLogTest = suite.ConformanceTest{
 	},
 }
 
-var OpenTelemetryTest = suite.ConformanceTest{
-	ShortName:   "OpenTelemetryAccessLog",
-	Description: "Make sure OpenTelemetry access log is working",
+var OpenTelemetryTestText = suite.ConformanceTest{
+	ShortName:   "OpenTelemetryTextAccessLog",
+	Description: "Make sure OpenTelemetry text access log is working",
 	Manifests:   []string{"testdata/accesslog-otel.yaml"},
 	Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
 		labels := map[string]string{
@@ -169,6 +169,112 @@ var OpenTelemetryTest = suite.ConformanceTest{
 	},
 }
 
+var OpenTelemetryTestJSONAsDefault = suite.ConformanceTest{
+	ShortName:   "OpenTelemetryAccessLogJSONAsDefault",
+	Description: "Make sure OpenTelemetry JSON access log is working as default when no format or type is specified",
+	Manifests:   []string{"testdata/accesslog-otel-default.yaml"},
+	Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
+		labels := map[string]string{
+			"k8s_namespace_name": "envoy-gateway-system",
+			"exporter":           "OTLP",
+		}
+
+		ns := "gateway-conformance-infra"
+		routeNN := types.NamespacedName{Name: "accesslog-otel", Namespace: ns}
+		gwNN := types.NamespacedName{Name: "accesslog-gtw", Namespace: ns}
+		gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)
+
+		t.Run("Positive", func(t *testing.T) {
+			expectedResponse := httputils.ExpectedResponse{
+				Request: httputils.Request{
+					Path: "/otel",
+					Headers: map[string]string{
+						"x-envoy-logged": "1",
+					},
+				},
+				Response: httputils.Response{
+					StatusCode: 200,
+				},
+				Namespace: ns,
+			}
+			// make sure listener is ready
+			httputils.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, expectedResponse)
+
+			runLogTest(t, suite, gwAddr, expectedResponse, labels, "", 1)
+		})
+
+		t.Run("Negative", func(t *testing.T) {
+			expectedResponse := httputils.ExpectedResponse{
+				Request: httputils.Request{
+					Path: "/otel",
+					// envoy will not log this request without the header x-envoy-logged
+				},
+				Response: httputils.Response{
+					StatusCode: 200,
+				},
+				Namespace: ns,
+			}
+			// make sure listener is ready
+			httputils.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, expectedResponse)
+
+			runLogTest(t, suite, gwAddr, expectedResponse, labels, "", 0)
+		})
+	},
+}
+
+var OpenTelemetryTestJSON = suite.ConformanceTest{
+	ShortName:   "OpenTelemetryAccessLogJSON",
+	Description: "Make sure OpenTelemetry JSON access log is working with custom JSON attributes",
+	Manifests:   []string{"testdata/accesslog-otel-json.yaml"},
+	Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
+		labels := map[string]string{
+			"k8s_namespace_name": "envoy-gateway-system",
+			"exporter":           "OTLP",
+		}
+
+		ns := "gateway-conformance-infra"
+		routeNN := types.NamespacedName{Name: "accesslog-otel", Namespace: ns}
+		gwNN := types.NamespacedName{Name: "accesslog-gtw", Namespace: ns}
+		gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)
+
+		t.Run("Positive", func(t *testing.T) {
+			expectedResponse := httputils.ExpectedResponse{
+				Request: httputils.Request{
+					Path: "/otel",
+					Headers: map[string]string{
+						"x-envoy-logged": "1",
+					},
+				},
+				Response: httputils.Response{
+					StatusCode: 200,
+				},
+				Namespace: ns,
+			}
+			// make sure listener is ready
+			httputils.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, expectedResponse)
+
+			runLogTest(t, suite, gwAddr, expectedResponse, labels, "", 1)
+		})
+
+		t.Run("Negative", func(t *testing.T) {
+			expectedResponse := httputils.ExpectedResponse{
+				Request: httputils.Request{
+					Path: "/otel",
+					// envoy will not log this request without the header x-envoy-logged
+				},
+				Response: httputils.Response{
+					StatusCode: 200,
+				},
+				Namespace: ns,
+			}
+			// make sure listener is ready
+			httputils.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, expectedResponse)
+
+			runLogTest(t, suite, gwAddr, expectedResponse, labels, "", 0)
+		})
+	},
+}
+
 var ALSTest = suite.ConformanceTest{
 	ShortName:   "ALS",
 	Description: "Make sure ALS access log is working",
diff --git a/test/e2e/tests/wasm_oci.go b/test/e2e/tests/wasm_oci.go
index 514ab9373..f9f74cec2 100644
--- a/test/e2e/tests/wasm_oci.go
+++ b/test/e2e/tests/wasm_oci.go
@@ -197,7 +197,7 @@ var OCIWasmTest = suite.ConformanceTest{
 				t, suite.Client,
 				types.NamespacedName{Name: testEEP, Namespace: testNS},
 				suite.ControllerName,
-				ancestorRef, "failed to login to private registry")
+				ancestorRef, "UNAUTHORIZED: authentication required")
 		})
 
 		// Verify that the wasm module can't be loaded if the password is incorrect
@@ -232,7 +232,7 @@ var OCIWasmTest = suite.ConformanceTest{
 				t, suite.Client,
 				types.NamespacedName{Name: testEEP, Namespace: testNS},
 				suite.ControllerName,
-				ancestorRef, "failed to login to private registry")
+				ancestorRef, "UNAUTHORIZED: authentication required")
 		})
 	},
 }
diff --git a/test/helm/gateway-helm/certjen-custom-scheduling.out.yaml b/test/helm/gateway-helm/certjen-custom-scheduling.out.yaml
index e4541b8cd..4bba4e1f5 100644
--- a/test/helm/gateway-helm/certjen-custom-scheduling.out.yaml
+++ b/test/helm/gateway-helm/certjen-custom-scheduling.out.yaml
@@ -37,7 +37,7 @@ data:
       kubernetes:
         rateLimitDeployment:
           container:
-            image: docker.io/envoyproxy/ratelimit:ae4cee11
+            image: docker.io/envoyproxy/ratelimit:0141a24f
           patch:
             type: StrategicMerge
             value:
diff --git a/test/helm/gateway-helm/control-plane-with-pdb.out.yaml b/test/helm/gateway-helm/control-plane-with-pdb.out.yaml
index 210af77b5..243833482 100644
--- a/test/helm/gateway-helm/control-plane-with-pdb.out.yaml
+++ b/test/helm/gateway-helm/control-plane-with-pdb.out.yaml
@@ -52,7 +52,7 @@ data:
       kubernetes:
         rateLimitDeployment:
           container:
-            image: docker.io/envoyproxy/ratelimit:ae4cee11
+            image: docker.io/envoyproxy/ratelimit:0141a24f
           patch:
             type: StrategicMerge
             value:
diff --git a/test/helm/gateway-helm/default-config.out.yaml b/test/helm/gateway-helm/default-config.out.yaml
index ebd8d9db5..dffdc90ac 100644
--- a/test/helm/gateway-helm/default-config.out.yaml
+++ b/test/helm/gateway-helm/default-config.out.yaml
@@ -37,7 +37,7 @@ data:
       kubernetes:
         rateLimitDeployment:
           container:
-            image: docker.io/envoyproxy/ratelimit:ae4cee11
+            image: docker.io/envoyproxy/ratelimit:0141a24f
           patch:
             type: StrategicMerge
             value:
diff --git a/test/helm/gateway-helm/deployment-custom-topology.out.yaml b/test/helm/gateway-helm/deployment-custom-topology.out.yaml
index c78eb3d24..7d580d91f 100644
--- a/test/helm/gateway-helm/deployment-custom-topology.out.yaml
+++ b/test/helm/gateway-helm/deployment-custom-topology.out.yaml
@@ -37,7 +37,7 @@ data:
       kubernetes:
         rateLimitDeployment:
           container:
-            image: docker.io/envoyproxy/ratelimit:ae4cee11
+            image: docker.io/envoyproxy/ratelimit:0141a24f
           patch:
             type: StrategicMerge
             value:
diff --git a/test/helm/gateway-helm/deployment-images-config.out.yaml b/test/helm/gateway-helm/deployment-images-config.out.yaml
index f09e89d19..c7d4d18e2 100644
--- a/test/helm/gateway-helm/deployment-images-config.out.yaml
+++ b/test/helm/gateway-helm/deployment-images-config.out.yaml
@@ -37,7 +37,7 @@ data:
       kubernetes:
         rateLimitDeployment:
           container:
-            image: docker.io/envoyproxy/ratelimit:ae4cee11
+            image: docker.io/envoyproxy/ratelimit:0141a24f
           patch:
             type: StrategicMerge
             value:
diff --git a/test/helm/gateway-helm/deployment-priorityclass.out.yaml b/test/helm/gateway-helm/deployment-priorityclass.out.yaml
index 9063afac9..7d3ed99c6 100644
--- a/test/helm/gateway-helm/deployment-priorityclass.out.yaml
+++ b/test/helm/gateway-helm/deployment-priorityclass.out.yaml
@@ -37,7 +37,7 @@ data:
       kubernetes:
         rateLimitDeployment:
           container:
-            image: docker.io/envoyproxy/ratelimit:ae4cee11
+            image: docker.io/envoyproxy/ratelimit:0141a24f
           patch:
             type: StrategicMerge
             value:
diff --git a/test/helm/gateway-helm/deployment-securitycontext.out.yaml b/test/helm/gateway-helm/deployment-securitycontext.out.yaml
index cf23490af..f0a5dff52 100644
--- a/test/helm/gateway-helm/deployment-securitycontext.out.yaml
+++ b/test/helm/gateway-helm/deployment-securitycontext.out.yaml
@@ -37,7 +37,7 @@ data:
       kubernetes:
         rateLimitDeployment:
           container:
-            image: docker.io/envoyproxy/ratelimit:ae4cee11
+            image: docker.io/envoyproxy/ratelimit:0141a24f
           patch:
             type: StrategicMerge
             value:
diff --git a/test/helm/gateway-helm/service-annotations.out.yaml b/test/helm/gateway-helm/service-annotations.out.yaml
index 83ea20ab6..e15f84270 100644
--- a/test/helm/gateway-helm/service-annotations.out.yaml
+++ b/test/helm/gateway-helm/service-annotations.out.yaml
@@ -37,7 +37,7 @@ data:
       kubernetes:
         rateLimitDeployment:
           container:
-            image: docker.io/envoyproxy/ratelimit:ae4cee11
+            image: docker.io/envoyproxy/ratelimit:0141a24f
           patch:
             type: StrategicMerge
             value: