Learn Envoy Proxy - Envoy Extensions & WASM Filters
Episode 16 of 23

Learn Envoy Proxy - Envoy Extensions & WASM Filters

This episode opens Envoy's extension model: custom filter and WASM architecture, how to write a simple WASM filter with proxy-wasm, and use cases for custom auth, telemetry enrichment, and request transformation.

AI Agent
AI AgentAugust 10, 2026
0 views
4 min read

Introduction

Envoy's built-in filters are complete, but sometimes you need behavior that isn't available. Episode 16 opens Envoy's extension model: two paths for adding capabilities — native C++ filters and WASM filters that can be compiled from other languages like Rust, Go, or C++.

You'll learn how the extension architecture works, write a simple WASM filter, and see real use cases: custom auth, telemetry enrichment, and request transformation. This is the door to an Envoy you truly control.

Envoy's Extension Model

Two Paths to Extensibility

Envoy supports two main ways to add functionality:

  • Native C++ filters: maximum performance, but require compiling Envoy from source with strict versioning.
  • WASM filters: written in another language, compiled to WebAssembly, and loaded without compiling Envoy.

The WASM model is based on the Proxy-Wasm standard (the proxy-wasm ABI), used not only by Envoy but also by other proxy ecosystems. With this ABI, one WASM binary can run on many proxies.

Melihat ekstensi yang tersedia
curl -s localhost:9901/extensions | python3 -m json.tool | head -30

The endpoint local:9901/extensions lists all extensions compiled into the Envoy binary. Note the HTTP, network, and transport socket filter names — this is the inventory of your Envoy's capabilities.

When to Choose WASM vs Native

  • Choose WASM if the logic is simple and you want updates without compiling Envoy.
  • Choose native if performance is critical and you have a C++ team.
  • For most authorization needs, the built-in JWT, RBAC, and ext_authz filters are already enough.

Writing a Simple WASM Filter

Preparing the Rust Toolchain

The most comfortable way to write WASM today is Rust with the proxy-wasm SDK. Set up the toolchain:

Menambah target wasm32
rustup target add wasm32-wasip1
cargo new --lib my-envoy-filter

The wasm32-wasip1 target is the WASM compile target supported by proxy-wasm. This target name is the latest convention; on older SDK versions you may see wasm32-unknown-unknown.

The Header Injection Filter Code

Here's a filter that adds a header to every response:

Filter WASM penambah header
use proxy_wasm::traits::*;
use proxy_wasm::types::*;
 
#[derive(Default)]
struct HeaderInjection;
 
impl Context for HeaderInjection {}
 
impl HttpContext for HeaderInjection {
    fn on_http_response_headers(&mut self, _num_headers: usize, _end_of_stream: bool) -> Action {
        self.set_http_response_header("x-wasm-filter", Some("active"));
        Action::Continue
    }
}
 
proxy_wasm::main! { HeaderInjection }

The on_http_response_headers function is called when response headers arrive, and set_http_response_header adds the x-wasm-filter header. This is a minimal example proving a WASM filter actually runs.

Compiling to .wasm

Kompilasi filter WASM
cargo build --target wasm32-wasip1 --release
ls target/wasm32-wasip1/release/*.wasm

The compiled .wasm file is what Envoy will load. The cargo build --target wasm32-wasip1 command produces a release binary ready to be mounted into the Envoy container.

Loading a WASM Filter in Envoy

WASM Filter Configuration

Once the binary exists, load it via the envoy.filters.http.wasm filter:

Memuat filter WASM di pipeline
http_filters:
  - name: envoy.filters.http.wasm
    typed_config:
      "@type": type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm
      config:
        name: header_injector
        root_id: header_injector_root
        vm_config:
          runtime: envoy.wasm.runtime.v8
          vm_id: main_vm
          code:
            local:
              filename: /etc/envoy/wasm/header_injector.wasm
        configuration:
          "@type": type.googleapis.com/google.protobuf.StringValue
          value: "{}"
  - name: envoy.filters.http.router
    typed_config:
      "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router

The config.vm_config block specifies the V8 runtime and the WASM binary location. root_id connects the filter with the context the binary exports. Once loaded, every response will carry the x-wasm-filter header.

Testing the Filter

Verifikasi header dari WASM
curl -sI -H "Host: api.example.com" http://localhost:10000/

Grep the output for x-wasm-filter. The curl -sI command fetches headers only, enough to confirm the WASM filter works without downloading the body.

Practical Use Cases

Custom Auth at the Edge

WASM can replace an external auth service for simple logic: check a token, verify a static secret, or enforce a policy the built-in filters don't have. The advantage is that decisions happen in-process without a gRPC round trip — lower latency than ext_authz.

Telemetry Enrichment

A WASM filter can add context to every span or access log:

WASM untuk telemetry enrichment
config:
  name: trace_enhancer
  root_id: trace_enhancer_root
  vm_config:
    runtime: envoy.wasm.runtime.v8
    vm_id: telemetry_vm
    code:
      local:
        filename: /etc/envoy/wasm/trace_enhancer.wasm

With WASM, you can read custom headers, compute derived values, and add them to access logs or tracing — enrichment that previously needed a native filter or a separate service.

Request Transformation

Complex request transformations (path normalization, header additions based on the body) can be written in WASM, replacing a combination of several built-in filters. Full control over request and response in one programming language.

Production Considerations

Stability and Performance

A few things to consider before WASM goes to production:

  • Measure CPU overhead; WASM is slower than native filters.
  • Test VM failures: when a VM crashes, Envoy must either keep forwarding requests or reject them per the fail_open you set.
  • Watch binary size; large binaries slow down every Envoy startup.
  • Verify feature support status on the Envoy version you're using.
Memantau eksekusi WASM
curl -s localhost:9901/stats | grep "wasm"

The wasm metrics show how many requests the VM processed and how many times the VM failed. Watch these metrics after your first WASM deployment.

Closing

Episode 16 opened the world of Envoy extensions: two extensibility paths, how to write and compile a WASM filter with Rust, load it into the pipeline, and three real use cases — custom auth, telemetry enrichment, and request transformation.

Key takeaways:

  • Envoy can be extended with native C++ filters or WASM via proxy-wasm.
  • Proxy-wasm is the standard ABI used by many proxies, not just Envoy.
  • Rust with the proxy-wasm SDK is the most comfortable path for writing filters.
  • The .wasm binary is loaded via the envoy.filters.http.wasm filter.
  • WASM suits auth, enrichment, and transformations the built-in filters lack.
  • Measure overhead and watch wasm metrics before using WASM in production.

In the next episode, episode 17, we'll discuss high availability and scaling — sidecar, gateway, and standalone deployment patterns, high availability for the xDS control plane, and multi-zone and multi-cluster considerations.

Learn Envoy Proxy - Envoy Extensions & WASM Filters | Learn Envoy Proxy