In short
- A session cookie on every page view silently blocks HTML caching — and caching it anyway can hand one shopper's session to everyone.
- Enabling a payment method does not make it appear. Availability is store currency × buyer country × your payment account's domicile, and one wrong combination makes it structurally impossible.
- Geo-detecting a shopper's country helps until you force one your store does not ship to — then checkout breaks for that visitor and nothing tells you.
- Your fastest pages are the ones with nothing personal on them. A cart count or a country-dependent shipping line makes a page uncacheable at the edge.
- Every one of these is invisible in staging and only appears under real traffic from real countries.
The category error underneath all of it
A content site can be rendered once and served to everyone. A storefront cannot, because a storefront is personal: it knows what is in your basket, where you are, what you can pay with, and what shipping will cost you specifically.
Almost every serious launch problem we have seen — and every one below — is a version of the same category error: treating a personal page as though it were a public one. It shows up in the caching layer, in the payment configuration and in the geo logic, and it is invisible until the store is live and taking real traffic from real places.
We run CapyOnsen, our own store, on WordPress and WooCommerce. The four failures below are ones we hit there, not ones we read about.
1. The session cookie that silently blocks your caching
This is the one that costs the most and is spotted the least.
Store platforms issue a session cookie so they can track a basket. If that cookie is set on every front-end page view — including your blog, your FAQ, your about page — then none of those pages can be cached as HTML at the edge, because caching a response that carries a personal session means serving one visitor's session to the next visitor. That is not a slow site; that is a security incident.
The trap is that nothing warns you. The pages work. They are just slow, and the caching layer you configured quietly refuses to help, or worse, helps in a way you will regret.
How to find it
Before proposing any HTML caching on a store, check what the origin actually sends. Request a page that should be perfectly cacheable — an article, a policy page — and look at the response headers for a Set-Cookie carrying a session. If it is there, that is your real blocker, and no amount of CDN configuration fixes it.
On our own store the culprit was a geo pre-set: code that read the visitor's country and primed the customer session so shipping would calculate correctly. Sensible in itself, and it ran on every page load, which meant a session cookie on the journal too.
How to fix it properly
Decide in one place whether a given page is cacheable, and make everything else obey that decision. A single predicate the application owns — this URL is public, this one is personal — with the personalising code returning early when the answer is "public", and the cookie stripped from public responses before they leave the origin.
Two details matter more than they look. First, public responses need the caching headers and non-public responses need an explicit "private, do not cache" header — otherwise a rejected page goes out with no instruction at all and the CDN applies its own default, which is the exact outcome you were avoiding. Second, configure the CDN to respect the origin's decision rather than override it, so the application stays the final authority and the edge can never store something the code already refused.
2. Payment methods you enabled that never appear
You switch on a local payment method because it is how people in that market actually pay. It does not show up at checkout. Everything is marked enabled. Support says the configuration looks correct.
The configuration is correct. Whether a method renders is decided by a combination most teams never write down:
- Your store's currency. Some methods exist only in one currency. A method tied to a local currency cannot appear in a store that prices in euros — not as a settings problem, but structurally. We hit exactly this: a popular local method that our processor supports only in that country's own currency, in a store priced in EUR. No amount of configuration would have made it render.
- The buyer's country and the order amount. Many methods are eligibility-gated per transaction, so they appear for one basket and not another.
- Where your payment account is domiciled. Method availability differs by the country your merchant account sits in, which is usually where your company is registered — a fact chosen for tax and corporate reasons long before anyone thought about checkout.
- Which layer is actually deciding. Modern checkouts increasingly take the method list from the processor's own dashboard configuration, at which point older per-store settings are ignored while still displaying as enabled. Two sources of truth, one of them lying.
Before promising a market a payment method, verify the whole chain: currency, buyer country, account domicile, and which layer decides. "The gateway supports it" is not the same as "your store can show it" — and the gap between those two statements is where launch dates go to die.
The practical consequence for market entry is that payment method availability is a market-entry constraint, not a configuration task. If a market's dominant payment method requires pricing in the local currency, then multi-currency stops being a nice-to-have and becomes the price of entering at all. That belongs in the business case, not the build backlog.
3. Geo-aware shipping, and the way it breaks checkout
Detecting a visitor's country so you can show real shipping costs instead of "calculated at checkout" is worth doing — it removes the single biggest source of basket abandonment in cross-border retail, which is a total that changes at the last step.
It also introduces a failure mode with a nasty shape: it breaks checkout for exactly the visitors you cannot see.
- Never set a country you do not ship to. If detection assigns a visitor a country outside your served list, the checkout can end up in a state it has no valid shipping option for. Leave unsupported and unknown visitors unset and show a neutral estimate. A vague estimate is recoverable; a broken checkout is not.
- A manual choice must always win. Someone browsing from an airport, on a VPN, or buying a gift for another country will correct the guess. If your detection re-applies itself on the next page load and overrides them, you have built something worse than no detection at all.
- Detect once, not continuously. Re-running detection on every request fights the shopper and multiplies the chance of a mid-session flip.
- Any page whose content varies by country must never be edge-cached. This follows directly from the first problem — a cached page showing German shipping to a French visitor is the same class of bug as a shared session, just quieter.
There is also a testing trap worth knowing. Behind a CDN you will always see your own edge location, so you cannot test another country by browsing normally, and your staging environment will confirm everything works. The only reliable test is to send the request with the country signal set explicitly, bypassing the CDN. Until you do that, "we tested it" means "we tested one country".
4. Accept that your storefront pages are the slow ones
Once the session problem is fixed, the honest picture usually looks like this: your editorial pages — journal, story, FAQ, policies — can be cached at the edge and become extremely fast. Your home page, shop and product pages cannot, because they render a basket count and a country-dependent shipping line.
That is not a failure. It is the correct answer, and the mistake is refusing to accept it and chasing a caching strategy that will eventually cause one of the bugs above.
If you do want the storefront fast, the work is not more caching — it is removing the personal parts from the server-rendered page so the page itself becomes public, and loading the basket count and shipping estimate separately in the browser. That is a real project with a real cost, and it should be scoped as one rather than attempted as a configuration change.
Meanwhile the biggest speed wins on a store are usually elsewhere entirely: image weight, the number of third-party scripts in the checkout path, and the theme's own query count. None of them require touching the caching layer.
The pattern worth taking away
Every failure here shares a shape: something that is correct in isolation becomes wrong once it runs on every request. A geo pre-set is sensible; running it on the FAQ is not. A payment method is available; available in your currency is a different claim. Edge caching is good; edge caching a page with a basket count on it is a bug with a security dimension.
The discipline that catches all of them is the same: know, explicitly and in one place, which of your pages are public and which are personal — and test the personal ones as though you were somewhere else in the world, because that is where they break.
That knowledge is why we run our own properties rather than only advising on other people's. If you want that applied to a build, our website development work is where it lives.