1.41.0 (Pending)

Incompatible behavior changes

Changes that are expected to cause an incompatibility if applicable; deployment changes are likely required

  • dynamic_forward_proxy: The dynamic forward proxy cluster now replaces a host with a new host instance when DNS re-resolution changes its address, publishing the change as a regular cluster membership update (old host removed, new host added). Connection pools bound to the old address are therefore drained, instead of existing connections continuing to use the stale address indefinitely. This change can be reverted by setting the runtime guard envoy.reloadable_features.dfp_cluster_replace_host_on_address_change to false, which restores the previous behavior of mutating the existing host’s address in place.

  • geoip: The HTTP geoip filter now removes every configured geolocation header from the incoming request before the lookup, so a header a client sent is never forwarded upstream as if the lookup had set it. Previously a client-supplied header was kept whenever the lookup had no value for it: an address absent from the database, a database error, a field the database lacks for that address, or a request whose lookup was skipped because the downstream address was not an IP address. Geolocation providers report the headers they are configured to write through the new Driver::geoHeaderNames() method, which defaults to empty for providers that do not implement it. This behavior can be reverted by setting the runtime guard envoy.reloadable_features.geoip_sanitize_geo_headers to false.

  • stats: The tag-extracted names of per-worker stats now keep worker as a separate name segment when the worker_id tag is extracted: server.worker_1.watchdog_miss is now extracted to server.worker.watchdog_miss instead of server.worker_watchdog_miss, and the same applies to the per-worker stats under listener.<address>., listener_manager. and thread_local_cluster_manager.. Prometheus names are unchanged, because . and _ are both sanitized to _ there. Metric names change only for sinks that emit the tag-extracted name verbatim, such as dog_statsd, metrics_service and the OpenTelemetry sink.

Minor behavior changes

Changes that may cause incompatibilities for some users, but should not for most

  • config: xDS resource types that may be subscribed to while a config update is being applied are now paused centrally for every subscription, regardless of its transport (gRPC, REST or filesystem), instead of by each xDS API. As part of this change, Virtual Host Discovery Service (VHDS) requests are now also paused while a Route Discovery Service (RDS) update is being applied, so that the VHDS subscriptions created by the update are batched into a single request.

  • contrib: The Istio peer metadata conversion now takes a peer’s app name and version from the app and version labels again, and uses app.kubernetes.io/name and app.kubernetes.io/version only when those labels are absent or empty. A workload’s own stats always use app and version, so giving the app.kubernetes.io/* labels precedence made source_app, destination_app, source_version and destination_version reported by a peer differ from the values the workload reports for itself, and the same workload showed up under two names. This restores the label precedence Istio proxies used before app.kubernetes.io/* labels were preferred.

  • mcp: Removed the 10MB (10485760 bytes) upper bound on the MCP filter’s max_request_body_size and the per-route max_request_body_size. Any uint32 value is now accepted; the 8KB default is unchanged.

  • on_demand: On-demand cluster discovery (ODCDS) can now answer repeated requests for a cluster the server has already reported as missing immediately from that remembered answer, instead of starting another discovery and waiting for the response or the discovery timeout. The answer is remembered per config source, only for explicitly requested clusters, and is withdrawn as soon as an update delivers the cluster. Requests answered this way are counted in the new cluster_manager.odcds.known_missing_answers counter. This behavior is off by default and can be enabled by setting the runtime guard envoy.reloadable_features.odcds_missing_cluster_cache to true.

  • stats: Added the new envoy.upstream_from_zone and envoy.upstream_to_zone tags, extracted by the default stats tag-extraction rules from a cluster’s upstream per-zone stats (cluster.<name>.zone.<from_zone>.<to_zone>.*). Flat stat names are unchanged.

Bug fixes

Changes expected to improve the state of the world and are unlikely to have negative effects

  • dynamic_modules: Fixed dynamic module metric IDs. The first metric defined with labels got the same ID as the first one defined without labels, and metrics of different types also shared IDs. A module that recorded a metric with the wrong number of label values, or through the call of another metric type, then changed a different metric instead of getting an error. Every metric now has its own ID.

  • mcp_json_rest_bridge: Fixed MCP header Base64 decoding so values with an incomplete Base64 wrapper are treated as literals, and an empty Base64 payload correctly decodes to an empty string.

  • mcp_json_rest_bridge: Fixed the mcp_json_rest_bridge HTTP filter per-route configuration validation to reject duplicate (host, path) endpoint configurations instead of silently merging tools and ignoring subsequent tool_list_http_rule or tool_list_local settings.

  • mcp_json_rest_bridge: Fixed the mcp_json_rest_bridge HTTP filter to respond with HTTP 404 Not Found instead of 200 OK when a stateless MCP 2026-07-28 request uses a method that the bridge does not implement, as required by the Streamable HTTP transport. The JSON-RPC error code is unchanged (-32601). This also applies to the legacy initialize and notifications/initialized methods sent as stateless requests. Requests that use MCP 2025-11-25 or earlier still receive 200 OK.

  • overload_manager: Fixed a use-after-free that could crash a worker thread during Envoy shutdown when a TCP listener is configured with exact_balance and the envoy.resource_monitors.global_downstream_max_connections overload manager resource monitor is configured. A connection accepted on one worker could be handed off to a different worker via connection balancing while still holding a reference into the originating worker’s thread-local overload state; because worker threads shut down independently with no ordering guarantee relative to one another, the destination worker could destroy the connection after the originating worker had already torn down its own thread-local state, dereferencing freed memory. The connection’s overload state reference is now rebound to the receiving worker’s own thread-local state once the connection has settled on its final worker, so it is always valid for as long as the connection itself is.

  • udp: Fixed a hot restart child reading packets from an inherited UDP socket while its listener was still paused. The QUIC listener injects a read event on its listener to process the handshakes it buffered from packets the parent forwarded, and the listener then read the socket the parent was still serving, so the packets of the parent’s connections were dequeued by the child and answered with stateless resets until the clients had reconnected. A paused listener now runs its read-ready callback on such an event without reading the socket.

  • upstream: Fixed a crash (SIGSEGV) in load stats reporting and health discovery responses when EDS removed the last host from a locality after active health checking failed. Both reporting paths now skip empty locality groups.

Removed config or runtime

Normally occurs at the end of the deprecation period

  • config: Removed runtime flag envoy.reloadable_features.xds_legacy_delta_skip_subsequent_node and legacy code paths.

  • http: Removed runtime flag envoy.reloadable_features.http1_close_connection_on_zombie_stream_complete and legacy code paths.

  • mcp: Removed the runtime guard envoy.reloadable_features.mcp_filter_use_new_metadata_namespace and the legacy code path it guarded. The MCP filter always publishes its dynamic metadata under the envoy.filters.http.mcp namespace (and the MCP router reads it from there); the legacy mcp_proxy namespace is no longer available.

New features

  • access_log: Added the %DOWNSTREAM_PEER_CERT_CHAIN% substitution formatter which returns the full downstream peer certificate chain in URL-encoded PEM format.

  • ai_protocol_manager: Added external_buffer_config to the AI Protocol Manager filter (alpha), making the store that offloaded payloads are written to a pluggable envoy.ai_protocol_manager.external_buffer extension. When unset, envoy.ai_protocol_manager.external_buffer.in_memory is used; it holds the payload in memory and does not bound its size.

  • config: YAML configuration now honors the explicit !!float tag: a scalar such as !!float 5000.0 is loaded as a JSON number instead of a string, so it stays a number inside a google.protobuf.Struct (e.g. a typed_config packed as a Struct). Untagged float literals are unchanged and still load as strings. Non-finite values are loaded as the strings Infinity, -Infinity and NaN. A !!float scalar that cannot be decoded as a double now fails configuration loading. See special YAML usage.

  • ext_proc: Added max_streamed_body_chunk_size and per-route max_streamed_body_chunk_size to limit the body payload size of individual ProcessingRequest messages sent in STREAMED and FULL_DUPLEX_STREAMED body processing modes.

  • http2: Added the metadata_dropped HTTP/2 codec stat. It counts outbound metadata that is dropped because allow_metadata isn’t enabled on the connection.

  • matching: Added the URI template input matcher, which matches a path against a URI template with the same semantics as route path_match_policy.

  • mcp_json_rest_bridge: Added validation of Mcp-Param-{name} request headers for MCP 2026-07-28 tools/call requests, configured per tool with the new mcp_param_headers field. Each mapping names the header, the literal property path of the argument, and its STRING, INTEGER or BOOLEAN type. A missing, unexpected, malformed, or mismatched header is rejected with a HeaderMismatch error.

  • overload_manager: Added the new HTTP priority load shed filter envoy.filters.http.priority_load_shed, which maps request header integer ranges to overload manager load shed points. It supports fallback shedding via a default load shed point.

  • upstream: Added cluster_manager.odcds.discovery_attempt, cluster_manager.odcds.discovery_success, cluster_manager.odcds.discovery_missing, and cluster_manager.odcds.discovery_timeout counters for on-demand cluster discovery. Each counter increments once per discovery, regardless of how many requests were waiting for that cluster.

  • watchdog: Added log_level to the envoy.watchdog.backtrace_action watchdog action to make the level of the emitted backtrace logs configurable. The level defaults to critical.