Repository: envoyproxy/gateway
Pull request #2657 (2024-02-20 01:25:06)

api: support retry on in BackendTrafficPolicy
**What this PR does / why we need it**:
api: support retry on in BackendTrafficPolicy

Relates to #1821,  #1907

Continue work by @tmsnan in #2168

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/envoyproxy/gateway/pull/2657?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=envoyproxy) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Comparison is base [(`cf46fbe`)](https://app.codecov.io/gh/envoyproxy/gateway/commit/cf46fbe776918ad19444e26d637ffcc79676ca23?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=envoyproxy) 63.41% compared to head [(`be9470b`)](https://app.codecov.io/gh/envoyproxy/gateway/pull/2657?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=envoyproxy) 63.43%.


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


```diff
@@            Coverage Diff             @@
##             main    #2657      +/-   ##
==========================================
+ Coverage   63.41%   63.43%   +0.02%     
==========================================
  Files         119      119              
  Lines       19227    19227              
==========================================
+ Hits        12193    12197       +4     
+ Misses       6222     6219       -3     
+ Partials      812      811       -1     
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/envoyproxy/gateway/pull/2657?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).

guydc: /retest
guydc: /retest

Patch:
diff --git a/api/v1alpha1/backendtrafficpolicy_types.go b/api/v1alpha1/backendtrafficpolicy_types.go
index 163563acb..41325dc09 100644
--- a/api/v1alpha1/backendtrafficpolicy_types.go
+++ b/api/v1alpha1/backendtrafficpolicy_types.go
@@ -82,6 +82,11 @@ type BackendTrafficPolicySpec struct {
 	// +optional
 	CircuitBreaker *CircuitBreaker `json:"circuitBreaker,omitempty"`
 
+	// Retry provides more advanced usage, allowing users to customize the number of retries, retry fallback strategy, and retry triggering conditions.
+	// If not set, retry will be disabled.
+	// +optional
+	Retry *Retry `json:"retry,omitempty"`
+
 	// Timeout settings for the backend connections.
 	//
 	// +optional
diff --git a/api/v1alpha1/healthcheck_types.go b/api/v1alpha1/healthcheck_types.go
index c2dd6a4b0..61172273e 100644
--- a/api/v1alpha1/healthcheck_types.go
+++ b/api/v1alpha1/healthcheck_types.go
@@ -160,12 +160,6 @@ type TCPActiveHealthChecker struct {
 	Receive *ActiveHealthCheckPayload `json:"receive,omitempty" yaml:"receive,omitempty"`
 }
 
-// HTTPStatus defines the http status code.
-// +kubebuilder:validation:Minimum=100
-// +kubebuilder:validation:Maximum=600
-// +kubebuilder:validation:ExclusiveMaximum=true
-type HTTPStatus int
-
 // ActiveHealthCheckPayloadType is the type of the payload.
 // +kubebuilder:validation:Enum=Text;Binary
 type ActiveHealthCheckPayloadType string
diff --git a/api/v1alpha1/retry_types.go b/api/v1alpha1/retry_types.go
new file mode 100644
index 000000000..1752806e8
--- /dev/null
+++ b/api/v1alpha1/retry_types.go
@@ -0,0 +1,111 @@
+// 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.
+
+package v1alpha1
+
+import (
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+// Retry defines the retry strategy to be applied.
+type Retry struct {
+	// NumRetries is the number of retries to be attempted. Defaults to 2.
+	//
+	// +optional
+	// +kubebuilder:validation:Minimum=0
+	// +kubebuilder:default=2
+	NumRetries *int32 `json:"numRetries,omitempty"`
+
+	// RetryOn specifies the retry trigger condition.
+	//
+	// If not specified, the default is to retry on connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes(503).
+	// +optional
+	RetryOn *RetryOn `json:"retryOn,omitempty"`
+
+	// PerRetry is the retry policy to be applied per retry attempt.
+	//
+	// +optional
+	PerRetry *PerRetryPolicy `json:"perRetry,omitempty"`
+}
+
+type RetryOn struct {
+	// Triggers specifies the retry trigger condition(Http/Grpc).
+	//
+	// +optional
+	Triggers []TriggerEnum `json:"triggers,omitempty"`
+
+	// HttpStatusCodes specifies the http status codes to be retried.
+	//
+	// +optional
+	HTTPStatusCodes []HTTPStatus `json:"httpStatusCodes,omitempty"`
+}
+
+// TriggerEnum specifies the conditions that trigger retries.
+// +kubebuilder:validation:Enum={"5xx","gateway-error","disconnect-reset","connect-failure","retriable-4xx","refused-stream","retriable-status-codes","cancelled","deadline-exceeded","internal","resource-exhausted","unavailable"}
+type TriggerEnum string
+
+const (
+	// HTTP events.
+	// For additional details, see https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/router_filter#x-envoy-retry-on
+
+	// The upstream server responds with any 5xx response code, or does not respond at all (disconnect/reset/read timeout).
+	// Includes connect-failure and refused-stream.
+	Error5XX TriggerEnum = "5xx"
+	// The response is a gateway error (502,503 or 504).
+	GatewayError TriggerEnum = "gateway-error"
+	// The upstream server does not respond at all (disconnect/reset/read timeout.)
+	DisconnectRest TriggerEnum = "disconnect-reset"
+	// Connection failure to the upstream server (connect timeout, etc.). (Included in *5xx*)
+	ConnectFailure TriggerEnum = "connect-failure"
+	// The upstream server responds with a retriable 4xx response code.
+	// Currently, the only response code in this category is 409.
+	Retriable4XX TriggerEnum = "retriable-4xx"
+	// The upstream server resets the stream with a REFUSED_STREAM error code.
+	RefusedStream TriggerEnum = "refused-stream"
+	// The upstream server responds with any response code matching one defined in the RetriableStatusCodes.
+	RetriableStatusCodes TriggerEnum = "retriable-status-codes"
+
+	// GRPC events, currently only supported for gRPC status codes in response headers.
+	// For additional details, see https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/router_filter#x-envoy-retry-grpc-on
+
+	// The gRPC status code in the response headers is “cancelled”.
+	Cancelled TriggerEnum = "cancelled"
+	// The gRPC status code in the response headers is “deadline-exceeded”.
+	DeadlineExceeded TriggerEnum = "deadline-exceeded"
+	// The gRPC status code in the response headers is “internal”.
+	Internal TriggerEnum = "internal"
+	// The gRPC status code in the response headers is “resource-exhausted”.
+	ResourceExhausted TriggerEnum = "resource-exhausted"
+	// The gRPC status code in the response headers is “unavailable”.
+	Unavailable TriggerEnum = "unavailable"
+)
+
+type PerRetryPolicy struct {
+	// Timeout is the timeout per retry attempt.
+	//
+	// +optional
+	// +kubebuilder:validation:Format=duration
+	Timeout *metav1.Duration `json:"timeout,omitempty"`
+	// Backoff is the backoff policy to be applied per retry attempt. gateway uses a fully jittered exponential
+	// back-off algorithm for retries. For additional details,
+	// see https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/router_filter#config-http-filters-router-x-envoy-max-retries
+	//
+	// +optional
+	BackOff *BackOffPolicy `json:"backOff,omitempty"`
+}
+
+type BackOffPolicy struct {
+	// BaseInterval is the base interval between retries.
+	//
+	// +kubebuilder:validation:Format=duration
+	BaseInterval *metav1.Duration `json:"baseInterval,omitempty"`
+	// MaxInterval is the maximum interval between retries. This parameter is optional, but must be greater than or equal to the base_interval if set.
+	// The default is 10 times the base_interval
+	//
+	// +optional
+	// +kubebuilder:validation:Format=duration
+	MaxInterval *metav1.Duration `json:"maxInterval,omitempty"`
+	// we can add rate limited based backoff config here if we want to.
+}
diff --git a/api/v1alpha1/shared_types.go b/api/v1alpha1/shared_types.go
index e0b245591..970c62ba6 100644
--- a/api/v1alpha1/shared_types.go
+++ b/api/v1alpha1/shared_types.go
@@ -364,3 +364,9 @@ type KubernetesHorizontalPodAutoscalerSpec struct {
 	// +optional
 	Behavior *autoscalingv2.HorizontalPodAutoscalerBehavior `json:"behavior,omitempty"`
 }
+
+// HTTPStatus defines the http status code.
+// +kubebuilder:validation:Minimum=100
+// +kubebuilder:validation:Maximum=600
+// +kubebuilder:validation:ExclusiveMaximum=true
+type HTTPStatus int
diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go
index 738dbc00a..d38d71088 100644
--- a/api/v1alpha1/zz_generated.deepcopy.go
+++ b/api/v1alpha1/zz_generated.deepcopy.go
@@ -89,6 +89,31 @@ func (in *ActiveHealthCheckPayload) DeepCopy() *ActiveHealthCheckPayload {
 	return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *BackOffPolicy) DeepCopyInto(out *BackOffPolicy) {
+	*out = *in
+	if in.BaseInterval != nil {
+		in, out := &in.BaseInterval, &out.BaseInterval
+		*out = new(v1.Duration)
+		**out = **in
+	}
+	if in.MaxInterval != nil {
+		in, out := &in.MaxInterval, &out.MaxInterval
+		*out = new(v1.Duration)
+		**out = **in
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackOffPolicy.
+func (in *BackOffPolicy) DeepCopy() *BackOffPolicy {
+	if in == nil {
+		return nil
+	}
+	out := new(BackOffPolicy)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *BackendTrafficPolicy) DeepCopyInto(out *BackendTrafficPolicy) {
 	*out = *in
@@ -187,6 +212,11 @@ func (in *BackendTrafficPolicySpec) DeepCopyInto(out *BackendTrafficPolicySpec)
 		*out = new(CircuitBreaker)
 		(*in).DeepCopyInto(*out)
 	}
+	if in.Retry != nil {
+		in, out := &in.Retry, &out.Retry
+		*out = new(Retry)
+		(*in).DeepCopyInto(*out)
+	}
 	if in.Timeout != nil {
 		in, out := &in.Timeout, &out.Timeout
 		*out = new(Timeout)
@@ -2511,6 +2541,31 @@ func (in *PathSettings) DeepCopy() *PathSettings {
 	return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *PerRetryPolicy) DeepCopyInto(out *PerRetryPolicy) {
+	*out = *in
+	if in.Timeout != nil {
+		in, out := &in.Timeout, &out.Timeout
+		*out = new(v1.Duration)
+		**out = **in
+	}
+	if in.BackOff != nil {
+		in, out := &in.BackOff, &out.BackOff
+		*out = new(BackOffPolicy)
+		(*in).DeepCopyInto(*out)
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PerRetryPolicy.
+func (in *PerRetryPolicy) DeepCopy() *PerRetryPolicy {
+	if in == nil {
+		return nil
+	}
+	out := new(PerRetryPolicy)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *ProxyAccessLog) DeepCopyInto(out *ProxyAccessLog) {
 	*out = *in
@@ -3013,6 +3068,61 @@ func (in *RequestHeaderCustomTag) DeepCopy() *RequestHeaderCustomTag {
 	return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *Retry) DeepCopyInto(out *Retry) {
+	*out = *in
+	if in.NumRetries != nil {
+		in, out := &in.NumRetries, &out.NumRetries
+		*out = new(int32)
+		**out = **in
+	}
+	if in.RetryOn != nil {
+		in, out := &in.RetryOn, &out.RetryOn
+		*out = new(RetryOn)
+		(*in).DeepCopyInto(*out)
+	}
+	if in.PerRetry != nil {
+		in, out := &in.PerRetry, &out.PerRetry
+		*out = new(PerRetryPolicy)
+		(*in).DeepCopyInto(*out)
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Retry.
+func (in *Retry) DeepCopy() *Retry {
+	if in == nil {
+		return nil
+	}
+	out := new(Retry)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *RetryOn) DeepCopyInto(out *RetryOn) {
+	*out = *in
+	if in.Triggers != nil {
+		in, out := &in.Triggers, &out.Triggers
+		*out = make([]TriggerEnum, len(*in))
+		copy(*out, *in)
+	}
+	if in.HTTPStatusCodes != nil {
+		in, out := &in.HTTPStatusCodes, &out.HTTPStatusCodes
+		*out = make([]HTTPStatus, len(*in))
+		copy(*out, *in)
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RetryOn.
+func (in *RetryOn) DeepCopy() *RetryOn {
+	if in == nil {
+		return nil
+	}
+	out := new(RetryOn)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *SecurityPolicy) DeepCopyInto(out *SecurityPolicy) {
 	*out = *in
diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml
index 84a144de1..2ba4265ae 100644
--- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml
+++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml
@@ -740,6 +740,82 @@ spec:
                 required:
                 - type
                 type: object
+              retry:
+                description: Retry provides more advanced usage, allowing users to
+                  customize the number of retries, retry fallback strategy, and retry
+                  triggering conditions. If not set, retry will be disabled.
+                properties:
+                  numRetries:
+                    default: 2
+                    description: NumRetries is the number of retries to be attempted.
+                      Defaults to 2.
+                    format: int32
+                    minimum: 0
+                    type: integer
+                  perRetry:
+                    description: PerRetry is the retry policy to be applied per retry
+                      attempt.
+                    properties:
+                      backOff:
+                        description: Backoff is the backoff policy to be applied per
+                          retry attempt. gateway uses a fully jittered exponential
+                          back-off algorithm for retries. For additional details,
+                          see https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/router_filter#config-http-filters-router-x-envoy-max-retries
+                        properties:
+                          baseInterval:
+                            description: BaseInterval is the base interval between
+                              retries.
+                            format: duration
+                            type: string
+                          maxInterval:
+                            description: MaxInterval is the maximum interval between
+                              retries. This parameter is optional, but must be greater
+                              than or equal to the base_interval if set. The default
+                              is 10 times the base_interval
+                            format: duration
+                            type: string
+                        type: object
+                      timeout:
+                        description: Timeout is the timeout per retry attempt.
+                        format: duration
+                        type: string
+                    type: object
+                  retryOn:
+                    description: "RetryOn specifies the retry trigger condition. \n
+                      If not specified, the default is to retry on connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes(503)."
+                    properties:
+                      httpStatusCodes:
+                        description: HttpStatusCodes specifies the http status codes
+                          to be retried.
+                        items:
+                          description: HTTPStatus defines the http status code.
+                          exclusiveMaximum: true
+                          maximum: 600
+                          minimum: 100
+                          type: integer
+                        type: array
+                      triggers:
+                        description: Triggers specifies the retry trigger condition(Http/Grpc).
+                        items:
+                          description: TriggerEnum specifies the conditions that trigger
+                            retries.
+                          enum:
+                          - 5xx
+                          - gateway-error
+                          - disconnect-reset
+                          - connect-failure
+                          - retriable-4xx
+                          - refused-stream
+                          - retriable-status-codes
+                          - cancelled
+                          - deadline-exceeded
+                          - internal
+                          - resource-exhausted
+                          - unavailable
+                          type: string
+                        type: array
+                    type: object
+                type: object
               targetRef:
                 description: targetRef is the name of the resource this policy is
                   being attached to. This Policy and the TargetRef MUST be in the
diff --git a/internal/gatewayapi/clienttrafficpolicy.go b/internal/gatewayapi/clienttrafficpolicy.go
index b6ef8f345..97caa87f4 100644
--- a/internal/gatewayapi/clienttrafficpolicy.go
+++ b/internal/gatewayapi/clienttrafficpolicy.go
@@ -290,7 +290,7 @@ func resolveCTPolicyTargetRef(policy *egv1a1.ClientTrafficPolicy, gateways []*Ga
 func (t *Translator) translateClientTrafficPolicyForListener(policy *egv1a1.ClientTrafficPolicy, l *ListenerContext,
 	xdsIR XdsIRMap, infraIR InfraIRMap, resources *Resources) error {
 	// Find IR
-	irKey := irStringKey(l.gateway.Namespace, l.gateway.Name)
+	irKey := t.getIRKey(l.gateway)
 	// It must exist since we've already finished processing the gateways
 	gwXdsIR := xdsIR[irKey]
 
diff --git a/internal/gatewayapi/translator.go b/internal/gatewayapi/translator.go
index 87aa5746c..5163deca3 100644
--- a/internal/gatewayapi/translator.go
+++ b/internal/gatewayapi/translator.go
@@ -6,12 +6,8 @@
 package gatewayapi
 
 import (
-	"fmt"
-	"strings"
-
 	"golang.org/x/exp/maps"
 	"k8s.io/apimachinery/pkg/runtime/schema"
-	"k8s.io/utils/ptr"
 	gwapiv1 "sigs.k8s.io/gateway-api/apis/v1"
 
 	egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1"
@@ -207,61 +203,12 @@ func (t *Translator) Translate(resources *Resources) *TranslateResult {
 	// Sort xdsIR based on the Gateway API spec
 	sortXdsIRMap(xdsIR)
 
-	// Add a catch-all route for each HTTP listener if needed
-	addCatchAllRoute(xdsIR)
-
 	return newTranslateResult(gateways, httpRoutes, grpcRoutes, tlsRoutes,
 		tcpRoutes, udpRoutes, clientTrafficPolicies, backendTrafficPolicies,
 		securityPolicies, xdsIR, infraIR)
 
 }
 
-// For filters without native per-route support, we need to add a catch-all route
-// to ensure that these filters are disabled for non-matching requests.
-// https://github.com/envoyproxy/gateway/issues/2507
-func addCatchAllRoute(xdsIR map[string]*ir.Xds) {
-	for _, i := range xdsIR {
-		for _, http := range i.HTTP {
-			var needCatchAllRoutePerHost = make(map[string]bool)
-			for _, r := range http.Routes {
-				if r.ExtAuth != nil || r.BasicAuth != nil || r.OIDC != nil {
-					needCatchAllRoutePerHost[r.Hostname] = true
-				}
-			}
-
-			// skip if there is already a catch-all route
-			for host := range needCatchAllRoutePerHost {
-				for _, r := range http.Routes {
-					if (r.Hostname == host &&
-						r.PathMatch != nil &&
-						r.PathMatch.Prefix != nil &&
-						*r.PathMatch.Prefix == "/") &&
-						len(r.HeaderMatches) == 0 &&
-						len(r.QueryParamMatches) == 0 {
-						delete(needCatchAllRoutePerHost, host)
-					}
-				}
-			}
-
-			for host, needCatchAllRoute := range needCatchAllRoutePerHost {
-				if needCatchAllRoute {
-					underscoredHost := strings.ReplaceAll(host, ".", "_")
-					http.Routes = append(http.Routes, &ir.HTTPRoute{
-						Name: fmt.Sprintf("%s/catch-all-return-404", underscoredHost),
-						PathMatch: &ir.StringMatch{
-							Prefix: ptr.To("/"),
-						},
-						DirectResponse: &ir.DirectResponse{
-							StatusCode: 404,
-						},
-						Hostname: host,
-					})
-				}
-			}
-		}
-	}
-}
-
 // GetRelevantGateways returns GatewayContexts, containing a copy of the original
 // Gateway with the Listener statuses reset.
 func (t *Translator) GetRelevantGateways(gateways []*gwapiv1.Gateway) []*GatewayContext {
diff --git a/internal/xds/translator/basicauth.go b/internal/xds/translator/basicauth.go
index 7d15ab1b6..c9658cfe8 100644
--- a/internal/xds/translator/basicauth.go
+++ b/internal/xds/translator/basicauth.go
@@ -7,7 +7,6 @@ package translator
 
 import (
 	"errors"
-	"fmt"
 
 	corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
 	routev3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
@@ -35,6 +34,7 @@ var _ httpFilter = &basicAuth{}
 // patchHCM builds and appends the basic_auth Filters to the HTTP Connection Manager
 // if applicable, and it does not already exist.
 // Note: this method creates an basic_auth filter for each route that contains an BasicAuth config.
+// The filter is disabled by default. It is enabled on the route level.
 func (*basicAuth) patchHCM(mgr *hcmv3.HttpConnectionManager, irListener *ir.HTTPListener) error {
 	var errs error
 
@@ -77,7 +77,8 @@ func buildHCMBasicAuthFilter(route *ir.HTTPRoute) (*hcmv3.HttpFilter, error) {
 	}
 
 	return &hcmv3.HttpFilter{
-		Name: basicAuthFilterName(route),
+		Name:     basicAuthFilterName(route),
+		Disabled: true,
 		ConfigType: &hcmv3.HttpFilter_TypedConfig{
 			TypedConfig: basicAuthAny,
 		},
@@ -116,52 +117,6 @@ func (*basicAuth) patchResources(*types.ResourceVersionTable, []*ir.HTTPRoute) e
 	return nil
 }
 
-// patchRouteCfg patches the provided route configuration with the basicAuth filter
-// if applicable.
-// Note: this method disables all the basicAuth filters by default. The filter will
-// be enabled per-route in the typePerFilterConfig of the route.
-func (*basicAuth) patchRouteConfig(routeCfg *routev3.RouteConfiguration, irListener *ir.HTTPListener) error {
-	if routeCfg == nil {
-		return errors.New("route configuration is nil")
-	}
-	if irListener == nil {
-		return errors.New("ir listener is nil")
-	}
-
-	var errs error
-	for _, route := range irListener.Routes {
-		if !routeContainsBasicAuth(route) {
-			continue
-		}
-
-		filterName := basicAuthFilterName(route)
-		filterCfg := routeCfg.TypedPerFilterConfig
-
-		if _, ok := filterCfg[filterName]; ok {
-			// This should not happen since this is the only place where the basicAuth
-			// filter is added in a route.
-			errs = errors.Join(errs, fmt.Errorf(
-				"route config already contains basicAuth config: %+v", route))
-			continue
-		}
-
-		// Disable all the filters by default. The filter will be enabled
-		// per-route in the typePerFilterConfig of the route.
-		routeCfgAny, err := anypb.New(&routev3.FilterConfig{Disabled: true})
-		if err != nil {
-			errs = errors.Join(errs, err)
-			continue
-		}
-
-		if filterCfg == nil {
-			routeCfg.TypedPerFilterConfig = make(map[string]*anypb.Any)
-		}
-
-		routeCfg.TypedPerFilterConfig[filterName] = routeCfgAny
-	}
-	return errs
-}
-
 // patchRoute patches the provided route with the basicAuth config if applicable.
 // Note: this method enables the corresponding basicAuth filter for the provided route.
 func (*basicAuth) patchRoute(route *routev3.Route, irRoute *ir.HTTPRoute) error {
diff --git a/internal/xds/translator/cors.go b/internal/xds/translator/cors.go
index a7fe606d0..ef8c6d8dd 100644
--- a/internal/xds/translator/cors.go
+++ b/internal/xds/translator/cors.go
@@ -165,10 +165,6 @@ func (*cors) patchRoute(route *routev3.Route, irRoute *ir.HTTPRoute) error {
 	return nil
 }
 
-func (*cors) patchRouteConfig(*routev3.RouteConfiguration, *ir.HTTPListener) error {
-	return nil
-}
-
 func (c *cors) patchResources(*types.ResourceVersionTable, []*ir.HTTPRoute) error {
 	return nil
 }
diff --git a/internal/xds/translator/extauth.go b/internal/xds/translator/extauth.go
index 2ae9df611..57536a3c2 100644
--- a/internal/xds/translator/extauth.go
+++ b/internal/xds/translator/extauth.go
@@ -7,7 +7,6 @@ package translator
 
 import (
 	"errors"
-	"fmt"
 	"net/url"
 
 	corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
@@ -38,6 +37,7 @@ var _ httpFilter = &extAuth{}
 // patchHCM builds and appends the ext_authz Filters to the HTTP Connection Manager
 // if applicable, and it does not already exist.
 // Note: this method creates an ext_authz filter for each route that contains an ExtAuthz config.
+// The filter is disabled by default. It is enabled on the route level.
 // TODO: zhaohuabing avoid duplicated HTTP filters
 func (*extAuth) patchHCM(mgr *hcmv3.HttpConnectionManager, irListener *ir.HTTPListener) error {
 	var errs error
@@ -80,7 +80,8 @@ func buildHCMExtAuthFilter(route *ir.HTTPRoute) (*hcmv3.HttpFilter, error) {
 	}
 
 	return &hcmv3.HttpFilter{
-		Name: extAuthFilterName(route),
+		Name:     extAuthFilterName(route),
+		Disabled: true,
 		ConfigType: &hcmv3.HttpFilter_TypedConfig{
 			TypedConfig: extAuthAny,
 		},
@@ -244,52 +245,6 @@ func createExtServiceXDSCluster(rd *ir.RouteDestination, tCtx *types.ResourceVer
 	return nil
 }
 
-// patchRouteCfg patches the provided route configuration with the extAuth filter
-// if applicable.
-// Note: this method disables all the extAuth filters by default. The filter will
-// be enabled per-route in the typePerFilterConfig of the route.
-func (*extAuth) patchRouteConfig(routeCfg *routev3.RouteConfiguration, irListener *ir.HTTPListener) error {
-	if routeCfg == nil {
-		return errors.New("route configuration is nil")
-	}
-	if irListener == nil {
-		return errors.New("ir listener is nil")
-	}
-
-	var errs error
-	for _, route := range irListener.Routes {
-		if !routeContainsExtAuth(route) {
-			continue
-		}
-
-		filterName := extAuthFilterName(route)
-		filterCfg := routeCfg.TypedPerFilterConfig
-
-		if _, ok := filterCfg[filterName]; ok {
-			// This should not happen since this is the only place where the extAuth
-			// filter is added in a route.
-			errs = errors.Join(errs, fmt.Errorf(
-				"route config already contains extAuth config: %+v", route))
-			continue
-		}
-
-		// Disable all the filters by default. The filter will be enabled
-		// per-route in the typePerFilterConfig of the route.
-		routeCfgAny, err := anypb.New(&routev3.FilterConfig{Disabled: true})
-		if err != nil {
-			errs = errors.Join(errs, err)
-			continue
-		}
-
-		if filterCfg == nil {
-			routeCfg.TypedPerFilterConfig = make(map[string]*anypb.Any)
-		}
-
-		routeCfg.TypedPerFilterConfig[filterName] = routeCfgAny
-	}
-	return errs
-}
-
 // patchRoute patches the provided route with the extAuth config if applicable.
 // Note: this method enables the corresponding extAuth filter for the provided route.
 func (*extAuth) patchRoute(route *routev3.Route, irRoute *ir.HTTPRoute) error {
diff --git a/internal/xds/translator/fault.go b/internal/xds/translator/fault.go
index 6fdf50080..3b622097a 100644
--- a/internal/xds/translator/fault.go
+++ b/internal/xds/translator/fault.go
@@ -121,10 +121,6 @@ func (*fault) patchResources(*types.ResourceVersionTable, []*ir.HTTPRoute) error
 	return nil
 }
 
-func (*fault) patchRouteConfig(routeCfg *routev3.RouteConfiguration, irListener *ir.HTTPListener) error {
-	return nil
-}
-
 // patchRoute patches the provided route with the fault config if applicable.
 // Note: this method enables the corresponding fault filter for the provided route.
 func (*fault) patchRoute(route *routev3.Route, irRoute *ir.HTTPRoute) error {
diff --git a/internal/xds/translator/httpfilters.go b/internal/xds/translator/httpfilters.go
index ac9df2dae..738e5ee94 100644
--- a/internal/xds/translator/httpfilters.go
+++ b/internal/xds/translator/httpfilters.go
@@ -39,9 +39,8 @@ func registerHTTPFilter(filter httpFilter) {
 //   - patchHCM: EG adds a filter for each route in the HCM filter chain, the
 //     filter name is prefixed with the filter's type name, for example,
 //     "envoy.filters.http.oauth2", and suffixed with the route name. Each filter
-//     is configured with the route's per-route configuration.
-//   - patchRouteConfig: EG disables all the filters of this type in the
-//     typedFilterConfig of the route config.
+//     is configured with the route's per-route configuration. The filter is
+//     disabled by default and is enabled on the route level.
 //   - PatchRouteWithPerRouteConfig: EG enables the corresponding filter for each
 //     route in the typedFilterConfig of that route.
 //
@@ -52,10 +51,6 @@ type httpFilter interface {
 	// patchHCM patches the HttpConnectionManager with the filter.
 	patchHCM(mgr *hcmv3.HttpConnectionManager, irListener *ir.HTTPListener) error
 
-	// patchRouteConfig patches the provided RouteConfiguration with a filter's
-	// RouteConfiguration level configuration.
-	patchRouteConfig(rc *routev3.RouteConfiguration, irListener *ir.HTTPListener) error
-
 	// patchRoute patches the provide Route with a filter's Route level configuration.
 	patchRoute(route *routev3.Route, irRoute *ir.HTTPRoute) error
 
@@ -179,23 +174,6 @@ func (t *Translator) patchHCMWithFilters(
 	return nil
 }
 
-// patchRouteCfgWithPerRouteConfig appends per-route filter configurations to the
-// provided listener's RouteConfiguration.
-// This method is used to disable the filters without native per-route support.
-// The disabled filters will be enabled by route in the patchRouteWithPerRouteConfig
-// method.
-func patchRouteCfgWithPerRouteConfig(
-	routeCfg *routev3.RouteConfiguration,
-	irListener *ir.HTTPListener) error {
-	// Only supports the oauth2 filter for now, other filters will be added later.
-	for _, filter := range httpFilters {
-		if err := filter.patchRouteConfig(routeCfg, irListener); err != nil {
-			return err
-		}
-	}
-	return nil
-}
-
 // patchRouteWithPerRouteConfig appends per-route filter configuration to the
 // provided route.
 func patchRouteWithPerRouteConfig(
diff --git a/internal/xds/translator/jwt.go b/internal/xds/translator/jwt.go
index 543da09bd..e1bc76fa1 100644
--- a/internal/xds/translator/jwt.go
+++ b/internal/xds/translator/jwt.go
@@ -333,10 +333,6 @@ func routeContainsJWTAuthn(irRoute *ir.HTTPRoute) bool {
 	return false
 }
 
-func (*jwt) patchRouteConfig(*routev3.RouteConfiguration, *ir.HTTPListener) error {
-	return nil
-}
-
 // buildJwtFromHeaders returns a list of JwtHeader transformed from JWTFromHeader struct
 func buildJwtFromHeaders(headers []v1alpha1.JWTHeaderExtractor) []*jwtauthnv3.JwtHeader {
 	jwtHeaders := make([]*jwtauthnv3.JwtHeader, 0, len(headers))
diff --git a/internal/xds/translator/local_ratelimit.go b/internal/xds/translator/local_ratelimit.go
index f54071d35..a3110cbf9 100644
--- a/internal/xds/translator/local_ratelimit.go
+++ b/internal/xds/translator/local_ratelimit.go
@@ -110,10 +110,6 @@ func (*localRateLimit) patchResources(*types.ResourceVersionTable,
 	return nil
 }
 
-func (*localRateLimit) patchRouteConfig(*routev3.RouteConfiguration, *ir.HTTPListener) error {
-	return nil
-}
-
 func (*localRateLimit) patchRoute(route *routev3.Route, irRoute *ir.HTTPRoute) error {
 	routeAction := route.GetRoute()
 
diff --git a/internal/xds/translator/oidc.go b/internal/xds/translator/oidc.go
index 2749598f2..3b12dd94b 100644
--- a/internal/xds/translator/oidc.go
+++ b/internal/xds/translator/oidc.go
@@ -40,6 +40,7 @@ var _ httpFilter = &oidc{}
 // patchHCM builds and appends the oauth2 Filters to the HTTP Connection Manager
 // if applicable, and it does not already exist.
 // Note: this method creates an oauth2 filter for each route that contains an OIDC config.
+// the filter is disabled by default. It is enabled on the route level.
 func (*oidc) patchHCM(mgr *hcmv3.HttpConnectionManager, irListener *ir.HTTPListener) error {
 	var errs error
 
@@ -85,7 +86,8 @@ func buildHCMOAuth2Filter(route *ir.HTTPRoute) (*hcmv3.HttpFilter, error) {
 	}
 
 	return &hcmv3.HttpFilter{
-		Name: oauth2FilterName(route),
+		Name:     oauth2FilterName(route),
+		Disabled: true,
 		ConfigType: &hcmv3.HttpFilter_TypedConfig{
 			TypedConfig: OAuth2Any,
 		},
@@ -341,52 +343,6 @@ func generateHMACSecretKey() ([]byte, error) {
 	return key, nil
 }
 
-// patchRouteCfg patches the provided route configuration with the oauth2 filter
-// if applicable.
-// Note: this method disables all the oauth2 filters by default. The filter will
-// be enabled per-route in the typePerFilterConfig of the route.
-func (*oidc) patchRouteConfig(routeCfg *routev3.RouteConfiguration, irListener *ir.HTTPListener) error {
-	if routeCfg == nil {
-		return errors.New("route configuration is nil")
-	}
-	if irListener == nil {
-		return errors.New("ir listener is nil")
-	}
-
-	var errs error
-	for _, route := range irListener.Routes {
-		if !routeContainsOIDC(route) {
-			continue
-		}
-
-		filterName := oauth2FilterName(route)
-		filterCfg := routeCfg.TypedPerFilterConfig
-
-		if _, ok := filterCfg[filterName]; ok {
-			// This should not happen since this is the only place where the oauth2
-			// filter is added in a route.
-			errs = errors.Join(errs, fmt.Errorf(
-				"route config already contains oauth2 config: %+v", route))
-			continue
-		}
-
-		// Disable all the filters by default. The filter will be enabled
-		// per-route in the typePerFilterConfig of the route.
-		routeCfgAny, err := anypb.New(&routev3.FilterConfig{Disabled: true})
-		if err != nil {
-			errs = errors.Join(errs, err)
-			continue
-		}
-
-		if filterCfg == nil {
-			routeCfg.TypedPerFilterConfig = make(map[string]*anypb.Any)
-		}
-
-		routeCfg.TypedPerFilterConfig[filterName] = routeCfgAny
-	}
-	return errs
-}
-
 // patchRoute patches the provided route with the oauth2 config if applicable.
 // Note: this method enables the corresponding oauth2 filter for the provided route.
 func (*oidc) patchRoute(route *routev3.Route, irRoute *ir.HTTPRoute) error {
diff --git a/internal/xds/translator/translator.go b/internal/xds/translator/translator.go
index 3e8de622b..4ce82ca54 100644
--- a/internal/xds/translator/translator.go
+++ b/internal/xds/translator/translator.go
@@ -294,11 +294,6 @@ func (t *Translator) processHTTPListenerXdsTranslation(
 		}
 		xdsRouteCfg.VirtualHosts = append(xdsRouteCfg.VirtualHosts, vHostsList...)
 
-		// Add per-route filter configs to the route config.
-		if err := patchRouteCfgWithPerRouteConfig(xdsRouteCfg, httpListener); err != nil {
-			errs = errors.Join(errs, err)
-		}
-
 		// Add all the other needed resources referenced by this filter to the
 		// resource version table.
 		if err := patchResources(tCtx, httpListener.Routes); err != nil {


Test patch:
diff --git a/internal/gatewayapi/testdata/securitypolicy-with-basic-auth.out.yaml b/internal/gatewayapi/testdata/securitypolicy-with-basic-auth.out.yaml
index 2356d6a4d..96c93cd3e 100644
--- a/internal/gatewayapi/testdata/securitypolicy-with-basic-auth.out.yaml
+++ b/internal/gatewayapi/testdata/securitypolicy-with-basic-auth.out.yaml
@@ -155,14 +155,3 @@ xdsIR:
           distinct: false
           name: ""
           prefix: /foo
-      - backendWeights:
-          invalid: 0
-          valid: 0
-        directResponse:
-          statusCode: 404
-        hostname: gateway.envoyproxy.io
-        name: gateway_envoyproxy_io/catch-all-return-404
-        pathMatch:
-          distinct: false
-          name: ""
-          prefix: /
diff --git a/internal/gatewayapi/testdata/securitypolicy-with-extauth.out.yaml b/internal/gatewayapi/testdata/securitypolicy-with-extauth.out.yaml
index 9076ad5e1..412c56438 100755
--- a/internal/gatewayapi/testdata/securitypolicy-with-extauth.out.yaml
+++ b/internal/gatewayapi/testdata/securitypolicy-with-extauth.out.yaml
@@ -272,25 +272,3 @@ xdsIR:
           distinct: false
           name: ""
           prefix: /bar
-      - backendWeights:
-          invalid: 0
-          valid: 0
-        directResponse:
-          statusCode: 404
-        hostname: www.foo.com
-        name: www_foo_com/catch-all-return-404
-        pathMatch:
-          distinct: false
-          name: ""
-          prefix: /
-      - backendWeights:
-          invalid: 0
-          valid: 0
-        directResponse:
-          statusCode: 404
-        hostname: www.bar.com
-        name: www_bar_com/catch-all-return-404
-        pathMatch:
-          distinct: false
-          name: ""
-          prefix: /
diff --git a/internal/gatewayapi/testdata/securitypolicy-with-oidc.in.yaml b/internal/gatewayapi/testdata/securitypolicy-with-oidc.in.yaml
index f443b0903..91fae31ce 100644
--- a/internal/gatewayapi/testdata/securitypolicy-with-oidc.in.yaml
+++ b/internal/gatewayapi/testdata/securitypolicy-with-oidc.in.yaml
@@ -62,7 +62,7 @@ httpRoutes:
     name: httproute-2
   spec:
     hostnames:
-    - www.foo.com
+    - www.example.com
     parentRefs:
     - namespace: envoy-gateway
       name: gateway-1
@@ -70,7 +70,7 @@ httpRoutes:
     rules:
     - matches:
       - path:
-          value: "/"
+          value: "/bar"
       backendRefs:
       - name: service-1
         port: 8080
diff --git a/internal/gatewayapi/testdata/securitypolicy-with-oidc.out.yaml b/internal/gatewayapi/testdata/securitypolicy-with-oidc.out.yaml
index 034461fa4..bd2496ecb 100644
--- a/internal/gatewayapi/testdata/securitypolicy-with-oidc.out.yaml
+++ b/internal/gatewayapi/testdata/securitypolicy-with-oidc.out.yaml
@@ -86,7 +86,7 @@ httpRoutes:
     namespace: default
   spec:
     hostnames:
-    - www.foo.com
+    - www.example.com
     parentRefs:
     - name: gateway-1
       namespace: envoy-gateway
@@ -97,7 +97,7 @@ httpRoutes:
         port: 8080
       matches:
       - path:
-          value: /
+          value: /bar
   status:
     parents:
     - conditions:
@@ -256,8 +256,8 @@ xdsIR:
               port: 8080
             protocol: HTTP
             weight: 1
-        hostname: www.foo.com
-        name: httproute/default/httproute-2/rule/0/match/0/www_foo_com
+        hostname: www.example.com
+        name: httproute/default/httproute-2/rule/0/match/0/www_example_com
         oidc:
           clientID: client1.apps.googleusercontent.com
           clientSecret: Y2xpZW50MTpzZWNyZXQK
@@ -272,15 +272,4 @@ xdsIR:
         pathMatch:
           distinct: false
           name: ""
-          prefix: /
-      - backendWeights:
-          invalid: 0
-          valid: 0
-        directResponse:
-          statusCode: 404
-        hostname: www.example.com
-        name: www_example_com/catch-all-return-404
-        pathMatch:
-          distinct: false
-          name: ""
-          prefix: /
+          prefix: /bar
diff --git a/internal/xds/translator/testdata/out/xds-ir/basic-auth.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/basic-auth.listeners.yaml
index ca39c5cd5..11ecc856c 100644
--- a/internal/xds/translator/testdata/out/xds-ir/basic-auth.listeners.yaml
+++ b/internal/xds/translator/testdata/out/xds-ir/basic-auth.listeners.yaml
@@ -14,7 +14,8 @@
           initialStreamWindowSize: 65536
           maxConcurrentStreams: 100
         httpFilters:
-        - name: envoy.filters.http.basic_auth_first-route
+        - disabled: true
+          name: envoy.filters.http.basic_auth_first-route
           typedConfig:
             '@type': type.googleapis.com/envoy.extensions.filters.http.basic_auth.v3.BasicAuth
             users:
diff --git a/internal/xds/translator/testdata/out/xds-ir/basic-auth.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/basic-auth.routes.yaml
index dbaae69d0..6fdefbdf7 100644
--- a/internal/xds/translator/testdata/out/xds-ir/basic-auth.routes.yaml
+++ b/internal/xds/translator/testdata/out/xds-ir/basic-auth.routes.yaml
@@ -1,9 +1,5 @@
 - ignorePortInHostMatching: true
   name: first-listener
-  typedPerFilterConfig:
-    envoy.filters.http.basic_auth_first-route:
-      '@type': type.googleapis.com/envoy.config.route.v3.FilterConfig
-      disabled: true
   virtualHosts:
   - domains:
     - '*'
diff --git a/internal/xds/translator/testdata/out/xds-ir/ext-auth.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/ext-auth.listeners.yaml
index 0e516bddb..ec6f73b21 100755
--- a/internal/xds/translator/testdata/out/xds-ir/ext-auth.listeners.yaml
+++ b/internal/xds/translator/testdata/out/xds-ir/ext-auth.listeners.yaml
@@ -14,7 +14,8 @@
           initialStreamWindowSize: 65536
           maxConcurrentStreams: 100
         httpFilters:
-        - name: envoy.filters.http.ext_authz_httproute/default/httproute-1/rule/0/match/0/www_example_com
+        - disabled: true
+          name: envoy.filters.http.ext_authz_httproute/default/httproute-1/rule/0/match/0/www_example_com
           typedConfig:
             '@type': type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz
             httpService:
@@ -27,7 +28,8 @@
                 cluster: securitypolicy/default/policy-for-first-route/http-backend
                 timeout: 10s
                 uri: http://http-backend.envoy-gateway:80/auth
-        - name: envoy.filters.http.ext_authz_httproute/default/httproute-2/rule/0/match/0/www_example_com
+        - disabled: true
+          name: envoy.filters.http.ext_authz_httproute/default/httproute-2/rule/0/match/0/www_example_com
           typedConfig:
             '@type': type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz
             allowedHeaders:
diff --git a/internal/xds/translator/testdata/out/xds-ir/ext-auth.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/ext-auth.routes.yaml
index 816ce53ba..d8aae1430 100755
--- a/internal/xds/translator/testdata/out/xds-ir/ext-auth.routes.yaml
+++ b/internal/xds/translator/testdata/out/xds-ir/ext-auth.routes.yaml
@@ -1,12 +1,5 @@
 - ignorePortInHostMatching: true
   name: first-listener
-  typedPerFilterConfig:
-    envoy.filters.http.ext_authz_httproute/default/httproute-1/rule/0/match/0/www_example_com:
-      '@type': type.googleapis.com/envoy.config.route.v3.FilterConfig
-      disabled: true
-    envoy.filters.http.ext_authz_httproute/default/httproute-2/rule/0/match/0/www_example_com:
-      '@type': type.googleapis.com/envoy.config.route.v3.FilterConfig
-      disabled: true
   virtualHosts:
   - domains:
     - '*'
diff --git a/internal/xds/translator/testdata/out/xds-ir/oidc.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/oidc.listeners.yaml
index 1ad1450a4..055104d42 100644
--- a/internal/xds/translator/testdata/out/xds-ir/oidc.listeners.yaml
+++ b/internal/xds/translator/testdata/out/xds-ir/oidc.listeners.yaml
@@ -14,7 +14,8 @@
           initialStreamWindowSize: 65536
           maxConcurrentStreams: 100
         httpFilters:
-        - name: envoy.filters.http.oauth2_first-route
+        - disabled: true
+          name: envoy.filters.http.oauth2_first-route
           typedConfig:
             '@type': type.googleapis.com/envoy.extensions.filters.http.oauth2.v3.OAuth2
             config:
@@ -48,7 +49,8 @@
                 cluster: oauth_foo_com_443
                 timeout: 10s
                 uri: https://oauth.foo.com/token
-        - name: envoy.filters.http.oauth2_second-route
+        - disabled: true
+          name: envoy.filters.http.oauth2_second-route
           typedConfig:
             '@type': type.googleapis.com/envoy.extensions.filters.http.oauth2.v3.OAuth2
             config:
diff --git a/internal/xds/translator/testdata/out/xds-ir/oidc.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/oidc.routes.yaml
index cffe6c9eb..f4ba10448 100644
--- a/internal/xds/translator/testdata/out/xds-ir/oidc.routes.yaml
+++ b/internal/xds/translator/testdata/out/xds-ir/oidc.routes.yaml
@@ -1,12 +1,5 @@
 - ignorePortInHostMatching: true
   name: first-listener
-  typedPerFilterConfig:
-    envoy.filters.http.oauth2_first-route:
-      '@type': type.googleapis.com/envoy.config.route.v3.FilterConfig
-      disabled: true
-    envoy.filters.http.oauth2_second-route:
-      '@type': type.googleapis.com/envoy.config.route.v3.FilterConfig
-      disabled: true
   virtualHosts:
   - domains:
     - '*'
diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md
index 55a26a48d..07cfc0dc8 100644
--- a/site/content/en/latest/api/extension_types.md
+++ b/site/content/en/latest/api/extension_types.md
@@ -97,6 +97,21 @@ _Appears in:_
 
 
 
+#### BackOffPolicy
+
+
+
+
+
+_Appears in:_
+- [PerRetryPolicy](#perretrypolicy)
+
+| Field | Type | Required | Description |
+| ---   | ---  | ---      | ---         |
+| `baseInterval` | _[Duration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#duration-v1-meta)_ |  true  | BaseInterval is the base interval between retries. |
+| `maxInterval` | _[Duration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#duration-v1-meta)_ |  false  | MaxInterval is the maximum interval between retries. This parameter is optional, but must be greater than or equal to the base_interval if set. The default is 10 times the base_interval |
+
+
 #### BackendTrafficPolicy
 
 
@@ -149,6 +164,7 @@ _Appears in:_
 | `healthCheck` | _[HealthCheck](#healthcheck)_ |  false  | HealthCheck allows gateway to perform active health checking on backends. |
 | `faultInjection` | _[FaultInjection](#faultinjection)_ |  false  | FaultInjection defines the fault injection policy to be applied. This configuration can be used to inject delays and abort requests to mimic failure scenarios such as service failures and overloads |
 | `circuitBreaker` | _[CircuitBreaker](#circuitbreaker)_ |  false  | Circuit Breaker settings for the upstream connections and requests. If not set, circuit breakers will be enabled with the default thresholds |
+| `retry` | _[Retry](#retry)_ |  false  | Retry provides more advanced usage, allowing users to customize the number of retries, retry fallback strategy, and retry triggering conditions. If not set, retry will be disabled. |
 | `timeout` | _[Timeout](#timeout)_ |  false  | Timeout settings for the backend connections. |
 | `compression` | _[Compression](#compression) array_ |  false  | The compression config for the http streams. |
 
@@ -1198,6 +1214,7 @@ HTTPStatus defines the http status code.
 
 _Appears in:_
 - [HTTPActiveHealthChecker](#httpactivehealthchecker)
+- [RetryOn](#retryon)
 
 
 
@@ -1683,6 +1700,21 @@ _Appears in:_
 | `disableMergeSlashes` | _boolean_ |  false  | DisableMergeSlashes allows disabling the default configuration of merging adjacent slashes in the path. Note that slash merging is not part of the HTTP spec and is provided for convenience. |
 
 
+#### PerRetryPolicy
+
+
+
+
+
+_Appears in:_
+- [Retry](#retry)
+
+| Field | Type | Required | Description |
+| ---   | ---  | ---      | ---         |
+| `timeout` | _[Duration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#duration-v1-meta)_ |  false  | Timeout is the timeout per retry attempt. |
+| `backOff` | _[BackOffPolicy](#backoffpolicy)_ |  false  | Backoff is the backoff policy to be applied per retry attempt. gateway uses a fully jittered exponential back-off algorithm for retries. For additional details, see https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/router_filter#config-http-filters-router-x-envoy-max-retries |
+
+
 #### ProviderType
 
 _Underlying type:_ _string_
@@ -2133,6 +2165,37 @@ _Appears in:_
 
 
 
+#### Retry
+
+
+
+Retry defines the retry strategy to be applied.
+
+_Appears in:_
+- [BackendTrafficPolicySpec](#backendtrafficpolicyspec)
+
+| Field | Type | Required | Description |
+| ---   | ---  | ---      | ---         |
+| `numRetries` | _integer_ |  false  | NumRetries is the number of retries to be attempted. Defaults to 2. |
+| `retryOn` | _[RetryOn](#retryon)_ |  false  | RetryOn specifies the retry trigger condition. <br /><br /> If not specified, the default is to retry on connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes(503). |
+| `perRetry` | _[PerRetryPolicy](#perretrypolicy)_ |  false  | PerRetry is the retry policy to be applied per retry attempt. |
+
+
+#### RetryOn
+
+
+
+
+
+_Appears in:_
+- [Retry](#retry)
+
+| Field | Type | Required | Description |
+| ---   | ---  | ---      | ---         |
+| `triggers` | _[TriggerEnum](#triggerenum) array_ |  false  | Triggers specifies the retry trigger condition(Http/Grpc). |
+| `httpStatusCodes` | _[HTTPStatus](#httpstatus) array_ |  false  | HttpStatusCodes specifies the http status codes to be retried. |
+
+
 #### SecurityPolicy
 
 
@@ -2381,6 +2444,17 @@ _Appears in:_
 
 
 
+#### TriggerEnum
+
+_Underlying type:_ _string_
+
+TriggerEnum specifies the conditions that trigger retries.
+
+_Appears in:_
+- [RetryOn](#retryon)
+
+
+
 #### XDSTranslatorHook
 
 _Underlying type:_ _string_
diff --git a/site/content/en/latest/user/oidc.md b/site/content/en/latest/user/oidc.md
index 419266033..6d4deba8e 100644
--- a/site/content/en/latest/user/oidc.md
+++ b/site/content/en/latest/user/oidc.md
@@ -3,12 +3,12 @@ title: "OIDC Authentication"
 ---
 
 This guide provides instructions for configuring [OpenID Connect (OIDC)][oidc] authentication.
-OpenID Connect (OIDC) is an authentication standard built on top of OAuth 2.0. 
-It enables client applications to rely on authentication that is performed by an OpenID Connect Provider (OP) 
+OpenID Connect (OIDC) is an authentication standard built on top of OAuth 2.0.
+It enables client applications to rely on authentication that is performed by an OpenID Connect Provider (OP)
 to verify the identity of a user.
 
-Envoy Gateway introduces a new CRD called [SecurityPolicy][SecurityPolicy] that allows the user to configure OIDC 
-authentication. 
+Envoy Gateway introduces a new CRD called [SecurityPolicy][SecurityPolicy] that allows the user to configure OIDC
+authentication.
 This instantiated resource can be linked to a [Gateway][Gateway] and [HTTPRoute][HTTPRoute] resource.
 
 ## Prerequisites
@@ -17,7 +17,7 @@ Follow the steps from the [Quickstart](../quickstart) guide to install Envoy Gat
 Before proceeding, you should be able to query the example backend using HTTP.
 
 OIDC authentication requires the redirect URL to be HTTPS. Follow the [Secure Gateways](../secure-gateways) guide
- to generate the TLS certificates and update the Gateway configuration to add an HTTPS listener.
+to generate the TLS certificates and update the Gateway configuration to add an HTTPS listener.
 
 Verify the Gateway status:
 
@@ -35,7 +35,7 @@ providers, including Auth0, Azure AD, Keycloak, Okta, OneLogin, Salesforce, UAA,
 Follow the steps in the [Google OIDC documentation][google-oidc] to register an OIDC application. Please make sure the
 redirect URL is set to the one you configured in the SecurityPolicy that you will create in the step below. If you don't
 specify a redirect URL in the SecurityPolicy, the default redirect URL is `https://${GATEWAY_HOST}/oauth2/callback`.
-Please notice that the `redirectURL` and `logoutPath` must be caught by the target HTTPRoute. For example, if the 
+Please notice that the `redirectURL` and `logoutPath` must be caught by the target HTTPRoute. For example, if the
 HTTPRoute is configured to match the host `www.example.com` and the path `/foo`, the `redirectURL` must
 be prefixed with `https://www.example.com/foo`, and `logoutPath` must be prefixed with`/foo`, for example,
 `https://www.example.com/foo/oauth2/callback` and `/foo/logout`, otherwise the OIDC authentication will fail.