Why your website is slower in some countries

A CDN edge near a visitor only speeds up what it holds. What your CDN caches, how far away your server is and how you redirect set your speed by country.

Last updated

Why the same site is fast in one country and slow in another

Somebody in another country tells you the site is slow. You open it from your desk and it loads fine, so you run a speed test, and that comes back fine too. The complaint doesn’t go away, and the tempting explanation is that your CDN has no edge near them.

Your team tests from where your team sits. Any delay that grows with distance lands on visitors far from that spot, so the people testing the site are the ones least exposed to it, and the people most exposed are customers.

Check the edge theory first, because it takes five minutes. Cloudflare, CloudFront and Fastly all publish where their edges are, and if yours lists one in or near that country, a missing edge isn’t the explanation.

An edge close to a visitor is only fast for what it’s holding. Whatever the CDN can’t answer from one of its caches comes from your server, however near the edge is, and that always includes anything it won’t cache. So what a visitor gets depends on three things that change with where they are: what your CDN is allowed to keep at the edge, how far they are from your server, and whether you redirect them by location. Then there’s the question of how you’d ever see it from your desk.

Check whether your CDN caches the page itself

Start with the page, because it’s the first thing a visit asks for, and on a default setup it can be the one request the edge doesn’t answer from its cache.

Cloudflare’s documentation is blunt about it: “The Cloudflare CDN does not cache HTML or JSON by default.” It decides what to cache from the file extension, not the content type, and HTML isn’t on its list. Unless somebody wrote a cache rule, the edge near your visitor holds your stylesheets, scripts and images, and every request for the page itself goes to your server and back before the visitor sees anything.

CloudFront fails in the opposite direction. If your server sends no Cache-Control header and the minimum time to live (TTL) is zero, CloudFront keeps the response for its default TTL, and “if you opt out of using a cache policy, the default TTL (Time to Live) is 24 hours.” One provider won’t serve your page from the edge at all. The other may serve yesterday’s copy of it. Both are defaults, so neither was a decision.

The same default costs visitors differently depending on where they are. Someone near your server pays a short trip for an uncached page, and someone on another continent pays a long one. That’s why this shows up as a country problem and gets diagnosed as a CDN problem.

The fix is a decision before it’s a setting. Decide, for each type of content, what the edge may keep and for how long, then configure it. On Cloudflare the page needs a cache rule before it can be cached at all, because its documentation sends you to Cache Rules “to create a rule to cache everything” beyond the defaults. Then send Cache-Control headers that say how long, and check that your CDN honours them. Give static files a long lifetime and a fingerprinted filename, so a new version arrives under a new name. Give HTML a short, deliberate lifetime: even a few seconds means a repeat request inside that window is answered at the edge instead of at your server. Keep pages that change per visitor, such as account pages and baskets, out of the shared cache altogether.

Then check what actually happens. Request your own homepage and read the cache status header that comes back. On Cloudflare it’s CF-Cache-Status, and the value to watch for is DYNAMIC: “Cloudflare determined at request time that the asset is not eligible for cache, so the request went to the origin web server without a cache lookup.” On any other CDN, look up which header it uses for this. One request answers a question no amount of CDN shopping can.

How far your visitors are from your server

A CDN has many edges, and you have one origin: the server your site actually runs on. Some CDNs check a second, larger cache before they go there, but a request none of their caches can answer goes to your server, and so does anything they won’t cache. For those requests, the distance that counts runs from the visitor to your server, and no coverage map shows you that.

Configuration isn’t the only reason a request misses the edge. CloudFront documents that “if a file in an edge location isn’t frequently requested, CloudFront might evict the file—remove the file before its expiration date—to make room for files that have been requested more recently.” So the edges serving your smallest markets are the ones least likely to be holding your files. That has an awkward consequence for any business expanding abroad: the country you’re trying to win is the one with the coldest cache, and it stays cold until the traffic you’re building arrives. The penalty is heaviest exactly when the expansion needs the site at its best.

Which puts weight on a decision that’s easy to leave alone: where your origin lives. Put it closest to the visitors whose speed matters most to the business, which isn’t necessarily where the company is registered or where the hosting was cheapest. If your buyers are spread across regions far apart, try caching HTML at the edge for a short time before you move servers. It’s a configuration change, and every repeat request it answers at the edge never makes the long trip.

Location redirects cost a round trip and can hide pages from Google

Sending visitors to their own country’s version of the site feels like a courtesy. Done automatically by IP address, it costs you twice.

The first cost is speed. A redirect sends the browser back out for a second request: “The browser must then make another HTTP request at the new location to retrieve the resource,” and Chrome’s Lighthouse documentation says that extra trip “can delay the loading of the resource by hundreds of milliseconds.” The further the visitor is from whatever answers the redirect, the longer it takes.

The other cost lands on the crawl. Google says “the default IP addresses of the Googlebot crawler appear to be based in the USA,” and that “the crawler sends HTTP requests without setting Accept-Language in the request header.” So a redirect keyed on the visitor’s country sends Googlebot wherever it sends a US visitor with no language preference, and your other versions may never be crawled. Google does also crawl “with IP addresses based outside the USA,” and asks you to “treat it like you would treat any other user from that country.” That’s good practice to follow, and no reason to rely on it finding every version.

Google’s own recommendation is the whole fix: “We recommend using separate locale URL configurations and annotating them with rel=“alternate” hreflang annotations.” Give each country version a stable URL, declare how the versions relate, and let the visitor choose. Whether those declarations point back at each other correctly is a job of its own, and hreflang is reciprocal or it is ignored.

Be clear about what the risk is. Google’s spam policies define cloaking as “presenting different content to users and search engines with the intent to manipulate search rankings and mislead users,” and sneaky redirecting as doing it “maliciously.” Both turn on intent and neither names location, so a location redirect built in good faith isn’t what those policies describe. The cost is plainer than a penalty: a version Google doesn’t crawl can’t rank on what’s on it, and a visitor dropped onto another country’s prices has been handed a reason to leave.

Replace the automatic redirect with a prompt: a banner offering the other country’s version, every link on the page still crawlable, and the choice remembered so it doesn’t ask twice. If you were counting on Search Console’s country-targeting setting instead, it’s gone, retired along with the International Targeting report in 2022. And if the crawl is what looks wrong, the contradictions between your canonicals, redirects and robots rules are the next place to look.

How to measure your site’s speed by country

The obvious move is to run a speed test from the country the complaint came from. A test like that measures one machine, in one place, on one connection, at one moment. It’s good for telling you whether a change made things faster. It can’t tell you what your visitors in that country get, because they aren’t on that machine.

Chrome already measures what they get. Its public field dataset, the Chrome UX Report, has a BigQuery dataset for each country, named chrome-ux-report.country_CC by two-letter country code, with a table for each month, and those tables record round trip time (round_trip_time) and connection type (effective_connection_type) alongside the Core Web Vitals. That lets you put one country’s round trip beside another’s and see the gap as a measured number. If you want something lighter, the country_summary table gives “aggregated statistics by month, origin, country and device,” including the share of visits in low, medium and high round-trip bands. Chrome’s public API won’t do this for you: it has no country field, so the numbers it returns combine every country.

Read the split like this. If two countries have similar round trips but different loading times, the difference is on your side of the connection, and you’re back to what the edge is allowed to hold. If one country’s round trip is much longer, the question is where your server is and how traffic reaches it.

There’s one limit. A site has to be popular enough to be included, and Chrome doesn’t say how popular: “An exact number is not disclosed.” Combinations that miss the bar at country level “may still be included at the global level,” so a smaller site can have a global row and nothing for the country you’re asking about. When that happens, collect timings from your own visitors and segment them by country. Field data gives the verdict and lab tests support it, which is also how you work out which of the three metrics you are actually failing.

Keep lab tests for checking your own changes, and run each before-and-after pair from the same place at the same hour, so the only thing that differs between the two runs is the change you made.

What to do this week

Smallest job first.

  1. Look up your CDN’s edge list for the countries your visitors are in. If there’s an edge nearby, take a missing edge off the list of suspects.
  2. Request your homepage and read your CDN’s cache status header. Find out whether the page came from the edge or from your server.
  3. Write down where your server is and which of your visitors’ countries is furthest from it. That distance is what every request your CDN can’t answer pays.
  4. Decide what the edge may keep for each type of content, add a cache rule if your CDN won’t cache HTML without one, and set Cache-Control to match: long and fingerprinted for static files, short and intentional for HTML, and pages that change per visitor kept out of the shared cache.
  5. Pull your site’s country split from the Chrome UX Report in BigQuery and read the round trip beside the loading metrics.
  6. Replace any automatic location redirect with a prompt, and give each country version its own URL.

Our Site Audit is worth running alongside this, because a missing canonical or a noindex on a shared template fails in every country that template serves. What it won’t give you is the split between countries, which is the question you’re trying to answer. That part is a header check and a query, neither of them longer than an afternoon. Location is one of several things that decide whether a visitor converts, and website performance covers the others.

One last thing, and it’s easy to miss: a link shared into a chat app or a social feed is fetched by that platform’s crawler, from its own data centre, which may be in a different country from the person who shared it. If your site behaves differently by location, the crawler reads the tags on whichever version it was served, which is one way a link preview goes wrong when the tags themselves look correct.

Standing offer

The hard part is deciding what to do first

Bring us a URL, an audit report, or a proposal you're not sure about. We'll tell you which problems deserve budget this quarter, and you'll leave with an order of operations you can hand to whoever does the work, whether that's us or not.

Book twenty minutes