The Envoy HTTP filter chain
Envoy processes every HTTP request through an ordered chain of filters in its HTTP Connection Manager. Filters run in two directions — decode on the way in, encode on the way out — and the chain always ends in the terminal router filter. This reference lists the phases and a catalogue of the most common built-in filters.
How it works
A request flows down the chain through the decode phases, hits the router which forwards it upstream, and the response flows back up through the encode phases in reverse order:
decodeHeaders -> decodeData -> decodeTrailers -> [router] upstream
encodeHeaders <- encodeData <- encodeTrailers <- upstream response
Each filter implements callbacks for these phases and returns a status
(Continue, StopIteration, StopAllIterationAndBuffer) to control flow. Many
filters touch only headers; others buffer the body. Because encode runs in
reverse, ordering matters: place authentication before transcoding, and rate
limiting before the router.
Common built-in HTTP filters
| Filter name | Purpose |
|---|---|
envoy.filters.http.router | Terminal filter — selects cluster and forwards |
envoy.filters.http.jwt_authn | Validates JWT tokens locally against configured JWKS |
envoy.filters.http.ext_authz | Delegates authz decision to an external gRPC/HTTP service |
envoy.filters.http.rbac | Role-based access control from connection/request attributes |
envoy.filters.http.ratelimit | Global rate limiting via external RLS service |
envoy.filters.http.local_ratelimit | Per-instance token-bucket rate limiting, no external dep |
envoy.filters.http.cors | Adds CORS preflight handling and headers |
envoy.filters.http.grpc_json_transcoder | Transcodes REST to gRPC and back |
envoy.filters.http.compressor | Gzip/Brotli response compression |
envoy.filters.http.wasm | Embeds a WebAssembly extension |
envoy.filters.http.fault | Injects delays and abort errors for chaos testing |
envoy.filters.http.lua | Runs Lua scripts for lightweight request/response mutation |
Recommended ordering pattern
The order in which you place filters is consequential. A practical ordering for a secure service mesh is:
- Authentication first (
jwt_authn, thenext_authz) — reject unauthenticated requests as early as possible, before buffering the body. - RBAC — once the identity is verified, enforce policy.
- Rate limiting (
local_ratelimitorratelimit) — shed excess traffic after auth so rate-limit state is not consumed by unauthenticated requests. - Transformation (
grpc_json_transcoder,lua,wasm) — mutate the request after it has been allowed through. - Observability (tracing, header modifications) — add headers that downstream services read.
router— always last.
Filter flow control
Each decode callback returns one of three statuses:
Continue— hand control to the next filter in the chain immediately.StopIteration— pause the chain; useful for async checks (for example, an ext_authz call waiting for a gRPC response). The filter resumes the chain when the check completes.StopAllIterationAndBuffer— stop and accumulate the request body; used when a filter needs the complete body before deciding (for example, a body-signing verification filter).
Understanding these return values matters when writing custom C++ or Wasm filter extensions, and also when debugging why a filter appears to “hang” a request — it usually means a StopIteration is never resumed.
Tips
- The router filter (
envoy.filters.http.router) is terminal and must be last in the chain. - Order auth filters (
jwt_authn,ext_authz,rbac) early so unauthorised requests stop quickly. local_ratelimitis per-instance and dependency-free;ratelimitis global via an external RLS.- Transform filters like
grpc_json_transcoderandwasmsit between auth and the router.