This episode deepens Envoy's filter chains: HTTP filters like ext_authz and the gRPC JSON transcoder, TCP filters like the tcp_proxy passthrough, and how to order filters and do matching correctly.

In episodes 4 and 7, you already installed the HTTP router and http_connection_manager filters. Episode 8 shows that was just the surface: advanced filter chains are Envoy's ability to assemble HTTP and TCP filter pipelines that change how requests are processed at every stage.
You'll meet ext_authz for external authentication, the gRPC JSON transcoder that converts REST to gRPC, and tcp_proxy for forwarding raw TCP traffic. More importantly, you'll understand filter ordering — because order determines behavior.
All HTTP filters process requests inside a pipeline that ends with router:
http_filters:
- name: envoy.filters.http.cors
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.cors.v3.Cors
- name: envoy.filters.http.ext_authz
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz
transport_api_version: V3
grpc_service:
envoy_grpc:
cluster_name: auth_service
with_request_body:
max_request_bytes: 8192
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.RouterThe http_filters pipeline processes requests in sequence: CORS adds headers, ext_authz asks an authentication service, and router finally forwards the request to a cluster. Filters that modify the request must be placed before filters that consume it.
ext_authz delegates authorization decisions to an external service over gRPC. Envoy sends request metadata, and the service answers allow or deny. We'll deepen the configuration details in episode 14, but the essence is: this filter doesn't decide on its own — it asks another service.
grpc_json_transcoder converts REST JSON requests into gRPC calls — and vice versa. This lets REST clients use a gRPC backend without any changes:
http_filters:
- name: envoy.filters.http.grpc_json_transcoder
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.grpc_json_transcoder.v3.GrpcJsonTranscoder
proto_descriptor: /etc/envoy/api_descriptor.pb
services:
- orders.v1.OrdersService
print_options:
always_print_primitive_fields: true
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.RouterThe grpc_json_transcoder configuration uses a protobuf descriptor to map REST endpoints to gRPC methods. With print_options set, the JSON response always includes fields with zero values.
Not all traffic is HTTP. For databases, Redis, or binary protocols, use a TCP filter:
listeners:
- name: listener_tcp
address:
socket_address:
address: 0.0.0.0
port_value: 13000
filter_chains:
- filters:
- name: envoy.filters.network.tcp_proxy
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy
stat_prefix: mysql_tcp
cluster: mysql_clusterThe tcp_proxy filter forwards raw TCP connections to a cluster without parsing the protocol. This is called passthrough: incoming bytes are forwarded as-is, suitable for protocols Envoy doesn't understand. Use HTTP filters when you need path-based routing, and TCP filters when the traffic is a binary protocol.
A single listener can have several filter chains selected by connection characteristics:
listeners:
- name: listener_multi
address:
socket_address:
address: 0.0.0.0
port_value: 10000
filter_chains:
- filter_chain_match:
server_names:
- api.example.com
filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
stat_prefix: api_http
route_config:
name: api_routes
virtual_hosts: []
http_filters:
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
- filter_chain_match:
server_names:
- db.internal
filters:
- name: envoy.filters.network.tcp_proxy
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy
stat_prefix: db_tcp
cluster: db_clusterThe filter_chain_match mechanism selects a chain based on fields like server_names (TLS SNI). Connections to api.example.com are processed as HTTP, while connections to db.internal are forwarded as raw TCP.
Here are some filter ordering principles to hold onto:
router is always last for HTTP.curl -s localhost:9901/config_dump | grep -o '"name": "envoy.filters[^"]*"' | sort -uThe curl localhost:9901/config_dump command lists all loaded filters. The envoy.filters grep above filters out the unique filter names — a quick way to verify the active pipeline.
To close this episode, here's a pipeline combining CORS, authentication, and routing:
http_filters:
- name: envoy.filters.http.cors
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.cors.v3.Cors
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.RouterNote: in this example CORS is placed before router, so the CORS headers are correct before the response is sent. Filter ordering will be discussed even more deeply when we add rate limiting in episode 10 and RBAC in episode 12.
Episode 8 opened Envoy's filter toolbox: an HTTP pipeline with ext_authz and the gRPC JSON transcoder, TCP passthrough with tcp_proxy, and the matching and ordering that determine overall behavior.
Key takeaways:
http_filters is a pipeline; router always takes the last position.ext_authz delegates authentication to an external gRPC service.grpc_json_transcoder converts REST into gRPC calls.tcp_proxy forwards raw bytes without parsing the protocol.filter_chain_match selects chains based on SNI and connection characteristics.In the next episode, episode 9, we'll discuss dynamic configuration with xDS — the LDS, RDS, CDS, EDS, and SDS principles, Envoy as a flexible xDS client, and basic integration with control planes like Gloo, Contour, Istio, or a custom xDS server.