CDN vs Load Balancer vs API Gateway: Who Does What on GCP
[IMAGE 1: upload 1.png. Caption: The CDN skips the server, the load balancer picks a server, and the API Gateway decides if a request is allowed.] Picture a shopper opening a product page on a Flipkart-style store. In o
[IMAGE 1: upload 1.png. Caption: The CDN skips the server, the load balancer picks a server, and the API Gateway decides if a request is allowed.]
Picture a shopper opening a product page on a Flipkart-style store. In one second, the phone asks for a product photo, the shopper's cart and, somewhere else, a seller's system is updating stock through an API. Three requests, three very different needs.
The problem
These three terms get mixed up because all of them sit "in front of" the app. Without a clear split, teams make two kinds of mistakes:
- Caching the wrong thing. A cart response cached at the edge can show one user's data to another.
- Re-building the same checks in every service. Each microservice writes its own API key check and rate limit, slightly differently, and one of them gets it wrong.
The cleanest way I know to separate them is one line each: the CDN saves a trip to your servers, the load balancer decides which server, and the gateway decides whether the request is allowed at all.
The solution: one entry point, split by path
On GCP, every request enters through one External Application Load Balancer with one global IP. Its URL map sends each path to the right backend.
pathRules:
- paths: ["/img/*", "/static/*"]
service: image-backend-bucket # Cloud CDN: ON
- paths: ["/api/cart/*"]
service: cart-backend-service # Cloud CDN: OFF
- paths: ["/seller/*"]
service: api-gateway-backend # gateway, then the seller service
Images: Cloud CDN
Cloud CDN is not a separate box. It is a checkbox on a backend inside the same load balancer. On a hit, the edge near the user answers. On a miss, it fetches once from the origin, stores it and serves it.
The origin decides what is cached, through response headers:
/img/iphone-v3.jpg Cache-Control: public, max-age=31536000
/api/cart Cache-Control: no-store
Product images get a long TTL with versioned filenames, so a new image gets a new name and never fights the old cache. The frontend never sets caching rules.
Cart: load balancer straight to the service
The cart is personal and changes every few seconds, so CDN stays off. The load balancer's job here is to spread live traffic across healthy instances. If one instance fails its health check, it stops getting traffic. If traffic grows past capacity, requests queue briefly, latency rises and then the load balancer returns 503. They are not silently dropped.
Seller API: API Gateway
Sellers call the store from their own systems with API keys. The path goes load balancer, then API Gateway, then the seller service. The gateway checks the key or JWT and enforces a fixed quota before any business code runs. You do not deploy it as a service; you give it an OpenAPI spec.
paths:
/seller/stock:
post:
security:
- api_key: []
x-google-backend:
address: https://seller-service-xyz.run.app
# simplified; check the current API Gateway docs for the full spec
A bad or over-limit request is rejected at the gateway, so the inventory service only sees clean, authorized calls.
Trade-offs and limits
- One load balancer is usually enough. I would add another only for a separate domain, internal-only traffic or non-HTTP protocols.
-
Lock the side door. Every Cloud Run service has its own public
run.appURL that skips the load balancer and gateway. I would set ingress to internal and Cloud Load Balancing only. - API Gateway has limits. It handles auth and fixed quotas well. Tiered limits (free vs premium), developer portals or monetization point to Apigee or custom code with Redis counters.
- Gateway is not the first line against floods. Cloud Armor runs at the edge on the load balancer and handles bad IPs, bots and attack patterns. The gateway then enforces who may call what. I would use both.
- Databases are never behind the load balancer. The app calls Cloud SQL and Memorystore directly over private networking.
Takeaway
CDN: same file for everyone, served from the edge.
Load balancer: one entry point that picks a healthy server.
API Gateway: auth and quotas before your code runs.
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.


