A news site with editions for different countries has to answer one question on every request: which edition should this reader see?
The easy answer is the browser's location prompt. We decided never to show it. Here's the order of fallbacks we use instead, and the small details that turned out to matter.
The resolution order
The edition is decided in Next.js middleware (the proxy), before any page renders. It takes the first of these that exists:
- The reader's own choice. If they've picked an edition in the switcher, a cookie remembers it and it wins over everything else.
- The hosting network's geo headers. The host already derives an approximate country, region and city from the IP address. We read those.
- The device's time zone. When there are no geo headers, the browser's IANA time zone is a surprisingly good hint.
- A default. If all else fails, the U.S. edition.
Nothing in this list needs a permission dialog. All of it is information the request already carries.
Detail 1: validate the geo header, even though your host sent it
Geo headers aren't always a clean two-letter country code. Cloudflare uses XX for "unknown" and codes with digits for things like Tor. If you pass those straight through, you end up looking for the "XX edition".
We accept a country only if it matches ^[A-Z]{2}$ and isn't XX. Everything else falls through to the next step in the list.
Detail 2: skip time zones that span several countries
Asia/Kolkata points to India. Europe/London points to the U.K. Some zones are shared by several countries, though, and guessing among them is worse than not guessing.
Our time-zone table simply leaves those out. Where a zone does pin down a place, like Australia/Perth, it fills in the state and city as well.
Detail 3: overwrite the headers your pages trust
The middleware passes the result to pages as request headers such as x-user-country. It always sets them itself and deletes any incoming copies first:
const requestHeaders = new Headers(request.headers);
requestHeaders.set(COUNTRY_HEADER, country);
requestHeaders.delete(REGION_HEADER);
requestHeaders.delete(CITY_HEADER);
Otherwise a client could send its own x-user-country and the page would believe it.
Detail 4: only show state and city for the reader's own country
If a reader in Ohio switches to the U.K. edition, they shouldn't see "local news for Ohio" mixed into British headlines. Region and city are only passed on when the edition being served is the reader's detected country.
Detail 5: don't set cookies on pages that should be cached
The middleware remembers the detected edition in a short-lived cookie. At first it set that cookie on every response, including article pages. But a caching layer like nginx or Cloudflare won't store a response that sets a cookie, so the pages most readers arrive on from search could never be cached.
Article pages are the same for every reader, so they don't need the cookie at all. Now the middleware returns early for /news/ paths, and the cookie is set on the first edition-dependent page the reader opens instead.
What we store
No precise location, ever. The cookies hold the chosen or detected country code, how it was detected, and the time zone name. A VPN can put a reader in the wrong edition, which is exactly why the manual choice comes first.
This is how editions work on Yuphora, our free news site with editions for 13 countries. The About page lists every source we draw on and how each is licensed.
Written by the Yuphora News team. Yuphora is a free, ad-supported news site published by APPDOOK.
Top comments (0)