Retail & E-commerce// diagnostic

Shoppers land on the wrong country's storefront, and the redirect loops

In short

Three signals decide which market a shopper sees — the URL they asked for, a preference stored from a previous visit, and a location inferred from their network. A loop means two of them are being applied at different layers with no agreed precedence. Fix the order first: explicit URL beats stored preference beats inferred location, and no redirect made on those signals may ever be cached.

Key takeaways

  • Precedence, not accuracy: explicit URL beats stored preference beats inferred location, in that order, everywhere.
  • A market redirect must never be cacheable, and never permanent. You cannot revoke a stored 301.
  • Two redirect authorities — one at the edge, one in the app — is the cause of almost every alternating loop.
  • An edge cache key that omits the market serves the first visitor's country to the next 10,000 on that POP.
  • Currency must be derived from the resolved market, never chosen separately from browser locale or IP.
  • Inferred location should offer a market in a dismissible banner, never perform the redirect.

Before anyone opens the geolocation database, establish which layer is issuing the redirect. A loop is not a sign that the network location lookup is wrong — it is a sign that two systems both believe they own the market decision and are correcting each other. One says Canada because the request arrived from a Toronto network; the other says United Kingdom because a cookie from three weeks ago says so. Neither yields, and the browser aborts after roughly 20 hops.

That is a different fault from a shopper seeing the right catalogue with the wrong currency, which usually involves no redirect at all. Separate the two before you start: they live in different code.

Reproduce it twice before changing anything

Two runs tell you which half of the system is at fault. Use a command-line client rather than a browser for the first: a browser hides the hop chain and reuses cookies you did not intend to send.

  1. Fresh session, explicit market URL. Request the market-prefixed URL with no cookies and follow redirects, printing every hop and its status code. Expected result: 1 hop, status 200, on the URL you asked for. Anything else means an explicit market is being overridden, which is the precedence bug.
  2. Contradictory stored preference. Set the market preference to one country through the selector, then request another country's URL in the same session. Expected result: the URL wins and the stored preference is quietly updated to match. If the page bounces back to the stored market, the preference layer is outranking the URL.
  3. Repeat both from a third country. Use an exit node or an edge function test in a region neither run used. This separates 'the inference is wrong' from 'the inference is applied at the wrong time', which look identical from one location.
  4. Record three things per run: the final URL, the currency on a known product, and the hop count. A loop shows as an alternating pair of paths; a cache fault shows as a correct first response and a wrong second one from the same edge location.

Four ways a shopper ends up on the wrong market

SignatureCauseWhere the fix goes
Hops alternate between the same 2 paths until the browser abortsTwo redirect authorities: an edge rule and an application rule each asserting a different marketCollapse to one authority. The edge may read signals; only one layer may redirect
Wrong market for everyone from one region, and a hard refresh fixes it for that visitor onlyThe redirect response itself was cached — a permanent redirect, or a temporary one served without a no-store directiveResponse headers. Never issue a market redirect as permanent
First shopper on an edge location gets the right market, the next 100 get theirsThe edge cache key omits the market, so one country's HTML is stored under a URL shared by all of themCache key and Vary. The market must be part of the key, not a property of the response
Catalogue and shipping are correct, only the money is wrongCurrency resolved by a different rule from the catalogue market — commonly browser language while market came from the networkPricing resolution. Derive currency from the resolved market and nowhere else
The selector works, then reverts on the next page viewThe preference cookie is scoped to a path or subdomain the other market cannot readCookie scope: domain-level, both markets, long lifetime
Only logged-in shoppers loopAn account country field applied after the session preference, adding a third authorityPrecedence table. Account country is a first-visit default, not a per-request override
What each cause looks like in the hop chain

The precedence order that makes a loop impossible

A loop requires two authorities that disagree. Write one ordered rule, implement it in exactly one place, and the loop cannot form regardless of how bad the location inference is. That is the whole fix, and it is about 40 lines of code in most storefronts.

  1. An explicit market in the URL wins, unconditionally. A path prefix or a country domain is a shopper telling you where they are shopping. Serve it, and write it to the stored preference. Never redirect away from an explicit market for any market reason.
  2. A stored preference wins over inference, and only applies when the URL carries no market. The shopper set it once; treating it as advisory is why the selector appears not to work.
  3. Inferred location is the last resort, and it does not redirect. Serve the default market and offer the inferred one in a dismissible banner. An offer that the shopper accepts becomes a stored preference, which is how the signal graduates.

Currency is a property of the market, not of the shopper

The most common non-looping version of this complaint is a shopper on the correct national catalogue seeing prices in the wrong money. Two different inputs were used: the catalogue market came from the network or the URL, and the currency came from the browser's language header. A shopper in Berlin with an English-language phone then gets German stock, German delivery and sterling prices, and abandons.

Resolve the market once, at the top of the request, and derive everything from it: price list, tax display, delivery options, payment methods and currency. When a price genuinely varies per account rather than per market, that is a separate rendering problem with its own cache boundary — rendering contract pricing without leaking it covers the split. And if the price the cart shows still disagrees with the page after the market is stable, the fault has moved: see the cart price does not match the page price.

Availability is market-scoped too, which is where a fixed routing bug surfaces a second one. A variant sellable in one market may not be listed in another, so a picker built from a global option matrix will offer a combination the resolved market cannot sell — the method for avoiding that is in a variant picker that never offers a dead end.

Geolocation is not the problem. Two systems each convinced they own the market decision is the problem, and better location data makes it worse, not better.

Which redirect to disable first

  1. Count the hops on a cold request to an explicit market URL. More than 1 means something is overriding an explicit instruction. Find it and delete it before anything else.
  2. Count the redirect authorities. Grep the edge configuration, the middleware and the theme or app router for anything issuing a location header on a market condition. If the count is above 1, that is your loop, whatever else is also wrong.
  3. Check the status code and cache headers on the redirect. Permanent, or temporary without a no-store directive, means visitors are carrying a stale decision you cannot revoke.
  4. Check the cache key on the market-resolved HTML. If the market is not in the key, the fault is intermittent by design and returns under load even after the routing is correct — the layering is in the four caches between you and the shopper.
  5. Only now check the inference itself. Compare the country your edge provider reports against the country the shopper says they are in, across 20 real sessions. Corporate networks, carriers routing through another country and privacy relays make a few per cent of these permanently wrong, which is exactly why inference must never be authoritative.

Shipping the fix without a freeze

Routing changes are structural: they alter every URL a crawler and a paid campaign already knows about. That makes them the class of change teams postpone into a peak-season freeze, then ship all at once with three other things. A graded release is better than either — one approver, one verification step and one rollback path per change class, as set out in shipping storefront changes during peak trading.

  • Keep the old market URLs resolving. Whatever mapping you replace, the previous paths must still reach the right catalogue, because campaign links and marketplace listings will point at them for years.
  • Verify with a synthetic check per market. One scheduled request per market URL from a matching region, asserting the final URL, the currency symbol and the hop count. It is the only monitor that catches a market regression before a shopper reports it.
  • Watch the checkout, not the landing page. A market change moves which payment methods are offered and which currency is presented to the processor, and a mismatch there fails after the shopper has committed — the charge fails after the agent confirmed the order is the failure mode to watch for in the first week.

What a correct redirect still will not fix

Market routing decides which catalogue, price list and currency a shopper gets. It does not decide what language the page is written in, and conflating the two is the next bug: a market is commercial, a locale is linguistic, and Switzerland alone breaks any model assuming one market means one language. Translated copy and localised product data belong to the catalogue work.

It also does not fix a slow storefront. If the redirect is correct and the page still arrives late on a phone, the loop was masking an unrelated delivery problem — start with the hero image arrives last on mobile.

Multi-market routing is a small piece of code sitting in the most contended part of a storefront, which is why it is worth deciding deliberately rather than accumulating. The surrounding decisions are in storefront architecture and platform choice, within software for retail and ecommerce; when the answer is a front-end rebuild rather than a patch, that is ordinary MVP and product build work.

Frequently asked questions

Short answers to the follow-ups this page tends to raise.

Should a storefront redirect shoppers based on their IP address at all?

No — infer, then offer, but do not redirect. Network location is wrong often enough (corporate VPNs, carrier routing, privacy relays, travellers) that an automatic redirect strands real shoppers on a catalogue they cannot buy from, and it is the one ingredient every redirect loop needs. Serving the default market with a dismissible banner suggesting the inferred one gets the same commercial benefit and cannot loop.

Which status code should a market redirect use?

A temporary one, with an explicit no-store cache directive. A permanent redirect is cached by browsers and intermediaries and will keep sending that shopper to the wrong market long after you have fixed the rule, with no mechanism to revoke it. Market decisions change per visit by definition, so the response that carries them is the one response on a storefront that must never be stored.

Why does the currency change when a shopper switches language?

Because currency is being resolved from the language header instead of from the market. They are separate inputs that merely correlate: a browser set to English in Berlin says nothing about which price list applies. Resolve the market once per request and derive currency, tax display and payment methods from it, so a language change alters words and nothing else.

Should the country selector store its choice in a cookie or in the URL?

Both, with the URL authoritative. The URL makes the market shareable, linkable and cacheable, which a cookie can never be; the cookie exists only to route a shopper who later arrives at a market-less URL. Store it at domain scope so both markets can read it, and rank it strictly below an explicit market in the path.

How do we test a market redirect without travelling?

Run the request from the region rather than faking the header. Most edge platforms let you invoke a function from a chosen point of presence, and a scheduled synthetic check per market asserts the final URL, the rendered currency and the hop count. Overriding a country header locally tests your code path but not the edge rule in front of it, which is where the loop usually lives.

  • multi-market
  • routing
  • caching
  • storefront architecture
// shipped work

The work behind this page

Builds from our portfolio that this page draws on.

Read next

Working on something in this space?

Tell us where you are in a sentence or two. We'll tell you honestly whether we're the right team, and what a sensible first slice of the work looks like.

Start the conversation