diff --git a/api/v1alpha1/clienttrafficpolicy_types.go b/api/v1alpha1/clienttrafficpolicy_types.go
index 202a6954a8..cae9efbbf0 100644
--- a/api/v1alpha1/clienttrafficpolicy_types.go
+++ b/api/v1alpha1/clienttrafficpolicy_types.go
@@ -6,6 +6,7 @@
package v1alpha1
import (
+ "k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
gwapiv1 "sigs.k8s.io/gateway-api/apis/v1"
)
@@ -188,6 +189,21 @@ type HeaderSettings struct {
//
// +optional
Host *HostSettings `json:"host,omitempty"`
+
+ // MaxRequestHeaderLimit provides configuration for the maximum size of the
+ // request headers allowed for incoming connections, mapping to the Envoy
+ // `max_request_headers_kb` HTTP connection manager setting. Requests whose
+ // headers exceed this limit receive a 431 (Request Header Fields Too Large)
+ // response. The value is rounded up to the nearest KiB, must be at least 1Ki,
+ // and cannot exceed 8192Ki (the maximum Envoy supports).
+ // For example, 60Ki, 96Ki, 128Ki etc.
+ // Note that when the suffix is not provided, the value is interpreted as bytes.
+ // Default: 60Ki bytes.
+ //
+ // +kubebuilder:validation:XIntOrString
+ // +kubebuilder:validation:Pattern="^[1-9]+[0-9]*([EPTGMK]i|[EPTGMk])?$"
+ // +optional
+ MaxRequestHeaderLimit *resource.Quantity `json:"maxRequestHeaderLimit,omitempty"`
}
// WithUnderscoresAction configures the action to take when an HTTP header with underscores
diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go
index b966f945df..6ab6ee2ef3 100644
--- a/api/v1alpha1/zz_generated.deepcopy.go
+++ b/api/v1alpha1/zz_generated.deepcopy.go
@@ -4977,6 +4977,11 @@ func (in *HeaderSettings) DeepCopyInto(out *HeaderSettings) {
*out = new(HostSettings)
(*in).DeepCopyInto(*out)
}
+ if in.MaxRequestHeaderLimit != nil {
+ in, out := &in.MaxRequestHeaderLimit, &out.MaxRequestHeaderLimit
+ x := (*in).DeepCopy()
+ *out = &x
+ }
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HeaderSettings.
diff --git a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml
index b873c582aa..0b56f3e259 100644
--- a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml
+++ b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml
@@ -748,6 +748,24 @@ spec:
- name
x-kubernetes-list-type: map
type: object
+ maxRequestHeaderLimit:
+ allOf:
+ - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ - pattern: ^[1-9]+[0-9]*([EPTGMK]i|[EPTGMk])?$
+ anyOf:
+ - type: integer
+ - type: string
+ description: |-
+ MaxRequestHeaderLimit provides configuration for the maximum size of the
+ request headers allowed for incoming connections, mapping to the Envoy
+ `max_request_headers_kb` HTTP connection manager setting. Requests whose
+ headers exceed this limit receive a 431 (Request Header Fields Too Large)
+ response. The value is rounded up to the nearest KiB, must be at least 1Ki,
+ and cannot exceed 8192Ki (the maximum Envoy supports).
+ For example, 60Ki, 96Ki, 128Ki etc.
+ Note that when the suffix is not provided, the value is interpreted as bytes.
+ Default: 60Ki bytes.
+ x-kubernetes-int-or-string: true
preserveXRequestID:
description: |-
PreserveXRequestID configures Envoy to keep the X-Request-ID header if passed for a request that is edge
diff --git a/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml b/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml
index 5b73c4161a..f6ed8c44a5 100644
--- a/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml
+++ b/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml
@@ -747,6 +747,24 @@ spec:
- name
x-kubernetes-list-type: map
type: object
+ maxRequestHeaderLimit:
+ allOf:
+ - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ - pattern: ^[1-9]+[0-9]*([EPTGMK]i|[EPTGMk])?$
+ anyOf:
+ - type: integer
+ - type: string
+ description: |-
+ MaxRequestHeaderLimit provides configuration for the maximum size of the
+ request headers allowed for incoming connections, mapping to the Envoy
+ `max_request_headers_kb` HTTP connection manager setting. Requests whose
+ headers exceed this limit receive a 431 (Request Header Fields Too Large)
+ response. The value is rounded up to the nearest KiB, must be at least 1Ki,
+ and cannot exceed 8192Ki (the maximum Envoy supports).
+ For example, 60Ki, 96Ki, 128Ki etc.
+ Note that when the suffix is not provided, the value is interpreted as bytes.
+ Default: 60Ki bytes.
+ x-kubernetes-int-or-string: true
preserveXRequestID:
description: |-
PreserveXRequestID configures Envoy to keep the X-Request-ID header if passed for a request that is edge
diff --git a/internal/gatewayapi/clienttrafficpolicy.go b/internal/gatewayapi/clienttrafficpolicy.go
index f6daf2a3ae..697ba63ec7 100644
--- a/internal/gatewayapi/clienttrafficpolicy.go
+++ b/internal/gatewayapi/clienttrafficpolicy.go
@@ -1001,6 +1001,10 @@ func translateClientIPDetection(clientIPDetection *egv1a1.ClientIPDetectionSetti
httpIR.ClientIPDetection = (*ir.ClientIPDetectionSettings)(clientIPDetection)
}
+// maxRequestHeaderLimitKB is the maximum value (in KiB) that Envoy supports for
+// the HTTP connection manager max_request_headers_kb setting.
+const maxRequestHeaderLimitKB = 8192
+
func translateListenerHeaderSettings(headerSettings *egv1a1.HeaderSettings, httpIR *ir.HTTPListener) error {
if headerSettings == nil {
return nil
@@ -1035,6 +1039,23 @@ func translateListenerHeaderSettings(headerSettings *egv1a1.HeaderSettings, http
var errs error
+ if headerSettings.MaxRequestHeaderLimit != nil {
+ // Envoy's max_request_headers_kb is expressed in KiB, so convert the
+ // byte quantity and round up to the nearest KiB.
+ bytes, ok := headerSettings.MaxRequestHeaderLimit.AsInt64()
+ switch {
+ case !ok || bytes < 1024:
+ errs = errors.Join(errs, fmt.Errorf("MaxRequestHeaderLimit value %s must be at least 1Ki", headerSettings.MaxRequestHeaderLimit.String()))
+ default:
+ kb := (bytes + 1023) / 1024
+ if kb > maxRequestHeaderLimitKB {
+ errs = errors.Join(errs, fmt.Errorf("MaxRequestHeaderLimit value %s exceeds the maximum of %dKi", headerSettings.MaxRequestHeaderLimit.String(), maxRequestHeaderLimitKB))
+ } else {
+ httpIR.Headers.MaxRequestHeadersKB = ptr.To(uint32(kb))
+ }
+ }
+ }
+
if headerSettings.EarlyRequestHeaders != nil {
headersToAdd, headersToRemove, removeOnMatch, err := translateHeaderModifier(headerSettings.EarlyRequestHeaders, "EarlyRequestHeaders")
if err != nil {
diff --git a/internal/gatewayapi/testdata/clienttrafficpolicy-max-request-header-limit-with-error.in.yaml b/internal/gatewayapi/testdata/clienttrafficpolicy-max-request-header-limit-with-error.in.yaml
new file mode 100644
index 0000000000..b60c4a74ec
--- /dev/null
+++ b/internal/gatewayapi/testdata/clienttrafficpolicy-max-request-header-limit-with-error.in.yaml
@@ -0,0 +1,29 @@
+clientTrafficPolicies:
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: ClientTrafficPolicy
+ metadata:
+ namespace: envoy-gateway
+ name: target-gateway-1
+ generation: 10
+ spec:
+ headers:
+ maxRequestHeaderLimit: "512"
+ targetRef:
+ group: gateway.networking.k8s.io
+ kind: Gateway
+ name: gateway-1
+gateways:
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: Gateway
+ metadata:
+ namespace: envoy-gateway
+ name: gateway-1
+ spec:
+ gatewayClassName: envoy-gateway-class
+ listeners:
+ - name: http-1
+ protocol: HTTP
+ port: 80
+ allowedRoutes:
+ namespaces:
+ from: Same
diff --git a/internal/gatewayapi/testdata/clienttrafficpolicy-max-request-header-limit-with-error.out.yaml b/internal/gatewayapi/testdata/clienttrafficpolicy-max-request-header-limit-with-error.out.yaml
new file mode 100644
index 0000000000..c2df66e38b
--- /dev/null
+++ b/internal/gatewayapi/testdata/clienttrafficpolicy-max-request-header-limit-with-error.out.yaml
@@ -0,0 +1,142 @@
+clientTrafficPolicies:
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: ClientTrafficPolicy
+ metadata:
+ generation: 10
+ name: target-gateway-1
+ namespace: envoy-gateway
+ spec:
+ headers:
+ maxRequestHeaderLimit: "512"
+ targetRef:
+ group: gateway.networking.k8s.io
+ kind: Gateway
+ name: gateway-1
+ status:
+ ancestors:
+ - ancestorRef:
+ group: gateway.networking.k8s.io
+ kind: Gateway
+ name: gateway-1
+ namespace: envoy-gateway
+ conditions:
+ - lastTransitionTime: null
+ message: 'Headers: MaxRequestHeaderLimit value 512 must be at least 1Ki.'
+ observedGeneration: 10
+ reason: Invalid
+ status: "False"
+ type: Accepted
+ - lastTransitionTime: null
+ message: spec.targetRef is deprecated, use spec.targetRefs instead
+ observedGeneration: 10
+ reason: DeprecatedField
+ status: "True"
+ type: Warning
+ controllerName: gateway.envoyproxy.io/gatewayclass-controller
+gateways:
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: Gateway
+ metadata:
+ name: gateway-1
+ namespace: envoy-gateway
+ spec:
+ gatewayClassName: envoy-gateway-class
+ listeners:
+ - allowedRoutes:
+ namespaces:
+ from: Same
+ name: http-1
+ port: 80
+ protocol: HTTP
+ status:
+ listeners:
+ - attachedRoutes: 0
+ conditions:
+ - lastTransitionTime: null
+ message: Sending translated listener configuration to the data plane
+ reason: Programmed
+ status: "True"
+ type: Programmed
+ - lastTransitionTime: null
+ message: Listener has been successfully translated
+ reason: Accepted
+ status: "True"
+ type: Accepted
+ - lastTransitionTime: null
+ message: Listener references have been resolved
+ reason: ResolvedRefs
+ status: "True"
+ type: ResolvedRefs
+ name: http-1
+ supportedKinds:
+ - group: gateway.networking.k8s.io
+ kind: HTTPRoute
+ - group: gateway.networking.k8s.io
+ kind: GRPCRoute
+infraIR:
+ envoy-gateway/gateway-1:
+ proxy:
+ listeners:
+ - name: envoy-gateway/gateway-1/http-1
+ ports:
+ - containerPort: 10080
+ name: http-80
+ protocol: HTTP
+ servicePort: 80
+ metadata:
+ labels:
+ gateway.envoyproxy.io/owning-gateway-name: gateway-1
+ gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway
+ ownerReference:
+ kind: GatewayClass
+ name: envoy-gateway-class
+ name: envoy-gateway/gateway-1
+ namespace: envoy-gateway-system
+xdsIR:
+ envoy-gateway/gateway-1:
+ accessLog:
+ json:
+ - path: /dev/stdout
+ globalResources:
+ proxyServiceCluster:
+ metadata:
+ kind: Service
+ name: envoy-envoy-gateway-gateway-1-196ae069
+ namespace: envoy-gateway-system
+ sectionName: "8080"
+ name: envoy-gateway/gateway-1
+ settings:
+ - addressType: IP
+ endpoints:
+ - host: 7.6.5.4
+ port: 8080
+ zone: zone1
+ metadata:
+ kind: Service
+ name: envoy-envoy-gateway-gateway-1-196ae069
+ namespace: envoy-gateway-system
+ sectionName: "8080"
+ name: envoy-gateway/gateway-1
+ protocol: TCP
+ http:
+ - address: 0.0.0.0
+ externalPort: 80
+ headers:
+ withUnderscoresAction: RejectRequest
+ hostnames:
+ - '*'
+ metadata:
+ kind: Gateway
+ name: gateway-1
+ namespace: envoy-gateway
+ sectionName: http-1
+ name: envoy-gateway/gateway-1/http-1
+ path:
+ escapedSlashesAction: UnescapeAndRedirect
+ mergeSlashes: true
+ port: 10080
+ readyListener:
+ address: 0.0.0.0
+ ipFamily: IPv4
+ path: /ready
+ port: 19003
diff --git a/internal/gatewayapi/testdata/clienttrafficpolicy-max-request-header-limit.in.yaml b/internal/gatewayapi/testdata/clienttrafficpolicy-max-request-header-limit.in.yaml
new file mode 100644
index 0000000000..05134ef8ce
--- /dev/null
+++ b/internal/gatewayapi/testdata/clienttrafficpolicy-max-request-header-limit.in.yaml
@@ -0,0 +1,29 @@
+clientTrafficPolicies:
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: ClientTrafficPolicy
+ metadata:
+ namespace: envoy-gateway
+ name: target-gateway-1
+ generation: 10
+ spec:
+ headers:
+ maxRequestHeaderLimit: 96Ki
+ targetRef:
+ group: gateway.networking.k8s.io
+ kind: Gateway
+ name: gateway-1
+gateways:
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: Gateway
+ metadata:
+ namespace: envoy-gateway
+ name: gateway-1
+ spec:
+ gatewayClassName: envoy-gateway-class
+ listeners:
+ - name: http-1
+ protocol: HTTP
+ port: 80
+ allowedRoutes:
+ namespaces:
+ from: Same
diff --git a/internal/gatewayapi/testdata/clienttrafficpolicy-max-request-header-limit.out.yaml b/internal/gatewayapi/testdata/clienttrafficpolicy-max-request-header-limit.out.yaml
new file mode 100644
index 0000000000..ded993ac32
--- /dev/null
+++ b/internal/gatewayapi/testdata/clienttrafficpolicy-max-request-header-limit.out.yaml
@@ -0,0 +1,143 @@
+clientTrafficPolicies:
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: ClientTrafficPolicy
+ metadata:
+ generation: 10
+ name: target-gateway-1
+ namespace: envoy-gateway
+ spec:
+ headers:
+ maxRequestHeaderLimit: 96Ki
+ targetRef:
+ group: gateway.networking.k8s.io
+ kind: Gateway
+ name: gateway-1
+ status:
+ ancestors:
+ - ancestorRef:
+ group: gateway.networking.k8s.io
+ kind: Gateway
+ name: gateway-1
+ namespace: envoy-gateway
+ conditions:
+ - lastTransitionTime: null
+ message: Policy has been accepted.
+ observedGeneration: 10
+ reason: Accepted
+ status: "True"
+ type: Accepted
+ - lastTransitionTime: null
+ message: spec.targetRef is deprecated, use spec.targetRefs instead
+ observedGeneration: 10
+ reason: DeprecatedField
+ status: "True"
+ type: Warning
+ controllerName: gateway.envoyproxy.io/gatewayclass-controller
+gateways:
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: Gateway
+ metadata:
+ name: gateway-1
+ namespace: envoy-gateway
+ spec:
+ gatewayClassName: envoy-gateway-class
+ listeners:
+ - allowedRoutes:
+ namespaces:
+ from: Same
+ name: http-1
+ port: 80
+ protocol: HTTP
+ status:
+ listeners:
+ - attachedRoutes: 0
+ conditions:
+ - lastTransitionTime: null
+ message: Sending translated listener configuration to the data plane
+ reason: Programmed
+ status: "True"
+ type: Programmed
+ - lastTransitionTime: null
+ message: Listener has been successfully translated
+ reason: Accepted
+ status: "True"
+ type: Accepted
+ - lastTransitionTime: null
+ message: Listener references have been resolved
+ reason: ResolvedRefs
+ status: "True"
+ type: ResolvedRefs
+ name: http-1
+ supportedKinds:
+ - group: gateway.networking.k8s.io
+ kind: HTTPRoute
+ - group: gateway.networking.k8s.io
+ kind: GRPCRoute
+infraIR:
+ envoy-gateway/gateway-1:
+ proxy:
+ listeners:
+ - name: envoy-gateway/gateway-1/http-1
+ ports:
+ - containerPort: 10080
+ name: http-80
+ protocol: HTTP
+ servicePort: 80
+ metadata:
+ labels:
+ gateway.envoyproxy.io/owning-gateway-name: gateway-1
+ gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway
+ ownerReference:
+ kind: GatewayClass
+ name: envoy-gateway-class
+ name: envoy-gateway/gateway-1
+ namespace: envoy-gateway-system
+xdsIR:
+ envoy-gateway/gateway-1:
+ accessLog:
+ json:
+ - path: /dev/stdout
+ globalResources:
+ proxyServiceCluster:
+ metadata:
+ kind: Service
+ name: envoy-envoy-gateway-gateway-1-196ae069
+ namespace: envoy-gateway-system
+ sectionName: "8080"
+ name: envoy-gateway/gateway-1
+ settings:
+ - addressType: IP
+ endpoints:
+ - host: 7.6.5.4
+ port: 8080
+ zone: zone1
+ metadata:
+ kind: Service
+ name: envoy-envoy-gateway-gateway-1-196ae069
+ namespace: envoy-gateway-system
+ sectionName: "8080"
+ name: envoy-gateway/gateway-1
+ protocol: TCP
+ http:
+ - address: 0.0.0.0
+ externalPort: 80
+ headers:
+ maxRequestHeadersKB: 96
+ withUnderscoresAction: RejectRequest
+ hostnames:
+ - '*'
+ metadata:
+ kind: Gateway
+ name: gateway-1
+ namespace: envoy-gateway
+ sectionName: http-1
+ name: envoy-gateway/gateway-1/http-1
+ path:
+ escapedSlashesAction: UnescapeAndRedirect
+ mergeSlashes: true
+ port: 10080
+ readyListener:
+ address: 0.0.0.0
+ ipFamily: IPv4
+ path: /ready
+ port: 19003
diff --git a/internal/ir/xds.go b/internal/ir/xds.go
index a1cf02a509..7a94f45960 100644
--- a/internal/ir/xds.go
+++ b/internal/ir/xds.go
@@ -951,6 +951,10 @@ type HeaderSettings struct {
// LateRemoveResponseHeadersOnMatch defines header name matchers that would remove headers after envoy response processing.
LateRemoveResponseHeadersOnMatch []*StringMatch `json:"lateRemoveResponseHeadersOnMatch,omitempty" yaml:"lateRemoveResponseHeadersOnMatch,omitempty"`
+
+ // MaxRequestHeadersKB defines the maximum request headers size in KiB allowed for incoming connections.
+ // Maps to the Envoy `max_request_headers_kb` HTTP connection manager setting.
+ MaxRequestHeadersKB *uint32 `json:"maxRequestHeadersKB,omitempty" yaml:"maxRequestHeadersKB,omitempty"`
}
// ClientTimeout sets the timeout configuration for downstream connections
diff --git a/internal/ir/zz_generated.deepcopy.go b/internal/ir/zz_generated.deepcopy.go
index 252d73c925..ae7b305c58 100644
--- a/internal/ir/zz_generated.deepcopy.go
+++ b/internal/ir/zz_generated.deepcopy.go
@@ -2822,6 +2822,11 @@ func (in *HeaderSettings) DeepCopyInto(out *HeaderSettings) {
}
}
}
+ if in.MaxRequestHeadersKB != nil {
+ in, out := &in.MaxRequestHeadersKB, &out.MaxRequestHeadersKB
+ *out = new(uint32)
+ **out = **in
+ }
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HeaderSettings.
diff --git a/internal/xds/translator/listener.go b/internal/xds/translator/listener.go
index be3e2eb136..48c038a976 100644
--- a/internal/xds/translator/listener.go
+++ b/internal/xds/translator/listener.go
@@ -406,6 +406,11 @@ func (t *Translator) addHCMToXDSListener(
mgr.StripTrailingHostDot = irListener.Host.StripTrailingHostDot
}
+ // Set the maximum request headers size if configured.
+ if h := irListener.Headers; h != nil && h.MaxRequestHeadersKB != nil {
+ mgr.MaxRequestHeadersKb = wrapperspb.UInt32(*h.MaxRequestHeadersKB)
+ }
+
// Set the :scheme header to match the upstream transport protocol (http/https) if configured.
// This ensures the correct scheme is sent to backends using TLS when enabled.
if irListener.MatchBackendScheme {
diff --git a/internal/xds/translator/testdata/in/xds-ir/max-request-headers-kb.yaml b/internal/xds/translator/testdata/in/xds-ir/max-request-headers-kb.yaml
new file mode 100644
index 0000000000..4043ce9b6d
--- /dev/null
+++ b/internal/xds/translator/testdata/in/xds-ir/max-request-headers-kb.yaml
@@ -0,0 +1,18 @@
+http:
+ - name: "first-listener"
+ address: "::"
+ port: 8081
+ hostnames:
+ - "*"
+ routes:
+ - name: "first-route"
+ hostname: "*"
+ destination:
+ name: "first-route-dest"
+ settings:
+ - endpoints:
+ - host: "1.1.1.1"
+ port: 8081
+ name: "first-route-dest/backend/0"
+ headers:
+ maxRequestHeadersKB: 96
diff --git a/internal/xds/translator/testdata/out/xds-ir/max-request-headers-kb.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/max-request-headers-kb.clusters.yaml
new file mode 100644
index 0000000000..886a7d9f6c
--- /dev/null
+++ b/internal/xds/translator/testdata/out/xds-ir/max-request-headers-kb.clusters.yaml
@@ -0,0 +1,23 @@
+- circuitBreakers:
+ thresholds:
+ - maxRetries: 1024
+ commonLbConfig: {}
+ connectTimeout: 10s
+ dnsLookupFamily: V4_PREFERRED
+ edsClusterConfig:
+ edsConfig:
+ ads: {}
+ resourceApiVersion: V3
+ serviceName: first-route-dest
+ ignoreHealthOnHostRemoval: true
+ loadBalancingPolicy:
+ policies:
+ - typedExtensionConfig:
+ name: envoy.load_balancing_policies.least_request
+ typedConfig:
+ '@type': type.googleapis.com/envoy.extensions.load_balancing_policies.least_request.v3.LeastRequest
+ localityLbConfig:
+ localityWeightedLbConfig: {}
+ name: first-route-dest
+ perConnectionBufferLimitBytes: 32768
+ type: EDS
diff --git a/internal/xds/translator/testdata/out/xds-ir/max-request-headers-kb.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/max-request-headers-kb.endpoints.yaml
new file mode 100644
index 0000000000..79a5266441
--- /dev/null
+++ b/internal/xds/translator/testdata/out/xds-ir/max-request-headers-kb.endpoints.yaml
@@ -0,0 +1,12 @@
+- clusterName: first-route-dest
+ endpoints:
+ - lbEndpoints:
+ - endpoint:
+ address:
+ socketAddress:
+ address: 1.1.1.1
+ portValue: 8081
+ loadBalancingWeight: 1
+ loadBalancingWeight: 1
+ locality:
+ region: first-route-dest/backend/0
diff --git a/internal/xds/translator/testdata/out/xds-ir/max-request-headers-kb.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/max-request-headers-kb.listeners.yaml
new file mode 100644
index 0000000000..96486484ae
--- /dev/null
+++ b/internal/xds/translator/testdata/out/xds-ir/max-request-headers-kb.listeners.yaml
@@ -0,0 +1,34 @@
+- address:
+ socketAddress:
+ address: '::'
+ portValue: 8081
+ defaultFilterChain:
+ filters:
+ - name: envoy.filters.network.http_connection_manager
+ typedConfig:
+ '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
+ commonHttpProtocolOptions:
+ headersWithUnderscoresAction: REJECT_REQUEST
+ http2ProtocolOptions:
+ initialConnectionWindowSize: 1048576
+ initialStreamWindowSize: 65536
+ maxConcurrentStreams: 100
+ httpFilters:
+ - name: envoy.filters.http.router
+ typedConfig:
+ '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
+ suppressEnvoyHeaders: true
+ maxRequestHeadersKb: 96
+ normalizePath: true
+ rds:
+ configSource:
+ ads: {}
+ resourceApiVersion: V3
+ routeConfigName: first-listener
+ serverHeaderTransformation: PASS_THROUGH
+ statPrefix: http-8081
+ useRemoteAddress: true
+ name: first-listener
+ maxConnectionsToAcceptPerSocketEvent: 1
+ name: first-listener
+ perConnectionBufferLimitBytes: 32768
diff --git a/internal/xds/translator/testdata/out/xds-ir/max-request-headers-kb.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/max-request-headers-kb.routes.yaml
new file mode 100644
index 0000000000..0b5b4bee7b
--- /dev/null
+++ b/internal/xds/translator/testdata/out/xds-ir/max-request-headers-kb.routes.yaml
@@ -0,0 +1,14 @@
+- ignorePortInHostMatching: true
+ name: first-listener
+ virtualHosts:
+ - domains:
+ - '*'
+ name: first-listener/*
+ routes:
+ - match:
+ prefix: /
+ name: first-route
+ route:
+ cluster: first-route-dest
+ upgradeConfigs:
+ - upgradeType: websocket
diff --git a/release-notes/current/new_features/5368-max-request-header-limit-clienttrafficpolicy.md b/release-notes/current/new_features/5368-max-request-header-limit-clienttrafficpolicy.md
new file mode 100644
index 0000000000..2a96e95523
--- /dev/null
+++ b/release-notes/current/new_features/5368-max-request-header-limit-clienttrafficpolicy.md
@@ -0,0 +1 @@
+Added a `maxRequestHeaderLimit` field under `ClientTrafficPolicy`'s `headers` to configure the maximum request header size (Envoy's `max_request_headers_kb`) without an EnvoyPatchPolicy. The value is a Quantity rounded up to the nearest KiB, must be at least 1Ki, and cannot exceed 8192Ki.
diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md
index 12e60a35cc..4217fbaba3 100644
--- a/site/content/en/latest/api/extension_types.md
+++ b/site/content/en/latest/api/extension_types.md
@@ -3421,6 +3421,7 @@ _Appears in:_
| `earlyRequestHeaders` | _[HTTPHeaderFilter](#httpheaderfilter)_ | false | | EarlyRequestHeaders defines settings for early request header modification, before envoy performs
routing, tracing and built-in header manipulation. |
| `lateResponseHeaders` | _[HTTPHeaderFilter](#httpheaderfilter)_ | false | | LateResponseHeaders defines settings for global response header modification. |
| `host` | _[HostSettings](#hostsettings)_ | false | | Host enables managing how the Host/Authority header set by clients can be normalized. |
+| `maxRequestHeaderLimit` | _[Quantity](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.32/#quantity-resource-api)_ | false | | MaxRequestHeaderLimit provides configuration for the maximum size of the
request headers allowed for incoming connections, mapping to the Envoy
`max_request_headers_kb` HTTP connection manager setting. Requests whose
headers exceed this limit receive a 431 (Request Header Fields Too Large)
response. The value is rounded up to the nearest KiB, must be at least 1Ki,
and cannot exceed 8192Ki (the maximum Envoy supports).
For example, 60Ki, 96Ki, 128Ki etc.
Note that when the suffix is not provided, the value is interpreted as bytes.
Default: 60Ki bytes. |
#### HealthCheck