The Envoy External Processing (ext_proc) filter routes HTTP request and response processing to an external gRPC service. Instead of writing a Wasm plug-in or embedding logic inside Envoy, you run a standalone gRPC server that inspects and modifies headers, bodies, and trailers as traffic flows through the mesh.
ext_proc is a good fit when you need to:
Add, remove, or rewrite HTTP headers based on business logic
Implement custom authentication or authorization checks outside the sidecar
Enrich requests with data from external systems before they reach the upstream service
Use a language or framework that Wasm does not support
How it works
The ext_proc filter intercepts traffic at the sidecar and forwards it to your external gRPC service for processing. The filter supports six independent processing steps, each controlled by a processing mode:
| Step | Direction | Description |
|---|---|---|
| Request headers | Inbound | Inspect or modify headers before the request reaches the upstream |
| Request body | Inbound | Inspect or modify the request body |
| Request trailers | Inbound | Inspect or modify HTTP/2 request trailers |
| Response headers | Outbound | Inspect or modify headers before the response reaches the downstream |
| Response body | Outbound | Inspect or modify the response body |
| Response trailers | Outbound | Inspect or modify HTTP/2 response trailers |
By default, none of these steps are sent to the external processor. You explicitly enable each step by setting its processing mode to SEND in the EnvoyFilter configuration.
The following diagram illustrates the full request-response flow:
A downstream service sends a request. Envoy intercepts it and forwards the request to the external processing service.
The external processing service inspects or modifies the request, then returns the result to Envoy.
Envoy applies the modifications and forwards the request to the upstream service.
The upstream service returns a response. Envoy intercepts it and forwards the response to the external processing service.
The external processing service inspects or modifies the response, then returns the result to Envoy.
Envoy applies the modifications and forwards the response to the downstream service.
Prerequisites
Before you begin, make sure that you have:
The HTTPBin application deployed and accessible, with a sleep application deployed as described in Related operations
Step 1: Write the external processing service
Build a gRPC server that implements the Envoy ExternalProcessor service interface. The server receives a stream of ProcessingRequest messages from Envoy and must return a matching ProcessingResponse for each.
The following Go snippet shows the core logic. For the complete runnable project, see ext-proc-demo on GitHub. For the full API specification, see the Envoy ext_proc proto reference.
This example handles two processing steps:
Request headers: Adds
x-ext-proc-header: hello-to-asmto every inbound request.Response headers: Adds
x-ext-proc-header: hello-from-asmto every outbound response.
Package your gRPC server as a container image using a Dockerfile and push it to a container registry before deployment.
Step 2: Deploy the external processing service
This step uses the sample ext_proc image provided by ASM. The sample service adds the header x-ext-proc-header: hello-to-asm to requests and x-ext-proc-header: hello-from-asm to responses.
Create a file named
ext.yamlwith the following content:apiVersion: v1 kind: Service metadata: name: ext-proc labels: app: ext-proc service: ext-proc spec: ports: # gRPC listener port for the external processing service - name: grpc port: 9002 targetPort: 9002 selector: app: ext-proc --- apiVersion: apps/v1 kind: Deployment metadata: name: ext-proc spec: replicas: 1 selector: matchLabels: app: ext-proc version: v1 template: metadata: labels: app: ext-proc version: v1 spec: containers: - image: registry-cn-hangzhou.ack.aliyuncs.com/ack-demo/ext-proc:v0.2 imagePullPolicy: IfNotPresent name: ext-proc ports: - containerPort: 9002Apply the manifest and verify that the service is running:
kubectl apply -f ext.yamlCheck the pod logs to confirm that the gRPC server started: Expected output: This log confirms that the external processing service is running and listening on port 9002.
kubectl logs deploy/ext-procI1126 06:41:25.467033 1 main.go:52] Starting gRPC server on port :9002
Step 3: Configure the EnvoyFilter
Create an EnvoyFilter to insert the ext_proc filter into the sidecar's HTTP filter chain. This routes request and response headers from the HTTPBin sidecar to your external processing service.
-
Log on to the ASM console. In the left-side navigation pane, choose .
-
On the Mesh Management page, click the name of the ASM instance. In the left-side navigation pane, choose .
Create an EnvoyFilter named
httpbin-ext-procwith the following content:Key fields:
Field Description context: SIDECAR_INBOUNDApplies the filter to inbound traffic on the sidecar proxy portNumber: 80Targets the HTTP listener on port 80 operation: INSERT_BEFOREInserts the ext_proc filter before the router filter in the chain cluster_nameThe Envoy cluster endpoint for the ext-proc service, in `outbound ` format request_header_mode: SENDSends request headers to the external processor. The default is SKIP, which means headers are not sentresponse_header_mode: SENDSends response headers to the external processor. The default is SKIP, which means headers are not sentapiVersion: networking.istio.io/v1alpha3 kind: EnvoyFilter spec: configPatches: - applyTo: HTTP_FILTER match: context: SIDECAR_INBOUND listener: portNumber: 80 filterChain: filter: name: envoy.filters.network.http_connection_manager proxy: proxyVersion: ^MIN_VERSION-MAX_VERSION.* patch: operation: INSERT_BEFORE value: name: envoy.filters.http.ext_proc typed_config: '@type': >- type.googleapis.com/envoy.extensions.filters.http.ext_proc.v3.ExternalProcessor grpc_service: envoy_grpc: cluster_name: outbound|9002||ext-proc.default.svc.cluster.local authority: ext-proc.default.svc.cluster.local processing_mode: request_header_mode: SEND response_header_mode: SEND
Verify the setup
Send a request through the mesh and confirm that the external processor added the expected headers.
From the sleep pod, run:
kubectl exec -it deploy/sleep -- curl httpbin:8000/headers -iLook for these two headers in the output:
Response header
x-ext-proc-header: hello-from-asm-- added by the external processor before the response reached the downstreamRequest header
X-Ext-Proc-Header: hello-to-asm(visible in the JSON body) -- added by the external processor before the request reached the upstream
Expected output:
HTTP/1.1 200 OK
server: envoy
date: Wed, 11 Dec 2024 06:47:59 GMT
content-type: application/json
content-length: 564
access-control-allow-origin: *
access-control-allow-credentials: true
x-envoy-upstream-service-time: 3
x-ext-proc-header: hello-from-asm
{
"headers": {
"Accept": "*/*",
"Host": "httpbin:8000",
"User-Agent": "curl/8.1.2",
"X-B3-Parentspanid": "5c6dd2cc9312d6bb",
"X-B3-Sampled": "1",
"X-B3-Spanid": "1153a2737cee4434",
"X-B3-Traceid": "baba86b696edc75a5c6dd2cc9312d6bb",
"X-Envoy-Attempt-Count": "1",
"X-Ext-Proc-Header": "hello-to-asm",
"X-Forwarded-Client-Cert": "By=spiffe://cluster.local/ns/default/sa/httpbin;Hash=69d8f267c3c00b4396a83e12d14520acc9dadb1492d660e10f77e94dcad7cb06;Subject=\"\";URI=spiffe://cluster.local/ns/default/sa/sleep"
}
}Both headers are present, confirming that the external processing service correctly intercepts and modifies traffic through the Envoy sidecar.