Benchmark numbers are one thing. What counts is how fast an API gateway is when it does real work. What a gateway does with each request, meaning the plugins it runs, affects its performance more than anything else.
We therefore ran three tests. The first two are classic benchmark setups: a plain proxy, and a secured API with cheap per-request checks. The third shows Membrane doing real, demanding work. It validates every request against an OpenAPI specification and still handles almost 90,000 requests per second. Every run finished with zero errors.
| Test | What the gateway does | Requests/sec | Errors |
|---|---|---|---|
| 1. Proxying | Forwards requests to the backend | 128,723 | 0 |
| 2. Secured API | Basic Auth, rate limiting, TLS on both hops | 109,542 | 0 |
| 3. OpenAPI validation | Checks every request against an OpenAPI spec | 89,197 | 0 |
Each test sent 1,000,000 requests through the gateway, with 175 requests in flight at once.
We measured the gateway alone, without any interference from the other parts of the test. Client, gateway and backend each ran on their own virtual machine, connected by a real network:
Standard_FX16mds_v2, backend: Standard_F16as_v7, client: Standard_F16as_v6.membrane.sh just like in production. Java 21 (Eclipse Temurin) with a 32 GB heap and the Parallel garbage collector.200 OK. It does almost no work, so the numbers show what the gateway itself can do.POST /shop/v2/products with a JSON body.The gateway receives each request and forwards it unchanged to the backend. This is the classic benchmark number: it shows the raw speed of the gateway engine. It is not a realistic use of an API gateway, though, because a gateway that only forwards requests does nothing a plain reverse proxy couldn't do.
api:
port: 2000
target:
host: backend
port: 2010
api:
port: 2000
target:
host: backend
port: 2010
| Request body | Requests | Errors | Requests/sec |
|---|---|---|---|
| JSON, ~1 KB | 1,000,000 | 0 | 128,723 |
This is a setup commonly used in published API gateway benchmarks, so we included it to make the results comparable. For every request, the gateway:
api:
port: 2000
ssl:
keystore:
location: gateway.p12
password: "..."
keyPassword: "..."
flow:
- basicAuthentication:
users:
- username: loadtest
password: "..."
- rateLimiter:
requestLimit: 2000000
requestLimitDuration: PT1H
target:
host: backend
port: 2011
ssl:
truststore:
location: truststore.p12
password: "..."
api:
port: 2000
ssl:
keystore:
location: gateway.p12
password: "..."
keyPassword: "..."
flow:
- basicAuthentication:
users:
- username: loadtest
password: "..."
- rateLimiter:
requestLimit: 2000000
requestLimitDuration: PT1H
target:
host: backend
port: 2011
ssl:
truststore:
location: truststore.p12
password: "..."
We set the rate limit high enough that no request is rejected. A rejected request is cheaper than a forwarded one, so rejections would make the result look better than it is. Zero errors means every request went through all four steps.
| Request body | Requests | Errors | Requests/sec |
|---|---|---|---|
| JSON, ~1 KB | 1,000,000 | 0 | 109,542 |
TLS, a password check and a rate-limit counter are cheap for the gateway, which is why the result is close to plain proxying.
This test shows how the gateway behaves in a real setup. Validation is a typical task for an API gateway, and one a plain reverse proxy cannot do. The gateway checks every request against the Fruitshop OpenAPI specification: method, path, content type, and the JSON body against the Product schema. It only forwards valid requests to the backend. Learn more about deploying APIs from OpenAPI.
api:
port: 2000
openapi:
- location: fruitshop-v2-2-0.oas.yml
validateRequests: true
target:
host: backend
port: 2010
api:
port: 2000
openapi:
- location: fruitshop-v2-2-0.oas.yml
validateRequests: true
target:
host: backend
port: 2010
The request body is a valid product, {"name":"Mangos","price":2.79}:
| Request body | Requests | Errors | Requests/sec |
|---|---|---|---|
| JSON, 33 bytes | 1,000,000 | 0 | 89,197 |
Validation is the most demanding of the three tasks: for every request, the gateway parses the JSON body and checks it against the schema. Throughput drops the most here, even though the body is much smaller than the ~1 KB body in tests 1 and 2. This is the key result: the plugins you use matter far more for performance than the gateway's raw speed. Membrane stays fast even under this kind of load.
The whole test is scripted. Log in to Azure and run the scripts. They create the VMs, install Membrane and run the tests. Afterwards, run the teardown script to delete the VMs again, because they cost money while they are running. The README explains the steps.
The YAML samples on this page show the essential part of each configuration. The complete configurations are in the conf folder.