Here is a pattern I see constantly on Indian products: the site scores well on Lighthouse, the CDN dashboard shows a high cache hit rate at the Mumbai edge, and users still say the app feels slow.
Both things are true at once. The CDN is working exactly as designed. It is just solving a problem that is not the one your users have.
Latency is per round trip, not per page
The number people quote — "we're about 200ms from the US" — is a round-trip time. One trip there and back. The mistake is treating it as a one-time cost per page load.
Watch what a single cold HTTPS request actually costs:
- DNS lookup — one round trip, unless cached
- TCP handshake — one round trip
- TLS handshake — one round trip on TLS 1.3, two on 1.2
- The actual request — one round trip
That is three to four round trips before your server has even seen the request. At 200ms each, you have spent most of a second on protocol overhead alone. Your application code has not run yet. Your database has not been queried.
Now add what a real page does. Your frontend loads and calls the API. The API endpoint queries the database three times in sequence — a lookup, a join, an update — because that is how ordinary application code is written. If the database is in the same region as the API, those three queries cost single-digit milliseconds. If it is not, each one is another full round trip.
This is why latency feels multiplicative rather than additive. It is not one 200ms penalty. It is 200ms charged repeatedly, at every layer that talks to something far away.
What the CDN actually fixed
A CDN caches static assets — your JavaScript bundle, CSS, images, fonts — at an edge location near the user. In India that means Mumbai, Chennai, Delhi and a few others. Those assets genuinely do get fast. Cache hit rate climbs. Lighthouse is happy.
But a CDN cannot cache a POST to /api/orders. It cannot cache a personalised dashboard. It cannot cache anything that depends on who is logged in, which is most of what a real application does.
So on a modern app — a thin frontend with an API behind it — the CDN accelerates the smallest and least important part of the experience. The user sees the shell of your app quickly, then stares at loading spinners while every request that matters crosses an ocean and comes back.
That is the worst possible outcome, incidentally. A slow page that loads slowly reads as "slow site." A fast shell full of spinners reads as "broken site."
India makes this worse than the raw numbers suggest
Two reasons, both structural.
Most of your users are on mobile. Last-mile latency on a mobile network adds to everything above, and it is variable rather than constant. A user on a good 5G connection in Bandra and a user on congested 4G in a tier-2 city are having very different experiences of the same 200ms link. Your monitoring, running from a datacenter, sees neither.
Peak hours are concentrated. Your entire user base is in one timezone, roughly one working day, with evening traffic bunched into a few hours. There is no natural load spreading across the globe to smooth things out. Whatever your worst-case latency behaviour is, most of your users will meet it at the same time.
Measure your own numbers first
Do not take my figures or anyone else's. Run this from a machine on an Indian connection — ideally from a phone tethered, not office fibre, because that is closer to what your users have.
Make a format file:
dns: %{time_namelookup}s
tcp: %{time_connect}s
tls: %{time_appconnect}s
ttfb: %{time_starttransfer}s
total: %{time_total}s
Then hit your own API:
curl -w "@curl-format.txt" -o /dev/null -s https://api.yourapp.com/health
Read it like this. The gap between tcp and tls is your handshake cost. The gap between tls and ttfb is your server actually doing work plus one more round trip. If ttfb is large and the gap from tls is small, your problem is the network. If the gap is large, your problem is your code or your database.
Run it against an endpoint that hits the database, not just /health. The health check is the flattering number.
For the cross-region baseline, tools like cloudping.co and awsspeedtest.com measure round-trip times from your current connection to each region. Useful for the comparison, but they measure the network, not your application — your own curl output is the number that matters.
What to actually move
In priority order:
1. Your database, next to your API. This is the highest-leverage fix and the one people skip. An API in Mumbai talking to a database in Virginia is worse than both being in Virginia — you have added a round trip to every query while keeping the original problem. Whatever you do, these two belong in the same region as each other.
2. Your API, near your users. Once the database moves, the API should follow. This is where the multiplied round trips collapse into single-digit milliseconds.
3. Static assets on a CDN. Still worth doing. Just recognise it as the last 10%, not the fix.
4. Fewer sequential calls. Independent of geography: three sequential API calls that could be one endpoint cost three round trips no matter where your servers are. Distance makes bad request patterns expensive, but it does not create them.
The honest summary
If your users are in India and your infrastructure is not, no amount of edge caching closes that gap, because the gap is in the requests that cannot be cached. The physics do not negotiate — light through fibre from Mumbai to Virginia takes as long as it takes, and every handshake pays that toll again.
Moving your API and database into an Indian region is not a compliance decision or a patriotism decision. It is the same decision as putting your database in the same availability zone as your app server, applied one level up.
Riven Deploy runs in AWS Mumbai, and the managed Postgres provisions in the same region as the service that uses it — because for a product serving Indian users, that is the correct default rather than a feature.


