DEV Community

Cover image for 6 Best API Gateways for Developers Breaking Their Routing Logic
Dev Stack Hub
Dev Stack Hub

Posted on

6 Best API Gateways for Developers Breaking Their Routing Logic

Six API gateways compared through the lens of routing: path matching, rewrites, header-based rules, and traffic splitting. Here is what each one does well, where it falls short, and which one I would choose for different projects.

Last reviewed: October 2026. Pricing, licensing, and features change often in this category, so check each vendor's current docs before you commit.

TL;DR

  • AWS-native and fully managed: Amazon API Gateway.
  • Enterprise API programs with governance and policies: Apigee.
  • Microsoft and Azure shops: Azure API Management.
  • Fine-grained route matching and a big plugin ecosystem: Kong Gateway (read the licensing note first).
  • Stateless, declarative, and good at aggregation: KrakenD.
  • Open-source core with an optional dashboard: Tyk.

Most gateway bugs are routing bugs. A trailing slash that returns a 404. A rewrite that doubles a path segment. A canary that sends everything to the new version. A header rule that never fires because the casing is different.

Every gateway here can route a request. They differ in how you express rules, split traffic, ship config changes, and what happens when a rule is wrong.

I compared 6 gateways across two groups: cloud-managed and self-hosted. This is an editorial comparison based on each product's documented capabilities, not a benchmark in which every gateway handled identical traffic.

My biggest takeaway: the best gateway is the one whose routing rules you can read, test, and change safely when something breaks at 2 a.m.

What is an API gateway?

An API gateway is the single entry point between clients and your services. It receives every request, decides which upstream should handle it, and applies cross-cutting rules on the way: authentication, rate limits, transformations, and logging.

For example, a client calls /v1/orders/42, and the gateway authenticates it, rewrites the path to /orders/42, and forwards it to the orders service without the client knowing where that service lives.

That routing step is where gateways differ most. Some express rules as declarative config, some as XML policies, some as code-like expressions. That distinction should drive your choice.

How gateway routing works

Every gateway in this guide runs roughly the same pipeline for each request:

Stage What happens Typical failure
1. Match Find the route by host, path, method, and headers Wrong route wins, or no route matches
2. Authenticate and limit Check keys, tokens, and quotas Rule applied to the wrong route
3. Transform Rewrite the path, add or strip headers Doubled or missing path segments
4. Select upstream Pick a backend: static, weighted, or conditional Split ratios that don't match the config
5. Proxy Forward with timeouts, retries, and health checks Retries on non-idempotent calls
6. Respond Return, transform, or aggregate the response Leaked internal headers or errors

Here is how each gateway expresses the routing-specific parts:

Gateway Match on Rewrite with Split traffic with
Amazon API Gateway Resource path and method Integration request mapping Canary release settings on a REST API stage
Apigee Base path, plus conditions on request variables Policies and the target Path Weighted load balancer across target servers
Azure API Management Operation URL template and method, plus policy expressions The rewrite-uri policy A choose policy with a random expression, or backend pools
Kong Gateway Paths (including regex), methods, hosts, and headers strip_path and the service path Weighted targets in an upstream
KrakenD Endpoint and method; header conditions in Enterprise url_pattern on each backend Repeated hosts for weights, or DNS SRV weights
Tyk listen_path, API versions, and rewrite rules strip_listen_path and target_url Load balancing with weights set in the Dashboard

One routing problem, six configs

To compare gateways fairly, I use the same problem throughout:

  • Rewrite: GET /v1/orders/{id} must reach the upstream as /orders/{id}.
  • Split: 90 percent of traffic goes to orders v1, and 10 percent goes to orders v2.
  • Override: a request with the header X-Canary: true always goes to v2.

Each tool section shows the config for this problem. Where a gateway can't do a part natively, I say so. The testing section at the end includes a small harness you can run against any of them.

How the tools are compared

A gateway that routes a demo request tells you little about production. So the tools are judged on five criteria that matter once real traffic and real teams arrive:

  1. Routing expressiveness: how precisely you can match requests and rewrite them.
  2. Traffic splitting and releases: how well it supports canary, blue/green, and header-based overrides.
  3. Config model and GitOps: whether rules live in reviewable files you can test and deploy from CI.
  4. Operations: how much infrastructure you run, and how it scales and fails.
  5. Cost and licensing clarity: how predictable the pricing and license terms are.

The better gateway is the one that lets you change a routing rule safely, review it in a pull request, and roll it back quickly.

The 6 tools at a glance

Tool Category Best for Config style
Amazon API Gateway Cloud-managed AWS-native APIs, Lambda, and private VPC integrations OpenAPI and infrastructure as code
Apigee Cloud-managed Large API programs with governance XML proxy bundles and policies
Azure API Management Cloud-managed Azure-based teams and hybrid deployments XML policies and Bicep or ARM
Kong Gateway Self-hosted Rich route matching and plugins Declarative YAML, decK, or Admin API
KrakenD Self-hosted Stateless gateways and API aggregation A single JSON file
Tyk Self-hosted Open-source core with optional dashboard API definitions in JSON or YAML

Scorecard against the five criteria

These ratings reflect my editorial judgment based on documented capabilities, not measured test results.

Tool Routing expressiveness Traffic splitting and releases Config model and GitOps Operations Cost and licensing clarity
Amazon API Gateway Medium High Medium High High
Apigee High Medium Medium High Low
Azure API Management High Medium Medium High Medium
Kong Gateway High High High Medium Low
KrakenD Medium Low High High High
Tyk Medium Medium High Medium Medium

The trade-off is clear. Cloud-managed gateways remove the operations work but tie your routing logic to one vendor's model. Self-hosted gateways give you declarative config and portability, and you run them yourself.

Who should pick what

You are... Start with Why
An AWS-native team using Lambda or private services Amazon API Gateway Fully managed, with native canary releases on REST APIs
A large organization with an API governance program Apigee Policy-driven proxies and API product management
A team standardized on Azure Azure API Management Policy expressions and deep Azure integration
A team that needs flexible route matching and plugins Kong Gateway Routes by path, host, header, and regex, plus weighted upstreams
A team that wants a stateless gateway in one JSON file KrakenD Simple deployment and built-in aggregation
A team that wants an open-source core and a dashboard Tyk Open-source gateway with a commercial dashboard option

My default picks: Amazon API Gateway if you're on AWS, Azure API Management if you're on Azure, KrakenD for a simple self-hosted gateway, and Kong when you need the richest route matching and can accept its licensing terms. Replace any of these if the specifics below fit your project better.

Category 1: Cloud-Managed Gateways

These run as a service, so you don't patch or scale gateway nodes. They are the best place to start if your backends already live in that cloud.

1. Amazon API Gateway


Amazon API Gateway is AWS's managed gateway. It offers REST, HTTP, and WebSocket APIs, and it integrates directly with Lambda, HTTP backends, and private services inside a VPC. Its canary release documentation describes how to send a percentage of traffic to a new deployment.

What makes it useful

  • Fully managed, with no gateway servers to run or scale.
  • Stage variables let one API definition point to different backends per stage.
  • Canary release deployments on REST API stages, with a percentTraffic setting and stage variable overrides.
  • Native integrations with Lambda, authorizers, usage plans, and AWS services.
  • Configuration through OpenAPI, the AWS CLI, and infrastructure-as-code tools.

Here is the rewrite part of the running problem for a REST API. The integration URI uses a stage variable for the host, and the path parameter is mapped from the method request to the integration request.

openapi: "3.0.1"
info:
  title: orders-gateway
  version: "1.0"
paths:
  /v1/orders/{id}:
    get:
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      x-amazon-apigateway-integration:
        type: http_proxy
        httpMethod: GET
        uri: http://${stageVariables.ordersHost}/orders/{id}
        requestParameters:
          integration.request.path.id: method.request.path.id
        passthroughBehavior: when_no_match
Enter fullscreen mode Exit fullscreen mode

The 90/10 split comes from a canary release. First deploy to the stage with the production host in a stage variable, then deploy again with canary settings that override that variable for 10 percent of traffic:

# 1. Deploy v1 to the stage, with the host in a stage variable
aws apigateway create-deployment \
  --rest-api-id abcd1234 \
  --stage-name prod \
  --variables ordersHost=orders-v1.internal.example.com

# 2. Deploy again as a canary that overrides the variable for 10% of requests
aws apigateway create-deployment \
  --rest-api-id abcd1234 \
  --stage-name prod \
  --canary-settings percentTraffic=10,stageVariableOverrides={ordersHost=orders-v2.internal.example.com}
Enter fullscreen mode Exit fullscreen mode

The important part is that the split is percentage-based and applies to the whole stage. I didn't find a native way to force a header such as X-Canary: true to v2 in the documentation I reviewed, so a header override needs a separate stage or a layer in front.

Where it falls short: A canary deployment covers the whole API stage, so you can't canary a single route. The documented canary workflow is for REST APIs, so check the current feature matrix if you plan to use the lighter HTTP API type. Routing rules also live partly in OpenAPI extensions and partly in stage settings, which makes them harder to review in one place.

My verdict: Amazon API Gateway is my default for AWS-native teams that want a managed gateway and simple percentage-based canaries.

2. Apigee


Apigee is Google Cloud's API management platform. You build an API proxy from a bundle of XML: a ProxyEndpoint that receives requests, a TargetEndpoint that calls your backend, and policies in between. Its load balancing documentation covers target servers and the available algorithms.

What makes it useful

  • RouteRules with conditions let one proxy send requests to different TargetEndpoints.
  • Target servers separate backend hosts from proxy config so that you can change them per environment.
  • A built-in load balancer with RoundRobin, Weighted, and LeastConnections algorithms.
  • A large policy library for transformation, security, and traffic management.
  • Managed and hybrid deployment options.

This proxy covers the whole routing problem: the BasePath handles /v1/orders, a header-based RouteRule overrides to v2, and a weighted load balancer splits everything else.

<ProxyEndpoint name="default">
  <HTTPProxyConnection>
    <BasePath>/v1/orders</BasePath>
  </HTTPProxyConnection>

  <!-- Header override: always v2 -->
  <RouteRule name="canary-header">
    <Condition>request.header.x-canary = "true"</Condition>
    <TargetEndpoint>orders-v2</TargetEndpoint>
  </RouteRule>

  <!-- Default rule goes last -->
  <RouteRule name="default">
    <TargetEndpoint>orders-split</TargetEndpoint>
  </RouteRule>
</ProxyEndpoint>

<TargetEndpoint name="orders-split">
  <HTTPTargetConnection>
    <LoadBalancer>
      <Algorithm>Weighted</Algorithm>
      <Server name="orders-v1"><Weight>9</Weight></Server>
      <Server name="orders-v2"><Weight>1</Weight></Server>
    </LoadBalancer>
    <Path>/orders</Path>
  </HTTPTargetConnection>
</TargetEndpoint>

<TargetEndpoint name="orders-v2">
  <HTTPTargetConnection>
    <LoadBalancer>
      <Server name="orders-v2" />
    </LoadBalancer>
    <Path>/orders</Path>
  </HTTPTargetConnection>
</TargetEndpoint>
Enter fullscreen mode Exit fullscreen mode

The request path after the BasePath is appended to the target Path, so /v1/orders/42 reaches the backend as /orders/42. The orders-v1 and orders-v2 names refer to target servers that you define in the environment.

The important part is that every server in one load balancer shares the same Path. If v2 lived at a different path, a weighted balancer can't express that, and you would need conditional routes or a separate proxy in front.

Where it falls short: The XML bundle model is verbose, and routing logic spreads across proxy endpoints, target endpoints, and target servers. Enterprise-grade pricing and platform breadth make it heavy for a small team that only needs routing. Check the current pricing model before you commit.

My verdict: Apigee is the strongest option when routing is one part of a larger API governance program, and a poor fit when it is the whole job.

3. Azure API Management


Azure API Management, or APIM, is Microsoft's managed gateway. Its routing logic lives in policies, XML snippets that run in the inbound, backend, outbound, and on-error sections of a request. Policies can be set at global, product, API, or operation scope. See the set-backend-service policy reference.

What makes it useful

  • Policy expressions use C# syntax so that conditions can inspect headers, query strings, and variables.
  • set-backend-service switches the backend by base URL or by a named backend entity.
  • Backend entities can form a load-balanced pool with circuit breaker rules.
  • Gateway options include classic, v2, consumption, and self-hosted gateways for hybrid setups.
  • You can configure this through the portal, Bicep, ARM, and the management API.

Here is an operation-level policy for the running problem. It rewrites the path, then picks a backend with a header override or a 10 percent random split.

<policies>
  <inbound>
    <base />
    <rewrite-uri template="/orders/{id}" />
    <choose>
      <when condition="@(context.Request.Headers.GetValueOrDefault(&quot;x-canary&quot;, &quot;&quot;) == &quot;true&quot; || new System.Random().Next(1, 101) > 90)">
        <set-backend-service base-url="https://orders-v2.internal.example.com" />
      </when>
      <otherwise>
        <set-backend-service base-url="https://orders-v1.internal.example.com" />
      </otherwise>
    </choose>
  </inbound>
  <backend>
    <base />
  </backend>
  <outbound>
    <base />
  </outbound>
  <on-error>
    <base />
  </on-error>
</policies>
Enter fullscreen mode Exit fullscreen mode

The operation's URL template would be /v1/orders/{id}. Published examples of this pattern note that APIM has no dedicated traffic-splitting policy, so you build it from a random expression, and each request is decided independently.

The important part is to keep the split values in named values, not hard-coded in the expression, so you can change the percentage without editing policy code.

Where it falls short: XML plus C# expressions is hard to unit test, and a quoting mistake is a runtime error. The random split is not sticky, so the same user can bounce between versions. Tier choice affects features and cost, so check the tier comparison before you design around a capability.

My verdict: Azure API Management is the natural choice for Azure-centric teams, and its policy expressions make header-based routing easy. Plan to build and test the traffic split yourself.

Category 2: Self-Hosted Gateways

These run on your infrastructure, in containers or on Kubernetes. You get portable, declarative config and full control, and you take on the operations and the licensing questions.

4. Kong Gateway


Kong Gateway models routing with services, routes, upstreams, and plugins. Routes match requests by path (including regex), method, host, and headers, and upstreams hold weighted targets. Its documentation covers the entities and the declarative config format.

A licensing note before you adopt it. Starting with Gateway 3.10, released in March 2025, Kong stopped publishing prebuilt open-source Docker images and removed free mode, so running 3.10 or later without a license behaves like an expired Enterprise license. The last open-source line is 3.9, which Kong's community discussions describe as frozen. Confirm Kong's current licensing and downloads before you choose it.

What makes it useful

  • Precise route matching on paths, regex, methods, hosts, and headers.
  • Upstreams with weighted targets and health checks, so a 90/10 split is a config value.
  • A large plugin ecosystem for authentication, rate limiting, and transformation.
  • Declarative config you can validate and sync from CI with decK, or run without a database.
  • Kubernetes support through its ingress controller.

This declarative file covers the running problem. The main route sends traffic to a weighted upstream, and a second, more specific route sends the canary header straight to v2.

_format_version: "3.0"

upstreams:
  - name: orders-upstream
    targets:
      - target: orders-v1.internal:8080
        weight: 90
      - target: orders-v2.internal:8080
        weight: 10

services:
  - name: orders
    host: orders-upstream
    path: /orders
    routes:
      - name: orders-route
        paths:
          - /v1/orders
        methods:
          - GET
        strip_path: true

  - name: orders-v2
    host: orders-v2.internal
    port: 8080
    path: /orders
    routes:
      - name: orders-canary-header
        paths:
          - /v1/orders
        methods:
          - GET
        headers:
          x-canary:
            - "true"
        strip_path: true
Enter fullscreen mode Exit fullscreen mode
# Preview, then apply (older decK versions use `deck diff` and `deck sync`)
deck gateway diff kong.yaml
deck gateway sync kong.yaml
Enter fullscreen mode Exit fullscreen mode

The important part is how the rewrite works. With strip_path set to true, Kong removes the matched route path, /v1/orders, and prepends the service path, /orders, so /v1/orders/42 reaches the backend as /orders/42.

Where it falls short: The licensing change is the biggest risk, because the free path now ends at an older release and current builds need a license. Route precedence between a plain route and a header route is easy to get wrong, so confirm the winner with a test request. Plugin availability also varies by tier, so check that the plugins you need are in the plan you choose.

My verdict: Kong has the most expressive routing in this list. Weigh the licensing terms first, and pick it when you will use that expressiveness.

5. KrakenD


KrakenD is a stateless gateway driven by one JSON file, with no database. You declare endpoints, and each endpoint lists the backends it calls. Its backend documentation explains how host, url_pattern, and aggregation work. The Community Edition is open source under Apache 2.0, and an Enterprise Edition adds features.

What makes it useful

  • Stateless and database-free, so deployment is a container and a config file.
  • An endpoint can call several backends in parallel and merge the results, which suits backend-for-frontend designs.
  • The url_pattern on each backend rewrites the path, using variables from the endpoint.
  • Egress load balancing across a host list, with DNS SRV records for weighted balancing.
  • Enterprise adds API key authentication and conditional routing, and the configuration is compatible with the Community Edition.

Here is the Community Edition config for the running problem. KrakenD's documentation describes a simple way to weight a host list: repeat the host. Nine entries for v1 and one for v2 give a 90/10 split.

{
  "$schema": "https://www.krakend.io/schema/krakend.json",
  "version": 3,
  "endpoints": [
    {
      "endpoint": "/v1/orders/{id}",
      "method": "GET",
      "output_encoding": "no-op",
      "backend": [
        {
          "encoding": "no-op",
          "url_pattern": "/orders/{id}",
          "host": [
            "http://orders-v1:8080", "http://orders-v1:8080", "http://orders-v1:8080",
            "http://orders-v1:8080", "http://orders-v1:8080", "http://orders-v1:8080",
            "http://orders-v1:8080", "http://orders-v1:8080", "http://orders-v1:8080",
            "http://orders-v2:8080"
          ]
        }
      ]
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

The header override needs Enterprise conditional routing. In that edition, you split v1 and v2 into separate backends and let a header rule choose between them, with a fallback for everything else:

"backend": [
  {
    "url_pattern": "/orders/{id}",
    "host": ["http://orders-v2:8080"],
    "extra_config": {
      "backend/conditional": {
        "strategy": "header",
        "name": "X-Canary",
        "value": "true"
      }
    }
  },
  {
    "url_pattern": "/orders/{id}",
    "host": ["http://orders-v1:8080"],
    "extra_config": {
      "backend/conditional": {
        "strategy": "fallback"
      }
    }
  }
]
Enter fullscreen mode Exit fullscreen mode

The important part is KrakenD's design rule that one endpoint can't have several write backends, which keeps the gateway from acting as a transaction coordinator.

Where it falls short: Weighted splitting in the Community Edition is a workaround, not a feature, and header-based routing and API keys are Enterprise-only. By default, url_pattern must be RESTful, which you can relax with disable_rest. KrakenD has also announced that it is dropping plugin support in the open-source edition, so check the current status if you depend on custom plugins.

My verdict: KrakenD is my default for a simple self-hosted gateway, especially when you want aggregation and a config file you can review in one place. It is the weakest option here for fine-grained traffic splitting.

6. Tyk


Tyk is a gateway whose routing lives in API definitions. Each definition sets a listen_path, a target_url, and options such as strip_listen_path and load balancing. The gateway is open source, and the Dashboard and other management features are offered commercially. Its load balancing documentation covers target lists and weighting.

What makes it useful

  • Simple API definitions, expressed as JSON or as Kubernetes resources through Tyk Operator.
  • Native round-robin load balancing with a target_list.
  • Weighting in the Dashboard so that you can send, for example, 10 percent of traffic to a beta backend.
  • Service discovery integrations, such as Consul and etcd, for dynamic target lists.
  • Rewrite rules that can act on request details, plus gRPC load balancing.

Here is a Tyk Operator definition for the running problem. Without weights, the two targets share traffic evenly.

apiVersion: tyk.tyk.io/v1alpha1
kind: ApiDefinition
metadata:
  name: orders
spec:
  name: orders
  use_keyless: true
  protocol: http
  active: true
  proxy:
    listen_path: /v1/orders
    strip_listen_path: true
    target_url: http://orders-v1.internal:8080/orders
    enable_load_balancing: true
    target_list:
      - http://orders-v1.internal:8080/orders
      - http://orders-v2.internal:8080/orders
Enter fullscreen mode Exit fullscreen mode

The rewrite follows the documented rule: Tyk strips the listen path and appends what remains to the target URL, so /v1/orders/42 reaches the backend as /orders/42. For the 90/10 ratio, set weights of 9 and 1 in the Dashboard's load balancing settings. A header override can use a URL rewrite rule that matches the header, and the exact syntax depends on your Tyk version, so check its rewrite documentation.

The important part is how Tyk treats versions. Its documentation recommends a separate, non-versioned API definition for each URL-based version (e.g., /v1 and /v2), because versions in an API definition assume the same URL structure.

Where it falls short: Weighted routing is documented through the Dashboard, so confirm how you will express weights in your deployment model, whether files, the Operator, or the Dashboard. Without strip_listen_path, proxied requests are literal, so the listen path leaks into the upstream URL. Check which management features your use case needs a commercial license for.

My verdict: Tyk is a solid choice if you want an open-source gateway with a Kubernetes-friendly definition format and the option of a dashboard.

Kong, KrakenD, or Tyk?

Kong has the richest route matching and plugin ecosystem, but it carries the licensing risk described above. KrakenD is the simplest to run and the best at aggregation, but it has the weakest traffic splitting in its Community Edition. Tyk sits between them, with an open-source gateway, simple definitions, and weighting that lives in its Dashboard. Test each on the same routing problem, using the harness below, before you decide.

Pricing, licensing, and lock-in

Prices change too often to list, but the models differ in ways that matter:

  • Cloud-managed gateways charge by usage, tier, or subscription. Amazon API Gateway bills per request, Azure API Management sells tiers with different features, and Apigee uses enterprise-style pricing, so model your expected traffic before you compare.
  • Kong changed its terms in 2025: the free path ends at the 3.9 open-source line, and later releases need a license.
  • KrakenD has a free Community Edition under Apache 2.0 and a commercial Enterprise Edition that is quoted on request.
  • Tyk offers an open-source gateway, with commercial management features such as the Dashboard.

Lock-in lives in your rules. XML policies and C# expressions don't port between vendors, while declarative route definitions are easier to translate. Keep business logic out of the gateway, so switching is a routing migration, not a rewrite.

In general, the more routing logic you put into vendor-specific policy, the more you should check export options and exit costs before you commit.

Test your routing logic before production

Routing rules fail quietly, so test them like code. The setup below works against any gateway in this guide. First, a tiny upstream that reports its path and version, so you can see exactly what the gateway sent.

# echo.py  (pip install flask)
import os
from flask import Flask, jsonify, request

app = Flask(__name__)
VERSION = os.environ.get("VERSION", "v1")

@app.get("/orders/<order_id>")
def get_order(order_id):
    response = jsonify({"id": order_id, "path": request.path, "version": VERSION})
    response.headers["X-Served-By"] = VERSION
    return response

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8080)
Enter fullscreen mode Exit fullscreen mode

Run one copy as v1 and another as v2, point the gateway at them, then assert the three requirements from the running problem:

import collections
import requests

GATEWAY = "http://localhost:8000"

def test_path_is_rewritten():
    r = requests.get(f"{GATEWAY}/v1/orders/42", timeout=5)
    assert r.status_code == 200
    assert r.json()["path"] == "/orders/42"

def test_header_forces_v2():
    for _ in range(50):
        r = requests.get(
            f"{GATEWAY}/v1/orders/42",
            headers={"X-Canary": "true"},
            timeout=5,
        )
        assert r.headers["X-Served-By"] == "v2"

def test_split_is_close_to_90_10():
    counts = collections.Counter()
    n = 2000
    for _ in range(n):
        r = requests.get(f"{GATEWAY}/v1/orders/42", timeout=5)
        counts[r.headers["X-Served-By"]] += 1
    v2_share = counts["v2"] / n
    assert 0.06 <= v2_share <= 0.14, counts
Enter fullscreen mode Exit fullscreen mode

The tolerance on the split matters. With 2,000 requests, a true 10 percent split will vary by well under a percentage point, so a band of 6 to 14 percent catches a wrong ratio without flaking. Run these in CI against a staging gateway whenever routing config changes.

Common routing bugs

Symptom Likely cause What to check
404 only when the URL ends in a slash Trailing-slash handling differs between gateways Test the path with and without a trailing slash
The backend receives /v1/orders/42 The route prefix wasn't stripped strip_path, strip_listen_path, or a rewrite rule
The backend receives /orders/orders/42 A stripped prefix plus a service path that repeats the segment Compare the route path with the service or target path
The canary header is ignored Route precedence, or the rule never matches Send the header through a test and confirm which route wins
The split is off at low volume Random selection needs a large sample Measure with at least 1,000 requests
Users bounce between versions The split is random per request, not sticky Route by header, cookie, or a hash of the user ID
Duplicate writes appear The gateway retried a non-idempotent call Limit retries to idempotent methods

Roll out a canary with a rollback

A percentage split is only safe if something watches it. This sketch shows the loop: raise the weight in steps, compare the error rate with the baseline, and roll back when it worsens. Wire set_weight and error_rate to your gateway's API and your metrics.

STEPS = [1, 5, 25, 50, 100]

def promote(set_weight, error_rate, baseline, tolerance=0.005):
    for percent in STEPS:
        set_weight(percent)
        # Let traffic soak here, then compare against the baseline
        if error_rate() > baseline + tolerance:
            set_weight(0)
            raise RuntimeError(f"Rolled back at {percent}%")
Enter fullscreen mode Exit fullscreen mode

Also set explicit upstream timeouts, add health checks or circuit breakers where the gateway supports them, and tag every response with the version that served it so that you can compare error rates and latency per version.

What gateways can't replace

A gateway centralizes routing, but it doesn't remove the need for good design.

  • Business logic belongs in services: Complex orchestration in gateway policy is hard to test, version, and debug. Keep the gateway to routing, security, and cross-cutting rules.
  • Authentication isn't authorization: A gateway can verify a token, but your services still decide what each caller may do.
  • Config drift is a real risk: Rules edited in a console and not in version control will diverge from what you reviewed. Treat gateway config as code.
  • Observability is your job: Without per-route metrics, logs, and traces, a bad rule looks like a flaky backend.

Gateways work best when they handle routing and policy, and you stay responsible for testing the rules, watching the traffic, and keeping the logic simple.

Final verdict

No single gateway suits every team. If you're on AWS, start with Amazon API Gateway for managed canaries. If you're on Azure, use Azure API Management and plan to build your own traffic split. If you run a large API program with governance needs, consider Apigee. If you want a simple self-hosted gateway, use KrakenD. If you need the richest route matching and can accept its licensing terms, use Kong. If you want an open-source gateway with an optional dashboard, consider Tyk.

A gateway shortens the path between a client request and the right service, but the first working route is only the beginning. Choose the gateway whose rules you can read, test in CI, and change safely when traffic and teams grow.

Frequently asked questions

What is the best API gateway for microservices?

It depends on where you run. Amazon API Gateway suits AWS, Azure API Management suits Azure, Apigee suits large governed API programs, and Kong, KrakenD, and Tyk suit self-hosted setups on Kubernetes or containers.

What is the difference between an API gateway, a reverse proxy, and an ingress controller?

A reverse proxy forwards requests to backends. An ingress controller routes external traffic into a Kubernetes cluster. An API gateway adds API-level features, such as authentication, rate limiting, transformation, and developer management. The lines blur, and several products play multiple roles.

How do I run a canary release at the gateway?

Send a small percentage of traffic to the new version and watch error rates and latency before raising it. Amazon API Gateway has canary settings on REST API stages, Kong uses weighted upstream targets, Apigee uses a weighted load balancer, and Azure API Management needs a policy expression. Use a header or cookie rule if users must stay on one version.

Is Kong still open source?

Kong's source repository is open, but the open-source distribution stopped at the 3.9 line. Starting with 3.10, Kong stopped publishing prebuilt open-source images and removed free mode, so current builds need a license. Check Kong's current terms before you adopt it.

Should I put business logic in my API gateway?

Generally no. Keep the gateway to routing, authentication, rate limiting, and transformation. Business rules in gateway policy are hard to test and tie your system to one vendor.

Top comments (2)

Collapse
 
alinashah profile image
Alina Shah •

The shared test harness is the strongest part, and I have a few technical notes after reading the configs closely.

  1. Kong route precedence. The header route and the plain route share the same paths and methods, so you’re relying on the router ranking the header-matched route higher. In the traditional router, extra match criteria like headers raise priority, but with the expressions router (router_flavor = expressions) you set priority explicitly. It’s worth stating which flavor the YAML assumes, since the same config could behave differently after an upgrade.

  2. KrakenD’s repeated-host weighting. As I understand it, this works through round-robin over the host list. That makes the split deterministic (exactly 1 in 10 per cycle, per instance) rather than random. That’s fine for a canary, but two caveats apply: each replica keeps its own counter, and if a v1 host gets dropped by a health check, the effective ratio drifts from 90/10.

  3. The split test’s tolerance. For n=2000 and p=0.1, the standard deviation is about 0.67 percentage points, so the 6–14% band is roughly ±6σ. That makes it flake-proof but also lenient. A deterministic balancer like KrakenD’s could use a much tighter band. For random ones (APIM), a binomial test at a fixed alpha is more principled than a hand-picked range.

  4. Azure APIM randomness. Since new System.Random() is evaluated per request, the split can’t be made sticky. A hash of a stable identifier (a user ID claim or a header) modulo 100 would give deterministic, sticky routing and be unit-testable. Moving the threshold into a named value, as you suggest, makes that approach even cleaner.

  5. API Gateway canary limits. Because the canary is stage-wide and percentage-only, the usual workaround for header-based overrides is two stages (or two custom domain base-path mappings) with routing in front. Do you know if anyone has done this cleanly without CloudFront Functions?

Question: for Tyk, did you get weighted targets working purely in the API definition (without the Dashboard)? The docs only seem to cover the Dashboard path, and I’d like to keep it all in Git.

Collapse
 
jennifer-smith profile image
Jennifer Smith •

Using one running problem (rewrite + 90/10 split + header override) across all six gateways makes the comparison much easier to follow than a plain feature list.

A few things I liked:

  • The “one routing problem, six configs” idea. Seeing that Azure APIM has no dedicated traffic-splitting policy, and that KrakenD CE needs repeated hosts to fake weights, says more than a feature matrix would.
  • The licensing note on Kong. Many people still assume it’s fully open source, so flagging the 3.10+ change up front is useful.
  • The pytest harness at the end. Testing routing rules in CI is something teams skip until a canary sends 100% of traffic to the wrong version.

One thing I’d love to see as a follow-up: sticky routing. You mention that random splits let users bounce between versions, so how would you do cookie- or user-ID-hash-based stickiness in Kong or Tyk without adding a layer in front?

Also curious whether you’ve tried Envoy or Traefik for the same problem. Their route-level weighted clusters might be interesting next to Kong’s upstream targets.

Bookmarked for the next time I have to debug a doubled /orders/orders/42 path at 2 a.m. 😅