envoyproxy/envoy
HTTP filters
HTTP filters are the L7 plugins that run inside the HTTP connection manager. At the time of writing, source/extensions/filters/http/ ships 71 filters in core and a handful more in contrib/. This page is the family overview; for a particular filter's configuration, see envoyproxy.io.
What an HTTP filter does
A filter implements Http::StreamFilter — a base class with decoder and encoder halves. For each request stream it can:
- Inspect or rewrite request headers, body, trailers, metadata.
- Inspect or rewrite response headers, body, trailers, metadata.
- Stop iteration and resume after an async event (callout, JWT verification, file read).
- Generate a local reply (short-circuit) instead of forwarding.
- Read or write
StreamInfoand per-route configuration.
Decoder filters run in declared order; encoder filters run in reverse declared order. The terminal decoder filter is almost always the router.
Categories
The 71 in-tree filters break down roughly into:
| Category | Examples | Path |
|---|---|---|
| Terminal | router, tcp_proxy (network) |
router/ |
| Authn / Authz | jwt_authn, ext_authz, oauth2, basic_auth, api_key_auth, rbac, mcp |
jwt_authn/ |
| Rate limiting | ratelimit, local_ratelimit, rate_limit_quota, admission_control |
ratelimit/ |
| External processing | ext_proc, ext_authz, gcp_authn, aws_lambda, aws_request_signing |
ext_proc/ |
| Buffering / fault injection | buffer, fault, bandwidth_limit, bandwidth_share, kill_request, file_system_buffer |
buffer/ |
| Compression | compressor, decompressor |
compressor/ |
| Caching | cache, cache_v2 |
cache_v2/ |
| Headers / metadata | header_mutation, header_to_metadata, set_metadata, set_filter_state, cors, csrf, ip_tagging, cdn_loop, geoip, credential_injector |
many |
| Routing helpers | on_demand, dynamic_forward_proxy, original_src |
many |
| Protocol bridges | grpc_http1_bridge, grpc_http1_reverse_bridge, grpc_web, grpc_json_transcoder, grpc_json_reverse_transcoder, connect_grpc_bridge |
grpc_*/ |
| Stats / extraction | grpc_stats, grpc_field_extraction, proto_message_extraction, proto_api_scrubber, tap, json_to_metadata, sse_to_metadata, thrift_to_metadata |
many |
| Stateful sessions | stateful_session, composite, match_delegate |
many |
| Custom responses / overrides | custom_response, health_check, direct_response, transform |
many |
| AI plumbing | mcp, mcp_json_rest_bridge, mcp_router, a2a |
new in 2025 |
| Scriptable | lua, wasm, dynamic_modules |
lua/, wasm/, dynamic_modules/ |
There are also dual-mode filters — ext_authz, rbac, local_ratelimit, ratelimit, wasm, dynamic_modules, set_filter_state, match_delegate, geoip — that have both an HTTP and a network variant.
Anatomy of a filter
A filter directory typically contains:
source/extensions/filters/http/<name>/
├── BUILD # Bazel target
├── <name>_filter.h # The Filter class
├── <name>_filter.cc
├── config.h # Factory wrapper
├── config.cc # Factory + REGISTER_FACTORY
└── (optional) matcher.cc, async_clients.cc, ...The filter implements:
class MyFilter : public Http::PassThroughFilter {
Http::FilterHeadersStatus decodeHeaders(Http::RequestHeaderMap&, bool end_stream) override;
Http::FilterDataStatus decodeData(Buffer::Instance&, bool end_stream) override;
Http::FilterHeadersStatus encodeHeaders(Http::ResponseHeaderMap&, bool end_stream) override;
// ... encodeData, decodeTrailers, encodeTrailers, on1xxHeaders, ...
};Many filters subclass Http::PassThroughFilter (envoy/http/filter.h) which defaults all callbacks to "continue".
The factory implements Server::Configuration::NamedHttpFilterConfigFactory:
class MyFilterConfigFactory : public Common::FactoryBase<MyFilterConfigProto> {
Http::FilterFactoryCb createFilterFactoryFromProtoTyped(...) override;
std::string name() const override { return "envoy.filters.http.my_filter"; }
};
REGISTER_FACTORY(MyFilterConfigFactory,
Server::Configuration::NamedHttpFilterConfigFactory);REGISTER_FACTORY puts a static initializer in the binary that registers the factory at process start, so simply linking the filter into the binary (via extensions_build_config.bzl) is what makes it available.
Per-route configuration
Filters can opt into per-route configuration via typed_per_filter_config. The base class is Router::RouteSpecificFilterConfig; the filter exposes getConfig helpers that read the most specific override (route → vhost → route_config). Most non-trivial filters use this.
Async filters
Filters that need to call out (ext_authz, jwt_authn JWKS fetch, oauth2, gcp_authn) follow the canonical async pattern in filter manager: return StopIteration, kick off an async client request via the cluster manager, then call decoder_callbacks_->continueDecoding() on completion.
Stats
Each filter gets a Stats::Scope from the HCM. The convention is to emit counters under <filter_name>.<event>, e.g. ext_authz.ok, ext_authz.error, ext_authz.denied, jwt_authn.allowed. Histograms are emitted for callout latencies.
Match delegate and composite
Two filters deserve a special mention because they wrap others:
match_delegate(source/extensions/filters/http/match_delegate/) — runs an inner filter only if a Matching API tree matches the request.composite(source/extensions/filters/http/composite/) — selects one of N filter actions based on the matcher tree.
Both let you do per-request filter selection without writing custom code.
Adding a new HTTP filter
The end-to-end recipe:
- Pick a name in the
envoy.filters.http.*namespace. - Create
source/extensions/filters/http/<name>/withBUILD,*_filter.{h,cc},config.{h,cc}. - Define the proto under
api/envoy/extensions/filters/http/<name>/v3/. - Register the filter in
source/extensions/extensions_build_config.bzl(orall_extensions.bzlfor non-removable ones). - Add a
CODEOWNERSentry and aextensions_metadata.yamlentry. - Add unit tests under
test/extensions/filters/http/<name>/. - Add an integration test if behaviour is end-to-end-visible.
- Add a release note in
changelogs/current.yaml. - Read extension policy and confirm the filter belongs in core (or use
contrib/if it doesn't).
The simplest reference for boilerplate is header_mutation — small, no async work, no per-route config. The most complex is ext_proc — async, streaming, mode changes, multiple side conversations.
See also
- HTTP connection manager — the host filter chain.
- Filter manager — the per-stream engine.
- Network filters — the L4 cousin.
- Wasm, Dynamic modules — non-C++ filter authoring.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.