WebAssembly Plugins for API Gateways: Portability, Isolation, and APISIX Boundaries
API7.ai
October 29, 2026
WebAssembly (Wasm) can let a platform team distribute a gateway extension as a portable binary instead of rebuilding the gateway for every change. The useful contract, however, is not “compile once and run everywhere.” It is: compile to a supported Proxy-Wasm ABI, use only host calls implemented by the target gateway, and validate the module against that exact gateway version and execution phase.
Wasm also narrows the interface between an extension and its host, but it does not make an untrusted module harmless. Supply-chain controls, resource limits, rollout isolation, and recovery behavior remain platform responsibilities.
Key Takeaways
- Treat Proxy-Wasm compatibility as an ABI-plus-host-capabilities contract, not only a
.wasmfile format. - Confirm callback phase, request/response body access, and failure behavior before choosing Wasm for a plugin.
- Separate memory sandboxing from process, tenant, resource, and supply-chain isolation.
- In Apache APISIX 3.19.0, only part of the Proxy-Wasm API is implemented and all Wasm plugins run in the same Wasm VM.
- Pin the module digest, SDK and ABI, test fixtures, gateway release, configuration, and rollback artifact as one release unit.
- Canary by gateway instance or traffic slice so a faulty extension cannot consume the whole fleet's error budget.
Define the Portability Contract
Proxy-Wasm specifies an ABI and conventions between L4/L7 proxy hosts and Wasm extensions. A language SDK turns higher-level plugin code into calls across that ABI. A gateway host decides which ABI version, callbacks, properties, and host functions it implements.
That produces four compatibility layers:
| Layer | Question to answer |
|---|---|
| Wasm binary | Can the selected runtime validate and instantiate the module? |
| Proxy-Wasm ABI | Which ABI version did the SDK target? |
| Host implementation | Which callbacks, properties, and host calls does this gateway release implement? |
| Gateway semantics | In which phase does the callback run, and what can still be changed or rejected then? |
A module can be valid Wasm and still fail because the host does not implement a required Proxy-Wasm call. It can also load successfully but behave differently because two gateways expose different properties, buffering rules, or lifecycle behavior. Maintain a per-host compatibility suite instead of inferring support from the file extension.
Place the Extension in the Request Lifecycle
flowchart LR
C[Client] --> R[Gateway rewrite phase]
R --> A[Gateway access phase]
A --> U[Upstream service]
U --> H[Response header filter]
H --> B[Response body filter]
B --> C
W[Proxy-Wasm module] -. host calls within supported callbacks .-> R
W -. host calls within supported callbacks .-> A
W -. host calls within supported callbacks .-> H
W -. host calls within supported callbacks .-> B
Phase changes meaning. A request rewrite can normalize routing inputs before access policy runs. An access callback can reject a request before proxying. A response-body callback runs after the upstream has responded and may be too late to change an already committed status. Body inspection can also require buffering and copying that header-only logic avoids.
For each callback, record:
- the input the module can read and who supplied it;
- the fields it can mutate;
- whether the response has begun;
- the maximum body and execution cost;
- what the client receives on a trap, timeout, or invalid result;
- whether the platform fails open, fails closed, or removes the extension from service.
Do not call a header, property, or context value trusted identity merely because the host exposes it. Authentication status and the origin of a value still determine whether downstream policy may rely on it.
Understand the APISIX 3.19 Boundary
Apache APISIX 3.19.0 supports plugins written with a Proxy-Wasm SDK, but its documentation also states that only a few APIs are currently implemented. Compatibility must therefore be proven against APISIX, not inferred from another Proxy-Wasm host.
Its documented programming model has several scopes:
- all Wasm plugins run in the same Wasm VM;
- each plugin has its own
VMContext; - each configured Route or Global Rule has a
PluginContext; - each matching HTTP request has an
HttpContext.
These contexts organize lifecycle and state. They are not separate operating-system processes or tenant sandboxes. A Wasm module still executes inside the gateway data-plane process and can consume latency, CPU, memory, or host-call capacity on the request path.
APISIX maps request-header callbacks to either access or rewrite. The default is access; selecting rewrite moves the callback earlier. Those two paths can observe different gateway state and affect which later policies see a mutation. Request-body and response-body callbacks are not automatic: the plugin must request them by setting the documented wasm_process_req_body or wasm_process_resp_body property from the corresponding header callback.
The following is an APISIX 3.19.0 config.yaml excerpt, not a complete deployment:
wasm: plugins: - name: request_guard priority: 7999 file: /opt/apisix/wasm/request-guard.wasm http_request_phase: access
With http_request_phase: access—the default—the request-header callback runs in access. With http_request_phase: rewrite, it runs in rewrite. Changing the setting does not merely optimize timing; it changes the policy sequence and must be regression-tested with every plugin that reads or modifies the same request.
The Route supplies plugin context through conf:
{ "uri": "/orders/*", "plugins": { "request_guard": { "conf": { "policy": "orders-read-v3" } } }, "upstream_id": "orders" }
This is a configuration excerpt. The module, upstream, Admin API authentication, and test service are omitted. Treat conf as untrusted until the module validates its shape and bounds; a control-plane write is not proof that every field is safe to use.
Separate Four Kinds of Isolation
The WebAssembly security model isolates module memory from the host and requires interaction through the embedding's APIs. That is valuable fault containment, but production isolation has more dimensions:
| Boundary | What it addresses | What still needs an operator control |
|---|---|---|
| Memory sandbox | Direct arbitrary access to host memory | Runtime vulnerabilities and unsafe host functions |
| Capability boundary | Which host calls and properties are exposed | Least-privilege host API design and compatibility review |
| Resource boundary | CPU, memory, body buffering, side calls | Enforced limits, load tests, and overload behavior |
| Process or tenant boundary | Blast radius across plugins and tenants | Separate gateway pools or out-of-process execution where required |
| Supply-chain boundary | Which binary was built and approved | Reproducible build, signature/digest, SBOM, scanning, and provenance |
APISIX's shared-VM design makes the last-mile test especially important: do not claim per-plugin VM isolation that the documented host model does not provide. For mutually untrusted tenants or strict fault-containment requirements, separate data-plane pools may be a more appropriate boundary.
Build a Release Unit, Not a Loose Binary
Version these artifacts together:
- source commit and locked dependencies;
- language SDK and Proxy-Wasm ABI version;
- compiler and build flags;
- module digest and signature or attestation;
- required host calls and properties;
- APISIX release and runtime build;
- static module registration and Route/Global Rule configuration;
- conformance, load, fault, and rollback tests.
Load modules from an operator-controlled, read-only path. Verify the digest before rollout. A Route should reference an approved plugin name and bounded configuration; it should not let a tenant provide an arbitrary filesystem path or downloadable module.
Test Failure and Rollback Paths
Use fixtures that cover more than the successful request:
- missing or malformed plugin configuration;
- callback trap and invalid host-call result;
- maximum allowed header and body sizes;
- slow or failing side calls, if the host supports them;
- first request after configuration activation;
- concurrent requests using distinct
HttpContextinstances; - Route plus Global Rule activation on the same request;
- response already started before a body-phase failure;
- worker restart, configuration reload, canary removal, and rollback.
Measure gateway latency and error rate with the extension disabled and enabled under the same workload. A module that is functionally correct but consumes the request-path budget is still unsafe to promote.
Roll Out with an Explicit Blast Radius
Prefer a dedicated canary gateway pool or a controlled traffic slice. Promotion gates should include request success, latency, traps, worker restarts, memory growth, body-buffer pressure, and application correctness. Keep the prior module and configuration together so rollback cannot pair an old binary with an incompatible new conf shape.
If the extension enforces security policy, define what happens when it cannot run. Failing open preserves availability but may bypass the control; failing closed preserves the control but can cause an outage. Make that decision visible in the threat model, alerting, and incident runbook rather than relying on an accidental runtime default.
WebAssembly Plugin Checklist
- Which Proxy-Wasm ABI and SDK version produced the module?
- Does APISIX 3.19 implement every required callback, property, and host call?
- Is the request callback intentionally in
accessorrewrite? - Are body callbacks necessary, bounded, and explicitly enabled?
- Is every identity or routing input traced to a trusted source?
- Are module digest, provenance, and dependencies verified before loading?
- Are CPU, memory, body, and side-call costs tested under concurrency?
- Does the required tenant boundary exceed APISIX's documented shared-VM model?
- Are trap, overload, first-request, response-started, and rollback paths tested?
- Can operators remove the module without rebuilding or corrupting Route configuration?
FAQ
Is a Proxy-Wasm plugin portable across all API gateways?
No. The ABI improves portability, but the target host must implement the callbacks, properties, and host calls the module uses. Test each gateway product and version.
Does Wasm make third-party plugins safe to run in production?
Not by itself. The memory sandbox reduces one class of risk. The module still needs provenance, capability, resource, tenant, rollout, and recovery controls.
Should every custom gateway plugin use Wasm?
No. Use a built-in plugin when it already provides the behavior. Choose Wasm when its language and distribution benefits outweigh host-API gaps and runtime cost. Use an external service or plugin runner when stronger process isolation or broader libraries matter more.
Next Steps
Map the extension to the API gateway request lifecycle, then deliver it through gateway DevSecOps controls and safe gateway CI/CD.