Skip to content

Gateways, proxies and service meshes

What belongs at the edge, what belongs beside each service, and what a mesh actually buys.

01

What an API gateway is for

intermediate

A gateway is the single front door to a set of services, and its job is the work that would otherwise be repeated in each of them: terminating TLS, authenticating the caller, applying rate limits, routing by path or host, and emitting consistent logs and metrics. Doing those once, correctly, is worth more than doing them eleven times with three subtle differences.

The clearest argument for it is security. Authentication implemented per service is authentication implemented inconsistently, and the service that gets it wrong is the one nobody remembers exists. Terminating at the gateway means one implementation to audit, one place to rotate keys, and a uniform answer to who is calling, which the services behind it can then trust rather than reimplement.

The clearest argument against a large one is that gateways attract logic. Request transformation, response shaping, per-client special cases, a small amount of business rule that had nowhere else to go, and eventually the gateway is a shared component that every team must change and nobody owns. That is the enterprise service bus reborn with a modern name, and the reason to state the boundary explicitly: routing, authentication, limits and observability, and no domain logic at all.

Aggregation is the case that genuinely justifies more. A mobile client on a slow network making eleven calls to render one screen is paying eleven round trips, and a backend for frontends, a gateway specific to that client, can make those calls internally and return one response. The important part is that it is owned by the client team and is one per client type, rather than a shared layer with every client's needs in it.

The failure to plan for is that the gateway is now on the path of everything. It must be stateless and horizontally scaled, its configuration must be deployable without a restart, and its own failure has to be understood, because a gateway that is down is an outage of everything behind it regardless of how healthy those services are.

Check yourself

Why is per-service authentication worse than terminating at the gateway?

resist thisWeb clientMobile clientGatewayTLS, auth, limits,routingService Atrusts the identityService BService CBusiness logic herethe slide into a bus
ClientEdge / CDNServiceExternalDashed = asynchronousConsidered, not chosen
Cross-cutting work once, at the door
Ask about this

Answers are generated and can be wrong. The topic above is the reviewed version.

02

Service meshes, and whether you need one

advanced

A mesh moves the concerns of service-to-service communication out of the application and into a proxy deployed beside each instance. Retries, timeouts, circuit breaking, mutual TLS, traffic splitting and per-call telemetry become configuration rather than library code, and crucially they become uniform across services written in different languages by different teams.

That last point is the honest argument. In a single-language estate, a shared client library does most of this, and a library is simpler to operate than a fleet of proxies. In a polyglot estate, a library means implementing and maintaining the same behaviour four times, which is where the sidecar's per-instance cost starts looking cheap.

The features that are genuinely hard to get any other way are mutual TLS everywhere with automatic certificate rotation, consistent traffic shifting for canaries at the network level, and telemetry that is identical across services because it is emitted by the same proxy. Each of those is possible without a mesh and each is fiddly enough that most teams do not do it consistently.

The cost is a second network to understand. A proxy per instance is CPU, memory and a latency addition on every hop, the control plane is a new dependency in the request path's configuration, and debugging now involves asking whether the problem is the application, its sidecar, the other sidecar or the control plane. That is a genuine increase in the number of places a failure can live.

So the honest test is the number of services and the number of languages. A handful of services in one language does not need a mesh and will feel the cost immediately. Fifty services in four languages, with a platform team to run it, is where the uniformity pays for the complexity. Everything in between is a judgement, and the sidecar-free variants that push the same functions into the kernel are a live attempt to lower the price.

Check yourself

Why are sidecar-free mesh modes being developed?

Serviceany languageShared libraryone per languageSidecar proxyone per instanceRetries and mTLSuniform either wayOnce per languageA second network
ServiceEdge / CDNData storeExternalDashed = asynchronousConsidered, not chosen
The same behaviour, in a library or in a proxy beside each instance
Evidence · 1 source
  1. 01
    Istio: ambient mode

    The sidecar-free direction described here, which exists because the per-pod proxy cost was the main objection to running a mesh.

Ask about this

Answers are generated and can be wrong. The topic above is the reviewed version.

03

The edge and the interior are different problems

intermediate

Traffic entering the estate and traffic moving inside it have almost nothing in common, and conflating them produces designs that are wrong at one end. External traffic comes from clients you do not control, over networks you cannot measure, with credentials that may be stolen and volumes that may be hostile. Internal traffic comes from services you deployed, in a network you configured, and every caller has an identity you issued.

So the concerns differ. At the edge: authentication of untrusted callers, per-tenant rate limiting, request size limits, bot mitigation, TLS termination and caching for anonymous traffic. Inside: identity between services, timeouts and retries with budgets, circuit breaking, load balancing that understands long-lived connections, and tracing that spans the whole call tree.

Zero trust is the idea that the interior should not be treated as safe simply because it is the interior. A flat internal network where any service can call any other with no identity means one compromised component reaches everything, which is how a modest breach becomes a total one. Mutual TLS and per-service authorisation make lateral movement a series of separate obstacles rather than one.

The mistake in the other direction is applying edge machinery inside. A heavyweight gateway between every pair of internal services adds a hop, a failure domain and a bottleneck to calls that were fine. Internal calls want the lightest thing that provides identity, timeouts and telemetry, which is the argument for a sidecar or a library rather than for a second gateway.

The practical layering that most estates converge on is a CDN and a public gateway at the edge for untrusted traffic, and a mesh or a shared library inside for identity and resilience between services. Naming which layer a requirement belongs to is usually enough to settle an argument about where a feature should live.

Check yourself

What usually settles an argument about where a feature belongs?

hostile until provenotherwiseauthenticated hopno identity betweenservicesThe internetunknown callersCDN and gatewayauth, limits, bots,TLSService Aidentity issued byyouService BMesh or librarymTLS, timeouts,tracingFlat, trustedone breach reachesall
ClientEdge / CDNServiceExternalDashed = asynchronousConsidered, not chosen
Untrusted callers at the edge, issued identities inside
Evidence · 1 source
  1. 01
    NIST SP 800-207: zero trust architecture

    The argument that a request should be authenticated regardless of its network position, because a flat trusted interior turns one compromised component into access to everything on it.

Ask about this

Answers are generated and can be wrong. The topic above is the reviewed version.