Announcing Envoy Gateway v1.10
Envoy Gateway v1.10 release announcement.
37 minute read
We are excited to announce the release of Envoy Gateway v1.10.0.
This release delivers new capabilities across traffic management, security, extensibility, observability, and infrastructure — along with key bug fixes and performance improvements. We extend our thanks to the entire Envoy Gateway community for your ongoing contributions, feedback, and collaboration. Your efforts make each release possible.
| Release Notes | Docs | Compatibility Matrix | Install |
|---|
What’s New
Envoy Gateway v1.10.0 introduces powerful enhancements, resolves critical issues, and continues to improve the platform’s reliability and performance.
🚨 Breaking Changes
- Cookie-based session persistence now always uses
Path=/instead of the matched HTTPRoute path, following updated GEP-1619 guidance. This fixes persistence when an upstream proxy rewrites the request path. Rules on the same host must use distinctsessionNamevalues to avoid cookie conflicts, or omitsessionNameto generate unique names per rule. Existing cookies remain until they expire; clients receive a new cookie withPath=/on their next request. ClusterSettingsinapi/v1alpha1has been split:ClusterSettingsnow holds only the CDS-only fields (loadBalancer,proxyProtocol,tcpKeepalive,healthCheck,circuitBreaker,timeout,connection,dns,http2), and a newBackendSettingstype embedsClusterSettingsand addsretry. YAML/JSON field paths are unchanged, but Go code that references these types directly is affected:retrymoved offClusterSettingsontoBackendSettings, andBackendCluster.BackendSettingsis now typed*v1alpha1.BackendSettingsinstead of*v1alpha1.ClusterSettings. Code that importsapi/v1alpha1and constructs or type-referencesClusterSettingsdirectly (e.g. extension implementations) needs to update to the new types.- Strict Lua validation now enforces a fail-closed allowlist of filesystem paths and environment variables that Lua scripts may access during validation. By default the allowlist is empty, so all filesystem and environment variable access during validation is denied. To permit specific paths or environment variables, configure
EnvoyProxy.spec.lua.strictValidation.allowedPathsandallowedEnvVars. A built-in set of sensitive paths (/etc,/proc,/sys,/certs,/var/run/secrets,/run/secrets) remains always denied and cannot be re-enabled through the allowlist. This only affects theStrictLua validation mode;InsecureSyntaxandDisabledare unchanged. - Wasm.Code is now a pointer (*WasmCodeSource) and is optional. Go clients that set Code: WasmCodeSource{…} must take the address. YAML that already sets code is unchanged. wasm[].name is limited to 253 characters. The unused ConfigMap Wasm code type was removed from the CRD enum.
- EnvoyProxy
KubernetesServiceSpec.loadBalancerSourceRangesnow accepts at most 64 entries and validates each value as a CIDR at CRD admission using the Kubernetescidrformat;loadBalancerIPvalidation now uses CEL IP parsing while preserving IPv4-only behavior. - Lua scripts that use the
getfenv,setfenv,newproxy, ormoduleglobals now failStrictLua validation, which is the default mode. Rewrite the script without them, or setEnvoyProxy.spec.lua.validationTypetoInsecureSyntaxorDisabled. - Rate-limited 429s now include
Retry-Afterby default. SetClientTrafficPolicy.spec.headers.disableRetryAfterHeader: true to preserve the previous behavior. - The
watchable_subscribe_duration_secondshistogram bucket boundaries changed from{0.001, 0.01, 0.1, 1, 5, 10}to{0.005, 0.025, 0.1, 0.25, 0.5, 1, 2, 4, 8, 15, 30, 60, 120}to improve latency resolution. The metric name and its_sum/_countseries are unchanged, but_bucketseries withle="0.001",le="0.01",le="5", andle="10"are no longer emitted;le="0.1"andle="1"remain available. Update any dashboard, alert, or recording rule that references the removed boundaries directly. Queries that aggregate dynamically byle, such ashistogram_quantile(), do not require changes. - Active HTTP health checks now use the HTTP protocol of the requests to the backend instead of always using HTTP/1.1. Health checks to TLS backends offer h2 and http/1.1 through ALPN and use the protocol that’s negotiated, and health checks to backends that use HTTP/2, such as GRPCRoute backends or those with the
kubernetes.io/h2cappProtocol, use HTTP/2. SethealthCheck.active.http.protocoltoHTTP1in the BackendTrafficPolicy to keep the previous behavior for a backend whose health check endpoint only serves HTTP/1.1. - SecurityPolicy authorization rules with
operationon a TCPRoute are now rejected with a translation error. - HTTP is no longer supported as an OIDC issuer URL scheme.
- OCI Wasm image pulls now require the registry to serve HTTPS. The implicit fallback to plain HTTP has been removed, so a Wasm extension backed by a plain HTTP registry will fail to load unless that registry is explicitly configured as insecure.
- A
SecurityPolicywhoseauthorizationrules reference a JWT provider that is not defined injwt.providersis now rejected with anAccepted: Falsecondition, and the routes it targets return a 500 direct response. Previously the policy was accepted but the rule never matched, so a request without a valid JWT was rejected with a 401 and a request with a valid JWT fell through toauthorization.defaultAction, a 403 when it isDeny. Fix the provider name, define the missing provider, or - for a policy withmergeTypeset - define it on the parent policy it merges into. - Changed the xDS shape of Lua filters generated from an
EnvoyExtensionPolicy. The HTTP connection manager now holds one empty, disabled Lua filter per execution slot, namedenvoy.filters.http.lua/<index>, instead of one filter per policy carrying its script. Each route carries its scripts asLuaPerRouteconfiguration with ashared_vm_idset to the script name, so Envoy builds one set of Lua VMs per script no matter how many routes run it. A script’s VM is now shared across every listener that runs it, where previously each listener had its own. Global variables that a script uses as shared state, such as a cache or a counter, were already shared by all routes of the policy on one listener and are now also shared across listeners. This should rarely matter, but scripts that rely on per-listener globals should be aware of it and use a separate policy per listener if needed, since each policy script gets its own VM. AnEnvoyPatchPolicythat targets a Lua filter by its old policy-derived name, or edits a script through the listener, needs to target the route instead. - The session name generated when a route rule omits
sessionPersistence.sessionNamenow ends with a short hash of the route identity (for examplehttproute-default-route-1-rule-0-1a2b3c4d), so routes whose namespace and name flatten to the same string (such asa-b/canda/b-c) no longer share a session cookie or header. Existing HTTPRoute sessions that rely on the generated name lose affinity once after upgrading; setsessionNameexplicitly to keep a stable name. EnvoyProxy.spec.dynamicModules[].local.pathnow requires an absolute path at CRD admission, matching the existing rule for local Wasm module paths. A relative path such asfoo.sopreviously passed validation and is now rejected.- Backend now requires
endpointswhentypeisEndpoints(the default). Such Backends previously passed API validation even though they had no destination to route to, and are now rejected. - ClientTrafficPolicy now rejects
tls.clientValidation.subjectAltNames,certificateHashesandspkiHasheswhen the client validation mode isVerifyIfGiven(or the deprecatedoptional: true). Envoy requires a client certificate whenever those matchers are set, so the combination silently behaved asRequireAndVerify; useRequireAndVerifywhen those fields are needed. - Health check
timeoutandinterval(active), andintervalandbaseEjectionTime(passive), are now rejected at admission when set to0s. These values were previously accepted by the API but failed IR validation during translation. The same rule now also applies torateLimit.serviceSettingsin the EnvoyGateway configuration, so an invalid value fails at startup instead of blocking xDS updates. - Removed the
watchable_depthmetric. It read the length of the subscription channel, whichwatchable.Map.Subscribereturns unbuffered, so it always reported 0. The bundled Grafana dashboards now build their Namespace and Runner variables fromwatchable_subscribe_total, and the depth panel is gone; any alert or dashboard of your own that querieswatchable_depthshould be removed or repointed. - Extension manager
backendResourcescan no longer include the backend kinds Envoy Gateway resolves itself (coreService,multicluster.x-k8s.io/ServiceImport,gateway.envoyproxy.io/Backend), and Envoy Gateway now fails to start with such a configuration. Because extension backends are matched by group and kind, listing one of these took over every backendRef of that kind in the cluster, skipping Envoy Gateway’s own validation and endpoint resolution for them. Remove these entries frombackendResources; extensions that need to adjust these backends should use the xDS translator hooks, such as theClusterhook, instead. - The jwt_authn requirement generated from a JWT
SecurityPolicyis now named after the SecurityPolicy that applies to the route,<namespace>/<name>, or for a merged policy after the route and parent policies it was built from,<route namespace>/<route name>+<parent namespace>/<parent name>, instead of the provider names and a hash of its content (<provider>[-or-<provider>...][-missing|-missing-or-failed]_<hash>). Routes that share a policy still share one requirement. AnEnvoyPatchPolicythat targets a requirement by its hash-based name needs to use the new name. The names change once on upgrade, so requests on connections opened before it may be refused with 403Wrong requirement_nameuntil those connections drain.
🔒 Security Updates
- Hardened the Lua validation sandbox by blocking the
getfenv,setfenv,newproxy, andmoduleglobals. - Enabled AES-256-GCM encryption for OAuth2/OIDC session cookies and disabled the legacy AES-256-CBC decryption path in the Envoy proxy bootstrap, addressing the padding oracle in CVE-2026-47775. Existing OIDC sessions were encrypted with AES-256-CBC and are no longer accepted, so users with an active session are redirected to re-authenticate once after upgrading. This is set in the default bootstrap, so an
EnvoyProxyusingspec.bootstrapwith typeReplace(the default when no type is given) does not receive it; OIDC users with a replacement bootstrap must addenvoy.reloadable_features.oauth2_use_gcm_encryption: trueandenvoy.reloadable_features.oauth2_legacy_cbc_decrypt_compat: falseto alayered_runtimestatic layer themselves. - Added validation for the OIDC issuer URL configured in SecurityPolicy.
- Fixed a nil pointer dereference in SecurityPolicy translation for TCPRoutes: a listener with no corresponding xDS IR entry (for example, a Gateway listener marked
Conflictedfor sharing a port with another listener) could panic the control plane during translation, silently dropping that reconcile’s IR and status publication. - Fixed a confused-deputy/PSA-escape issue (GHSA-w42f-28h3-998w) where a tenant-supplied
KubernetesContainerSpec.SecurityContexton anEnvoyProxyreplaced Envoy Gateway’s hardened defaultSecurityContextoutright instead of merging on top of it, allowing a tenant whoseEnvoyProxyis materialized in the shared controller namespace to drop restrictions (e.g. run privileged or as root) that the controller namespace’s Pod Security Admission would otherwise enforce. - Fixed a control-plane availability issue in EnvoyExtensionPolicy Wasm OCI permission handling.
- Fixed OCI Wasm image pulls silently downgrading to plain HTTP when the registry rejected the HTTPS request, which allowed an on-path attacker to serve arbitrary Wasm code to the Envoy proxies. Plain HTTP is now used only for registries that are explicitly configured as insecure.
- Prevent user-defined proxies from conflicting with or using controller-owned resources in ControllerNamespace mode. The following are now rejected: resource names colliding with
envoy-gatewayorenvoy-gateway-config; service account names matching the controller’s own SA or the certgen SA (<fullname>-certgen, where<fullname>is the Helm release fullname, defaulting toenvoy-gateway); volumes mounting theenvoy-gatewaySecret or ConfigMap; and environment variables referencing those resources viasecretKeyRef,configMapKeyRef, orenvFrom. - Added an
EnvoyProxyPatchruntime flag toEnvoyGateway. EnvoyProxy Kubernetes resourcepatchfields can grant arbitrary access to resources applied by Envoy Gateway’s more privileged ServiceAccount when EnvoyProxy is namespace-scoped and tenant-authored. The flag is enabled by default to preserve pre-existing behavior; multi-tenant clusters where tenants can author their own EnvoyProxy resources should disable it throughruntimeFlags.disabled. - Fixed a panic in the xDS server’s Kubernetes JWT authentication when the TokenReview response contains an
Extrafield without the pod name key (authentication.kubernetes.io/pod-name). Such tokens (e.g. service account tokens not bound to a pod) are now rejected with anUnauthenticatederror instead of crashing the control plane.
✨ New Features
API & Traffic Management
- Added a
failTrafficOnPanicfield to the zone-awarepreferLocalload balancer settings, exposing Envoy’sfail_traffic_on_panic. When a cluster enters panic mode because the share of healthy endpoints dropped belowpanicThreshold, Envoy load balances across every endpoint regardless of health; setting this totruemakes it reject the traffic instead, applying backpressure to clients and giving the backend room to recover. It sits underpreferLocalbecause Envoy only defines the setting insidezone_aware_lb_config. When unset it defaults tofalse, preserving the existing behavior. - Added support for tuning Gzip, Brotli and Zstd compressor settings in the
compressorfield ofBackendTrafficPolicy. - Added a
modefield torequestBufferinBackendTrafficPolicy. The defaultBufferAndLimitkeeps the existing behavior of buffering the whole request before forwarding it upstream. The newLimitOnlymode only raises the request body buffer limit for the route, so a filter later in the chain can buffer a larger body without the gateway fully buffering the request; unlikeBufferAndLimit, it is compatible with protocol upgrades and streaming. Requests whose buffered body exceeds the limit are rejected with HTTP 413 in both modes. - Added session persistence support to
GRPCRoute. ThesessionPersistencefield on a GRPCRoute rule was previously accepted but ignored; it is now applied the same way as onHTTPRoute. Cookie-based (the default) and header-based persistence are supported, a session name is generated per rule whensessionNameis omitted, andabsoluteTimeoutsets the cookie TTL whencookieConfig.lifetimeTypeisPermanent. A rule with an unknown persistence type is rejected with reasonUnsupportedValue. - Added a
forwardProtoConfigfield toproxyProtocolinClientTrafficPolicy, allowing thex-forwarded-protoheader to be inferred from the PROXY protocol destination port when PROXY protocol is enabled. This is useful when a Layer 4 load balancer (such as AWS NLB) terminates TLS and forwards traffic to Envoy over PROXY protocol. - Added a
mutationsfield toClientTrafficPolicy’sheaders.earlyRequestHeadersandheaders.lateResponseHeadersfilters, an ordered list of header operations applied in exactly the order given. Each entry is one ofwrite(a header plus anactionofAppend,Overwrite,AddIfAbsentorOverwriteIfExists, and an optionalkeepEmptyValue),remove, orremoveOnMatch. This expresses sequences the existing flatset/add/addIfAbsent/remove/removeOnMatchfields cannot, such as overwriting a header and then appending to it, or removing a header and then re-adding it. - Added a
maxRequestHeaderLimitfield underClientTrafficPolicy’sheadersto configure the maximum request header size (Envoy’smax_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. - Added a
disableConnectionReusefield to the active health check configuration in BackendTrafficPolicy, exposing Envoy’sHealthCheck.reuse_connection. Setting it totruemakes Envoy open a fresh connection for each health check probe, which is required to interoperate with backends such as haproxy-style agent checks that do not expect a reused connection. When unset it defaults tofalse, preserving the existing behavior of reusing the connection across probes. - Added
ClientTrafficPolicy.spec.headers.disableRetryAfterHeaderto control whether aRetry-Afterheader is emitted on rate-limited 429 responses, for both the global and local rate limit filters. The header is emitted by default; set this field totrueto opt out. - Added
healthCheck.active.http.protocolto BackendTrafficPolicy, to select the HTTP protocol used to send active health check requests to a backend whose health check endpoint doesn’t serve the protocol of its application endpoint. - Added support for BackendTrafficPolicy to target a Service, ServiceImport, or Backend directly, applying cluster-scoped settings to the backend’s merged BackendCluster (requires the
MergeBackendsEnvoyProxy feature). - Added
ClientTrafficPolicy.spec.http3.advertisedPortto configure the port in HTTP/3alt-svcresponse headers when an external load balancer exposes a different port than the Gateway listener. - Added gRPC-JSON transcoding to
HTTPRouteFilterwith thegrpcJSONTranscoderfield, so REST/JSON clients can call gRPC backends. The proto descriptor set is read from a ConfigMap and validated when the route is translated. - Added support for matching on request headers in
BackendTrafficPolicy.responseOverride. - Added
Backendtype: KubernetesServiceto route to an in-cluster Service through its EndpointSlices, so BackendtlsandappProtocolsapply on top of the same endpoint handling as a Service backendRef. - Added
http3toBackendTrafficPolicy, enabling HTTP/3 to the backend. The defaultAutomode uses HTTP/3 only for backends that advertise it through analt-svcresponse header and falls back to TCP when QUIC is unavailable, whileAlwayssends every request over HTTP/3. The backend must be configured with TLS through a BackendTLSPolicy or the Backend’sspec.tls; where HTTP/3 cannot be used for a route, it is left off and the policy reports aWarningcondition. - Added support for rewriting the Host header on mirrored requests in
HTTPRouteFilterusingspec.requestMirror.hostnamewithLiteralandBackendmodes. - Added
BackendTrafficPolicy.spec.http1(BackendHTTP1Settings) for configuring HTTP/1 settings on upstream backend connections, includingenableTrailersandpreserveHeaderCase. When bothBackendTrafficPolicy.spec.http1andClientTrafficPolicy.spec.http1are set,BackendTrafficPolicytakes precedence for backend traffic. Settinghttp1: {}(empty) forces explicit HTTP/1 on the upstream cluster. When targeting a GRPCRoute, TCPRoute, TLSRoute, or UDPRoute directly viatargetRef,targetRefs, ortargetSelectors, the policy remains accepted but receives aWarningcondition since those route kinds do not use HTTP/1 and the field has no effect. When attached to a broader target (Gateway, ListenerSet, or a specific listener viasectionName),http1is silently ignored for any GRPCRoute, TCPRoute, TLSRoute, or UDPRoute destinations beneath that target and still applies to HTTPRoute destinations. For gRPC-only external service backends (ExtProc, gRPC ExtAuth, ALS, OpenTelemetry access log/metrics/tracing),http1on their ownBackendSettingsis rejected at admission by existing validations on those types. - Added the
backendPriorityfield toHTTPRouteFilter, which sets the Envoy locality priority of the HTTPRoute backendRef it is referenced from, enabling multi-tier, per-route priority failover between a rule’s backendRefs. Rules using it are excluded from MergeBackends cluster deduplication. - Added
maxConnectionDurationJitterto the connection limit settings in ClientTrafficPolicy, so connections opened at the same time do not all reach their max duration and reconnect together.
Security & Authentication
- Added OIDC token cookie chunking: Envoy now splits an access, ID or refresh token too large for one browser cookie across several cookies, instead of the browser dropping it and the login looping back to the OIDC provider. Tokens that fit in one cookie are written as before. Envoy Proxy older than v1.40 can’t read the split cookies, so while proxies of an earlier release serve the same hostnames, for example during the upgrade, disable the new
OIDCTokenCookieChunkingruntime flag, which is enabled by default, throughruntimeFlags.disabled. - Added
spec.oidc.postLogoutRedirectto SecurityPolicy to configure thepost_logout_redirect_uriparameter EG sends to the OIDC Provider during RP-Initiated Logout, either by setting an expliciturior by settingdisabledto omit the parameter entirely. - Added
spec.oidc.codeVerifierTTLto SecurityPolicy to configure how long the PKCE code verifier cookie generated during the OAuth2 authorization flow remains valid. AlongsidecsrfTokenTTL, this bounds how long the cookies of an abandoned authorization flow live in the browser. Defaults to 10 minutes. - Added support for customizing all Envoy OAuth2 cookie names through SecurityPolicy OIDC cookieNames, and a cookieNames.suffix field that pins the suffix Envoy Gateway appends to every OAuth2 cookie name.
- Added
UDPRouteas a validSecurityPolicytarget, so client IP allowlists and denylists can now be applied to UDP services such as DNS, syslog, NTP, telemetry collectors and game servers. As withTCPRoute, onlyauthorizationrules usingprincipal.clientCIDRsapply; the client IP is the source address of the datagram, and a denied source is dropped without a reply rather than receiving an error. - Added an optional
claimPathfield toSecurityPolicy’s JWTclaimToHeaders, letting claims whose names contain dots — such as URI-namespaced OIDC claims likehttps://example.com/claims/tenant_name— be extracted into a header via explicit path segments instead of the dot-splitclaimfield. - Added
SecurityPolicy.spec.cors.allowOriginRegexesto allow CORS origins using regular expressions alongside existingallowOriginsentries. - Added
IssuertocertDetailsToAddin ClientTrafficPolicy, so the issuer of the client certificate can be forwarded in the XFCC header. - Added
packAsBytesto SecurityPolicyextAuth.bodyToExtAuthto send the request body to gRPC external authorization services as raw bytes.maxRequestBytesis now optional and defaults to 32768 bytes (32KiB).
Extensibility
- Added local Wasm modules registered on EnvoyProxy.spec.wasmModules (source type Local with a path today; shape matches DynamicModule for future sources). When EnvoyExtensionPolicy.wasm[].code is omitted, wasm[].name is looked up in that list so Envoy can load the file without the control-plane fetch and cache path. HTTP and Image stay on wasm[].code. The unused ConfigMap enum value (never implemented) was removed.
- Added named backend dependencies to EnvoyExtensionPolicy for Lua, Wasm and dynamic module HTTP callouts, with optional backend connection settings and alias bindings exposed through static route metadata. Disabled by default and enabled by setting
extensionApis.enableExtensionBackendstotruein the EnvoyGateway configuration. - Added the per-entry
vmSharingScopeoption to EnvoyExtensionPolicy. Setting it toNamespaceallows compatible Wasm extensions in the same policy namespace to reuse a VM on each Envoy worker. It defaults toPolicy, preserving a separate VM ID for each policy’s Wasm entry. The same entry may reuse its VM across routes; different entries do not share a VM. ExtensionServerPolicycan now target aGatewayClass(viatargetRefsortargetSelectors) whenmergeGatewaysis enabled, attaching the policy to every listener of the merged Gateways. Targeting aGatewayClasswithoutmergeGatewaysnow reports anAccepted=Falsestatus instead of being silently ignored.sectionNameis not supported on aGatewayClasstarget, since Gateway API only defines it for kinds such asGateway, and such a target is rejected asInvalid; target aGatewaywithsectionNameto select a listener.- Added support for listener TLS certificates via extension server using the
Secrethook. A Gateway or ListenerSet listener’stls.certificateRefscan reference a kind registered in the Extension Manager’ssecretResources, and the extension server resolves it to the SDS secret config Envoy uses. - Added a
timeoutfield to the Extension Manager’sservice. It bounds each extension hook call, including the time spent waiting for the extension server to become reachable and any retries. Previously an unreachable extension server blocked xDS translation indefinitely, because hook calls had no deadline andfailOpenonly applies once a call returns an error. Withtimeoutset, the call fails withDEADLINE_EXCEEDEDandfailOpentakes effect. Leavingtimeoutunset keeps the existing behavior. ExtensionServerPolicycan now target aListenerSet(viatargetRefsortargetSelectors), or a singleListenerSetlistener viasectionName, attaching the policy to that ListenerSet’s listeners and reporting status against theListenerSet. Previously such policies were silently ignored.- Extension server policies targeting a
Gateway(withoutsectionName) are now forwarded toPostVirtualHostModifyHookvia the hook context’sextension_resourcesfield. Each virtual host receives only the policies attached to its own listener (or containing Gateway).
Infrastructure & Helm
- Added
deployment.pod.initContainersanddeployment.pod.extraContainersto thegateway-helmchart, passed through to the control-plane Deployment so additional init/sidecar containers (including native sidecars withrestartPolicy: Always) can run alongside the Envoy Gateway controller. - Added a
serviceSettingsfield underEnvoyGateway.spec.rateLimitto configure the Envoy cluster that connects the proxies to the global rate limit service, such as circuit breakers, timeouts and health checks. It reuses theClusterSettingstype, but its fields are supported on a best-effort basis: known-unsupported fields are rejected (see the API reference), and a field that doesn’t apply to the rate limit service cluster may have no effect. The CRD schema defaults, OpenAPI constraints and CEL rules ofClusterSettingsare applied when the config is loaded, so an invalid value is rejected at startup. - Added
PreferDualStacktoEnvoyProxy’sipFamily. It sets the proxy Service to thePreferDualStackIP family policy and leavesipFamiliesunset, so the same EnvoyProxy applies to both dual-stack and single-stack clusters.DualStackkeeps requiring both families. - Added cosign keyless signing of the
docker.io/envoyproxy/gatewayrelease image. Verify it withcosign verifyand the identity of the release workflow, as shown in the “Verify Image Signatures” install docs.
Observability
- Added support for tuning the Gzip, Brotli and Zstd compressor settings of the Prometheus metrics endpoint compression in
EnvoyProxy. - Added an optional
prefixfield to the OpenTelemetry metrics sink (EnvoyProxy.spec.telemetry.metrics.sinks[].openTelemetry.prefix) that is prepended to emitted stat names, so the full stat name becomes<prefix>.<stat_name>. - Added
enableCircuitBreakerRemainingStatstoEnvoyProxytelemetry metrics, enabling Envoy’s circuit breakertrack_remainingfield so that theremaining_*gauges report how much headroom is left before a circuit breaker opens. - Envoy Gateway’s own traces now carry the Envoy Gateway version in the
service.versionresource attribute and the git commit inenvoy_gateway.git_commit_id, so traces can be compared across versions. - Added extension hook call counts and durations as OTel span attributes on the
XdsTranslator.processHTTPListenerXdsTranslationspan (extension.hook.route_call_count,extension.hook.route_duration_ms,extension.hook.vhost_call_count,extension.hook.vhost_duration_ms) and theXdsTranslator.notifyExtensionServerAboutListenersspan (extension.hook.listener_call_count,extension.hook.listener_duration_ms), enabling latency and call-count visibility for extension server hooks in distributed traces. When multiple extension servers are registered, call counts reflect the sum of all servers actually invoked and durations cover the total time across all of them.
Status & Validation
- Added a
BackendPriorityIneffectivecondition to HTTPRoute and GRPCRoute status when a rule’s backends have different priorities, set by abackendPriorityHTTPRouteFilter or a fallback Backend, but are split into separate clusters, so priority failover between them has no effect.
🐞 Bug Fixes
- Fixed
minContentLengthbeing ignored for the Prometheus metrics endpoint compression inEnvoyProxy. - Fixed gateway-wide ClientTrafficPolicy port-overlap validation for Gateways whose names share the same prefix.
- Fixed OIDC flow-state cookies accumulating in the browser and overflowing the request header size limit. Envoy mints a nonce (CSRF) and a PKCE code verifier cookie for every authorization flow it starts, but only deletes the pair belonging to the flow that completes the callback, so flows that are abandoned - parallel requests from a logged out browser, a user navigating away from the provider’s login page - leave their cookies behind until they expire. Envoy Gateway now scopes both cookies to the OIDC redirect path, the only path where Envoy needs their value, so any orphans are no longer sent on every request. Note this bounds the damage rather than eliminating it: orphans are still sent to the callback endpoint itself until they expire, and logout can no longer purge them early because the browser no longer sends them to the signout path, so also consider lowering
csrfTokenTTL. The PKCE code verifier cookie is also now namedCodeVerifier-<suffix>, carrying the same per-policy suffix as the other OAuth2 cookies instead of Envoy’s shared default, so SecurityPolicies on the same cookie domain no longer delete each other’s in-flight flow cookies on logout. On upgrade, a browser already holding flow cookies keeps them at the oldpath=/, since the new deletion headers are scoped to the redirect path. They expire on the lifetime they were originally issued with - 10 minutes by default, and unaffected by anycsrfTokenTTLyou configure during the upgrade. The old code verifier cookie name is also no longer read, so a login that was in progress across the rollout may need to be retried once. - Fixed HTTPRoute (and other xRoute) acceptance not being re-evaluated when a namespace’s labels changed to newly match, or stop matching, a Gateway listener’s
allowedRoutes.namespaces.from: Selector, requiring a controller restart to pick up the change. Envoy Gateway now watches Namespace label updates and re-reconciles affected Gateways automatically. - Fixed the File and ALS access log sinks silently falling back to the default JSON fields when
telemetry.accessLog.settings[].formatsetstextwithouttype, which the API accepts. - Added CRD validation (
minimum: 200,maximum: 599) toBackendTrafficPolicy’sresponseOverride[].response.statusCodefield, rejecting out-of-range values such as-1at admission time instead of letting them reach the generated Envoy config. - Fixed HTTPRoutes attached to a listener that won a hostname conflict being rejected with
NoMatchingListenerHostnameand omitted from xDS by excluding conflict-losing listeners from route hostname filtering. - Fixed active health checks failing against backends that speak HTTP/2: health checks are now sent over the protocol of the backend, and health checks to a backend that negotiates its protocol through ALPN negotiate it the same way, so that every endpoint is checked over the protocol that requests to it use. Negotiating health checks require Envoy 1.40 or later.
- Fixed the controller crash-looping when an extension manager’s
backendResourcesreferences a CRD that is not installed in the cluster. The CRD existence is now checked once at controller startup and cached in a field on the reconciler, following the same pattern already used forServiceImport,Backend, and other optional CRDs. When the CRD is absent, both the watch and the reconcile list path are skipped; all other errors from the list call are returned so the reconcile is retried instead of publishing an incomplete resource snapshot. - The global rate limit cluster is now built from the in-cluster
envoy-ratelimitService/EndpointSlices using EDS instead of resolving a static DNS hostname, so requests keep hitting rate limit service replicas correctly as they scale up or down. When the Service or its endpoints can’t be discovered, Envoy Gateway falls back to the previous STRICT_DNS behavior. - Fixed a
BackendTrafficPolicysetting aConsistentHashload balancer having no effect when it targets aUDPRoute. The cluster was configured with Maglev, but no hash policy was set on the UDP proxy listener filter, so Envoy had no hash key to compute and fell back to picking an upstream host at random for every UDP session. The source IP hash policy is now configured on the UDP proxy. Only theSourceIPconsistent hash type applies to UDP, since headers, cookies and query parameters do not exist in a UDP datagram. - Removed the
--cpuset-threadsflag from the Envoy proxy startup arguments, which was causing Envoy to log a warning when the container’s CPU limits did not align with a CPU set. - Fixed controller panic when translating backend credential injection with an invalid header.
- Fixed the Wasm image permission cache key omitting the CA certificate, which allowed a permission check result to be reused across different TLS trust configurations.
- Fixed
rateLimitDeployment.pod.priorityClassNamebeing ignored when rendering the rate limit Deployment, so the configured PriorityClass is now applied to the rate limit pod the same way it is for the Envoy Proxy deployment. - Fixed
egctl x translateignoring thebinaryDataof ConfigMaps. - Fixed a Gateway, xRoute, policy, Backend or EnvoyProxy being left with no status when it is deleted and recreated with the same name and an unchanged spec within a single reconcile, which previously left the recreated object stuck at the default status its CRD defines until the controller was restarted.
- Fixed
responseOverrideinBackendTrafficPolicyclearing%RESPONSE_CODE_DETAILS%. The reason the request actually failed (for examplecsrf_origin_mismatch) is now preserved in access logs instead of being logged as an empty string. - Fixed a route-level
SecurityPolicywithmergeTypeset being rejected on admission when itsauthorizationrules reference a JWT provider defined by the parentSecurityPolicyit merges into (if authorization.rules.principal.jwt is used, jwt must be defined). The CRD validation ran against the policy before the merge, so the JWT providers had to be duplicated on every route. The reference is now resolved against the merged policy. - Fixed
EnvoyGateway.spec.envoyProxyaccepting an invalidEnvoyProxySpectemplate. This default/merge-base spec is now validated the same way a standaloneEnvoyProxyresource is, so misconfigurations such as an invalid provider are rejected at config-load time instead of surfacing later as a translation failure. - Fixed extension-declared backend resources (
extensionManager.backendResources) not triggering reconciliation when only their labels or annotations changed, requiring a spurious spec change or controller restart to pick up the update. The watch predicates for these resources (and for other extension-managed GVKs) now share a common generation/label/annotation-changed predicate, so annotation or label updates on an extension backend now regenerate xDS as expected. - Fixed local rate limiting for Distinct client selectors to retain up to 10,000 per-value token buckets per wildcard descriptor by applying the cache limit to each route’s filter configuration instead of relying on Envoy’s default of 20.
- Fixed HTTPRoute status collapsing when a route had two parentRefs to the same Gateway differing only by port (with no sectionName). IsParentRefEqual short-circuited before comparing Port, so both refs resolved to the same RouteParentStatus and one listener’s verdict overwrote the other’s — a route accepted on one listener could report Accepted: False borrowed from another. Listener selection and the data plane were unaffected; only the reported status was wrong.
- Fixed a duplicate filter chain matcher error when a TLS listener with no attached routes shares a port with an HTTPS listener, which caused Envoy to reject every subsequent xDS update to that listener.
- Fixed HTTPS and TLS listeners from different merged Gateways sharing a port and a hostname being accepted, which produced two Envoy filter chains with the same SNI match and caused Envoy to reject the listener.
- Fixed
mergeBackendsapplying a whole-GatewayBackendTrafficPolicy’s entire cluster-scoped feature set to merged UDP and TCP clusters regardless of the backend’s protocol. WithoutmergeBackendsthose settings can never reach such a cluster -ir.UDPRoute, for instance, only carries load balancing and DNS - so enabling cluster deduplication could puthealthCheck,circuitBreaker,tcpKeepalive,proxyProtocol,http2,useClientProtocolandHttpProtocolOptionson the clusterudp_proxyroutes to, most damagingly running TCP/HTTP health checks against a UDP-only port. A merged cluster now receives only what the same policy would produce for that protocol withoutmergeBackends. - Fixed the xDS translator emitting equivalent-but-byte-different resources on every reconcile: filter typed_config was marshaled non-deterministically, so proto map fields (such as an access log’s
json_format) re-ordered their keys each translation and caused repeated no-op xDS pushes. Typed configs are now marshaled deterministically. - Fixed
ClientTrafficPolicyrejecting BoringSSL equal-preference cipher groups such as[ECDHE-ECDSA-AES128-GCM-SHA256|ECDHE-ECDSA-CHACHA20-POLY1305]as an unsupported cipher suite. Each member of a group is now validated on its own. - Fixed MergeBackends being unnecessarily disabled for HTTP and gRPC rules using gateway-level ConsistentHash load balancing.
- Adding, editing or removing a Lua
EnvoyExtensionPolicyno longer drains the listener. The scripts travel with the routes, so a policy change is a route configuration update rather than a listener rebuild. The listener carries placeholder Lua filter slots allocated in blocks of 10, sized to the longest Lua chain on any of its routes. It is still rebuilt when a Gateway gets its first Lua policy or loses its last one, or when that longest chain crosses a multiple of 10, for example growing from 10 scripts to 11. - Fixed active gRPC health checks sending an invalid
:authorityheader. Envoy Gateway only set the service name on the gRPC health checker, so Envoy fell back to the name of the cluster, which is generated per route rule and contains slashes (grpcroute/my-ns/my-route/rule/0). Slashes are not valid in an authority, and gRPC servers built ongolang.org/x/netv0.59.0 or later reset the stream withPROTOCOL_ERROR, leaving every endpoint markedfailed_active_hcand clients receivingno healthy upstreamfrom a healthy backend. The authority now defaults to the effective route hostname, mirroring the HTTP health checker, andBackendTrafficPolicygainedhealthCheck.active.grpc.hostnameto set it explicitly. Upgrading alone does not fix routes with a wildcard hostname or no hostname: neither is a valid authority, so unless the backend endpoint defines ahostnamein aBackendresource, the authority still falls back to the cluster name, andhealthCheck.active.grpc.hostnamemust be set explicitly. - Fixed the shutdown manager exiting after the minimum drain period while UDP proxy sessions were still active. The drain now also waits for
udp.*.downstream_sess_activeto reach the exit threshold. UDP sessions only close on their idle timeout (60s by default), so a proxy that has handled UDP traffic recently may wait until the drain timeout before exiting. - Fixed the shutdown-manager failing to create /tmp/shutdown-ready (breaking graceful shutdown and delaying proxy pod termination by the full grace period) when the Envoy container’s securityContext sets readOnlyRootFilesystem: true, by mounting a writable emptyDir at /tmp on the shutdown-manager.
- Fixed a panic during shutdown caused by closing the shared
infraIR/pResourceschannels and resources maps before all runner goroutines still consuming or writing to them had exited. - Fixed obsolete resource snapshots being retained by the backing array of coalesced subscription updates.
- Fixed HTTPS listeners referencing non-existent RouteConfigurations when they share a container port with an HTTP listener that is translated first (for example HTTPS on port 443 and HTTP on port 10443). The HTTPS virtual hosts were added to the HTTP listener’s RouteConfiguration, leaving the HTTPS filter chains without routes.
- Fixed an HTTPRoute or GRPCRoute rule with
sessionPersistence.sessionName: ""producing an empty cookie or header name that Envoy rejects, which blocked the xDS update for the whole listener; such a rule is now rejected with reasonUnsupportedValue. - Fixed route status conditions being reported in a non-deterministic order when a route parent had errors of more than one condition type, which caused unnecessary route status updates.
- A BackendTrafficPolicy targeting a Service, ServiceImport, or Backend now reports
Accepted=Falsewith reasonNotMergedon each Gateway that references the backend but has no merged cluster for it, instead of silently doing nothing. - Fixed the listener-level
api_key_authfilter being built from the first route that uses API key auth. Rotating that route’s keys no longer forces a listener update that drains long-lived connections on every route of the listener, and a route whose credential Secret has no usable keys now rejects requests instead of accepting the first route’s keys. - Fixed Service and ServiceImport backends whose EndpointSlices carry both
kubernetes.io/service-nameandmulticluster.kubernetes.io/service-name(as Cilium ClusterMesh creates them): ServiceImport backends no longer reportEndpointsNotFound, and Service backends now pick up endpoint changes. - Fixed a BackendTrafficPolicy’s cluster-scoped settings (circuit breaker, load balancer, timeout, etc.) failing to apply to merged backend clusters whenever an unrelated route-scoped field (such as a broken
rateLimit) failed to translate. This affected both whole-Gateway BackendTrafficPolicies and backend-targeted BackendTrafficPolicy merging. - Fixed
healthCheck.passive.maxEjectionPercent: 0being dropped from the generated Envoy cluster; the explicitly configured 0% limit is now preserved instead of falling back to Envoy’s 10% default. - Fixed an HTTPRoute not being reconciled when an
HTTPRouteFilterthat it references only from backendRef filters is created or updated, if the rule has no rule-level filters. - Fixed the Kubernetes provider’s cache scope being random on each start when
watch.typeisNamespaces, which could leave the controller unable to become Ready or unable to see its own namespace’s Secrets and ConfigMaps in Gateway Namespace Mode; the Helm chart now also grants EndpointSlice read in the controller namespace for this mode. - Fixed a BackendTrafficPolicy with mergeType being recorded twice in a route’s policy metadata when two of the route’s parentRefs reach the same listener.
- Fixed a route-level
SecurityPolicy,BackendTrafficPolicyorEnvoyExtensionPolicywithmergeTypeset reporting its status on two ancestors for the same parent when the route’s parentRef has nosectionName. The merge results (Merged, orAccepted: Falsewhen the merged policy failed to translate) went on one ancestor per listener, whileAccepted: Truewent on the Gateway or ListenerSet itself, so a policy whose merge failed reported bothAccepted: FalseandAccepted: True. The merge results of all listeners are now reported on the parentRef’s ancestor, the same one used withoutmergeType. - Fixed route status.parents accumulating duplicate entries until the 32-entry limit when another controller writes status for the same parentRef, by matching parent status entries on both parentRef and controllerName.
- Fixed the Helm chart not granting Secret and EndpointSlice read in the controller namespace when
watch.namespacesis set in the default deploy mode, which kept the controller from becoming Ready unless the controller namespace was listed inwatch.namespaces. - Fixed the default Envoy proxy version reported by
envoy-gateway versionmissing the image digest. - Fixed GatewayNamespace mode surfacing a misleading “illegal change in a custom label” error when a pre-existing, unmanaged Deployment or DaemonSet shares the Gateway’s name; it now reports a clear ownership-conflict error, detected via an uncached API read so label-filtered resource kinds (e.g. DaemonSet) are caught too.
- Fixed SecurityPolicy OIDC returning HTTP 500 on every protected route when the issuer’s
/.well-known/openid-configurationcould not be fetched during a reconciliation, by retaining the last successfully discovered configuration per issuer across translations and falling back to it when discovery fails. - Fixed a stale
status.parentsentry lingering on a Route after one of itsspec.parentRefswas removed, while still keeping status entries for parents not touched in a given reconciliation batch. - Fixed
backendTrafficPolicy.spec.compressor[].minContentLength(and the deprecatedspec.compression[]) silently wrapping when set above 4 GiB: the quantity was cast straight to a uint32, so a value such as4Gitruncated to0and Envoy compressed every response instead of only large ones. Such out-of-range values are now rejected with a policy error rather than truncated. - Fixed route translation panicking when a backendRef to an extension backend resource whose kind collides with a built-in one (for example
custom.example.io/Service) was dispatched to the built-in Service resolver. Extension backend resources are now always handed to the extension, and the Service/ServiceImport resolvers reportBackendNotFoundfor a missing object. - Fixed
extensionReffilters being dispatched by kind only, so an extension resource with kindHTTPRouteFilterin a different group was resolved as the Envoy Gateway HTTPRouteFilter of the same name instead of being passed to the extension server. Both the group and the kind are now checked when loading, indexing and translating HTTPRouteFilter references. - Fixed requests on existing connections being refused with 403
Wrong requirement_nameafter a JWT SecurityPolicy edit. Since v1.9.0 the jwt_authn requirement was named after a hash of its content, so an edit of the JWT config (an audience, the JWKS,claimToHeaders,optional) renamed it in both the listener’srequirement_mapand every route’srequirement_name, and Envoy applies the two as separate updates. The requirement is now named after the SecurityPolicy that applies to the route, so such an edit changes only the listener’s requirement content. Connections that are still draining verify against the previous config until they close, as they did before v1.9.0. - An invalid telemetry setting (
clusterStatName, an access log format or sink, tracing, a metrics sink or Prometheus compression) or an invalidfilterOrderno longer rejects the whole EnvoyProxy. The invalid field is ignored, the rest of the EnvoyProxy is applied, and the EnvoyProxy status reports aWarningcondition with reasonInvalidField(orWarningswhen the EnvoyProxy is also warned about for another reason, such as a deprecated field). Previously the Gateway was not accepted and its proxy infrastructure was reset to defaults, which could recreate the Envoy Deployment under a different name and drop all traffic. Invalid provider, bootstrap, ormergeGateways/mergeBackendssettings still reject the EnvoyProxy. - Fixed status updates being dropped when the API server returned a timeout, throttling or another transient error. The status writer now retries these errors with backoff, in addition to conflicts.
🚀 Performance Improvements
- Improved reconcile performance by listing the extension manager’s
resourcesandbackendResourcesonce per GatewayClass instead of once per Gateway. - Added an opt-in debounce for the provider reconcile, configurable via the top-level
debouncefield in the EnvoyGateway config. Every watch enqueues the same GatewayClass request, so the workqueue already collapses events that arrive while a reconcile is in flight, but it has no quiet period: when reconciles are fast relative to the event rate, each event costs a full rebuild of the resource tree. Setting the field turns debouncing on: enqueue requests are held until no new request has arrived fordebounce.after(default 100ms), or untildebounce.max(default 10s) if requests keep arriving, so a burst of resource changes costs a single reconcile. Because status is computed during reconcile, setting this also delays status updates by up todebounce.max. Debouncing is off when the field is unset. - Reduced control-plane memory on gateways where many backends validate against the same CA by storing each upstream CA bundle once per gateway and referencing it by a digest of its content, instead of carrying a copy on every route destination.
- Reduced xDS translation work for listeners with many routes by computing GeoIP header removals once per listener.
- Reduced repeated JSON decoding and encoding when JSONPath patches match multiple locations.
- Reduced snapshot-copy allocations when HTTP routes share destination settings across matches or listeners, while preserving isolation between snapshots.
- Reduced the cost of checking cross-namespace references during reconciliation by looking up only the ReferenceGrants in the target namespace that allow references from the source namespace to the target kind, instead of listing every ReferenceGrant for the target kind across the cluster.
- Reduced translation time for gateways with many BackendTLSPolicies by indexing the policies once per translation by the backend (and section) they target, instead of scanning every policy for each route backendRef.
- Reduced translation time for routes that use extensionRef filters or custom backends by indexing extension resources once per translation, instead of scanning every extension resource for each filter and backendRef.
- Improved xDS translation performance for large configurations by indexing cluster names during resource construction.
- Policy CRDs are no longer deep-copied from the informer cache on every reconcile.
- Reduced BackendTrafficPolicy translation work for large HTTPRoute and GRPCRoute target sets by indexing repeated route lookups within each translation.
- Reduced BackendTrafficPolicy translation work by reusing consecutive child/parent policy merges within one translation pass.
🛑 Deprecations
- The
ClientTrafficPolicyhttp1fieldsenableTrailers,preserveHeaderCase, andhttp10currently configure both client and backend HTTP/1 protocol settings. In a future release these fields will be restricted to client connections only. TheApplyClientTrafficPolicyHTTP1SettingsToClientsOnlyruntime flag can be used to opt in to the new behavior today. Migration guidance per field: -enableTrailers: useBackendTrafficPolicy.http1.enableTrailersfor backend settings. -preserveHeaderCase: useBackendTrafficPolicy.http1.preserveHeaderCasefor backend settings. -http10: this field currently forces explicit HTTP/1 on backend connections whenenableTrailersandpreserveHeaderCaseare absent. In a future release it will no longer affect backend connections. UseBackendTrafficPolicy.http1: {}to force explicit HTTP/1 on a backend cluster. Note:accept_http_10anddefault_host_for_http_10are downstream-only fields in Envoy and have never had any effect on upstream clusters. - The following deprecated fields are now scheduled for removal in v1.11, which is stated in the API reference and in the
DeprecatedFieldwarning condition set on the resource’s status; migrate to their replacements before upgrading:targetRefon all policies (usetargetRefs/targetSelectors),backendRefon backend clusters such as ext auth, ext proc and OIDC (usebackendRefs),host/porton EnvoyProxy tracing, OpenTelemetry metrics and OpenTelemetry access log sinks (usebackendRefs),host/porton the EnvoyGateway extension manager service (usefqdn/ip/unix), ClientTrafficPolicyenableProxyProtocol(useproxyProtocol) andheaders.preserveXRequestID(useheaders.requestID: PreserveOrGenerate), BackendTrafficPolicycompression(usecompressor), and the consistent hashheaderfield andHeadertype (useheadersandHeaders). Deprecation warnings are now also reported forbackendRefon ext proc, JWT remote JWKS and OIDC providers, forhost/backendRefon EnvoyProxy telemetry sinks, for the consistent hashheaderfield andHeadertype, and, in the Envoy Gateway startup log, forhost/porton extension manager and remote infrastructure provider services.
📝 Other Changes
- Added per-phase tracing spans to the Gateway API and xDS translators, each recording the size of the input it processed, so that a slow translation can be attributed to a specific phase — listener processing, HTTP and gRPC route processing, the main policy types, EnvoyPatchPolicy JSON patches, extension server hooks, and xDS resource validation — instead of showing up as one opaque multi-second span.
📝 Upgrade Notes
- We encourage all users to upgrade to v1.10.0 to take advantage of the new features, security improvements, and performance gains. For full details, see the Release Notes and updated Documentation.
- Envoy Gateway v1.10 ships with Envoy Proxy v1.40 and keeps the bundled Gateway API CRDs at v1.6.1.
- Review the Breaking Changes above before upgrading, especially if you use EnvoyPatchPolicy or an extension server that matches generated xDS names.
- For upgrade instructions, see Helm or YAML.
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.