WooCommerce Geolocation Shows the Wrong Currency: a Checklist
When WooCommerce geolocation shows the wrong currency, the GeoIP rules in FOX are rarely the broken part. The currency for a new visitor is the end of a short chain: WooCommerce works out the country from the IP address, FOX looks that country up in its GeoIP rules, and the result is stored for the visitor. A wrong currency means one link of that chain failed: the country was not detected, the country was detected but did not match a rule, or the visitor never reached the code at all because a cache answered first. The checks below find which link it is, in that order.
How FOX chooses a currency for a new visitor
FOX has no geolocation database of its own. It calls WC_Geolocation::geolocate_ip(), the same function WooCommerce uses for tax and shipping estimates, and gets back a two-letter country code. Then it walks through the lists in the GeoIP tab of its settings (WooCommerce → Settings → Currency): the first currency whose list contains that country becomes the visitor's currency. If no list contains it, the visitor stays on the Welcome currency from the Options tab. Setting the rules up from scratch is covered in automatically change currency by country; this page is about finding out why they do not work.
Three details of this logic explain most "it does not work" reports:
- The rules apply once, on the first visit. After that FOX stores the visitor's currency and uses it on every page, so a visitor who switched currency by hand keeps their choice. Changing the rules later does not touch visitors who already have a stored currency.
- The first visit is marked as used even if no country was detected. If the very first page view could not geolocate the visitor, the rules are not tried again on the second page. The visitor stays on the welcome currency until their stored data expires.
- The decision is made in PHP, while WordPress builds the page. A page served as finished HTML from a cache does not run it.
Step 1: see what WooCommerce detects
Create a draft page with these two shortcodes and exclude it from every cache (caching plugin, server cache, CDN):
[woocs_geo_hello] [woocs_show_current_currency]
[woocs_geo_hello] asks WooCommerce for the country of the current request and prints Your country is: ..., or Your country is not defined! Troubles with GeoIp service. when WooCommerce returns nothing. [woocs_show_current_currency] prints the currency FOX is using for you. The page must stay uncached: a cached copy of it shows the country of whoever loaded it first.
Open the page as a genuinely new visitor. A private window is not always enough, because what counts as "new" depends on Currency storage in the Options tab:
- PHP Session and FOX Session keep the data behind a browser cookie, so a fresh private window is a new visitor.
- Transient, Memcached and Redis key the data by IP address. Every browser on the same connection is the same visitor, and a VPN address that you or anyone else already used for a test is not new either. Test from another connection, such as a phone on mobile data, or switch to PHP Session while you test.
| What the test page shows | Where the problem is |
|---|---|
| Country not defined | WooCommerce cannot geolocate at all |
| A wrong country, often the shop's or the server's | Wrong IP address reaches WooCommerce, or the server caches the location |
| Right country, wrong currency, on a brand-new visit | The GeoIP rules |
| Right country and currency here, wrong currency on shop pages | Page cache |
No country, or the wrong country
This part is WooCommerce and the server, and FOX can only use what they return.
MaxMind is not set up
WooCommerce looks the IP address up in the free MaxMind GeoLite2 database, which it downloads only when you give it a key. Create a free MaxMind account, generate a key and paste it into WooCommerce → Settings → Integration → MaxMind Geolocation. Set Default customer location in WooCommerce → Settings → General to one of the two Geolocate options, because WooCommerce keeps the database downloaded and updated only when geolocation is in use. Without the database WooCommerce falls back to public lookup services with a two-second timeout, which can explain a country that is detected on some requests and missing on others.
The server passes its own IP address
WooCommerce takes the visitor's address from the X-Real-IP header first, then the first address in X-Forwarded-For, then REMOTE_ADDR. Behind a reverse proxy, a load balancer or Varnish, these can hold the proxy's address, and every visitor is placed in the country of the data centre. Behind Cloudflare the simplest fix is Cloudflare's own IP Geolocation setting: it adds a CF-IPCountry header, and WooCommerce reads a country from that header before it looks at any IP address. Otherwise the server must restore the real address; on nginx that is the real_ip_header and set_real_ip_from directives, which your hosting can set.
To see which address WooCommerce receives, add this temporary shortcode and put [geo_debug] on the uncached test page:
/*
* Temporary geolocation check. Child theme functions.php or a code snippets plugin.
* Remove it when you are done.
*/
add_shortcode('geo_debug', function () {
if (!class_exists('WC_Geolocation')) {
return '';
}
$geo = WC_Geolocation::geolocate_ip();
return 'IP seen by WooCommerce: ' . esc_html(WC_Geolocation::get_ip_address())
. '<br>Country: ' . esc_html($geo['country']);
});
If the IP is your server's or always the same address whoever opens the page, the proxy is the cause.
The hosting caches the location
Some managed hosts run their own page or "dynamic" cache in front of WordPress, and some cache the detected location per page. The test page then shows the same country for every visitor even though no caching plugin is installed. Ask the host to exclude your pages from that layer or to vary it by country, or test with it switched off to confirm.
Right country, wrong currency: the GeoIP rules
When the test page shows the correct country on a brand-new visit and the currency is still wrong, open the GeoIP tab:
- Empty lists. If no currency has countries assigned, FOX never switches by location and every visitor gets the welcome currency.
- A country in two lists. FOX takes the first currency, in the order of the Currencies tab, whose list contains the country. A country left in the USD list keeps those visitors on USD, whatever else it is assigned to. Pressing Select all for one currency and then assigning countries to others is an easy way to get there.
- A country that is not in any list. Shipping to a new market does not add it to the rules; add it to the right currency.
- A rule for a currency that is no longer in the shop. Rules are stored by currency code. After you remove or rename a currency in the Currencies tab, a leftover rule points to a code FOX cannot switch to, and those visitors get the base currency. Saving the settings again rewrites the rules for the current currencies only.
- The built-in EU countries profile. It lists Poland under the code
PO, which is not a country code, so applying the profile leaves Poland out. Add Poland by hand.
Two more causes sit outside the rules. Custom code that calls $WOOCS->set_currency() on every page, for example in wp_head, overrides the GeoIP choice; search the theme and snippets for it. And a currency drop-down built by the theme or a page builder is not the FOX switcher and may simply not reflect the active currency; use the [woocs] shortcode to compare.
Geolocation shows the wrong currency only on cached pages
A full page cache stores one finished copy of a page and hands it to everyone, so the first visitor's currency is shown to the next one, and the GeoIP code never runs for the visitor who got the copy. Why a currency switcher and a page cache fight each other explains the conflict in full; for geolocation these four settings work together:
- Default customer location = Geolocate (with page caching support). WooCommerce then redirects to URLs with a
?v=key made from the visitor's location, so a cache that keys pages by the full URL stores a separate copy for each location, built by a visitor from that location. The cache must not ignore thevquery parameter. - I am using cache plugin on my site in the FOX Options tab. After a cached page loads, the plugin asks the server for the visitor's stored currency and, when it differs from the one in the cached HTML, redraws the prices.
- A storage the cache cannot share between visitors. With PHP Session, add the
PHPSESSIDcookie to the cache plugin's "never cache" cookie list; with FOX Session, the cookie that starts withwp_woocs_session_. With a CDN in front of the site, Transient plus the cache option avoids cookies altogether. - Cart, checkout and account pages excluded from the cache, including custom checkout pages built by a theme or a funnel plugin.
One limit of FOX 2.5.5 matters here: the redraw request compares the currency of the cached copy with the visitor's current currency before it reaches the GeoIP step. A new visitor starts in the Welcome currency, so on pages cached in the Welcome currency the GeoIP rules are skipped and the visitor keeps it. The Cloudflare APO article gives a short snippet that runs the GeoIP step first; it works with any page cache, not only Cloudflare.
For WP Rocket specifically, FOX and WP Rocket lists the script exclusions it needs. To confirm that the cache is the cause, purge every layer (plugin, server, CDN), disable the cache for a test and repeat the first-visit test.
VPN users, border towns and the billing country
IP geolocation tells you where the connection comes out, not where the customer lives. A VPN, a corporate network, a mobile carrier that routes through a neighbouring country, or a town near a border all produce the "wrong" country, and WooCommerce reports it correctly from the IP address's point of view. Free VPNs are especially unreliable for testing, and a VPN address someone already used is not a first visit for IP-based storage.
When the currency must match the customer rather than the connection, decide it at checkout from the address the customer types. Switch currency by billing address instead of geolocation has ready code for that, and change currency by delivery country does the same for the shipping country. If you use fixed prices per currency, the Checkout by GeoIP rules option in the Advanced tab instead makes the checkout use the GeoIP currency of the visitor's country, whatever they selected while browsing.
Checklist
- The uncached test page shows the right country for a brand-new visitor.
- MaxMind key saved, Default customer location set to a Geolocate option.
- Behind a proxy or CDN, the real visitor IP or a country header reaches WordPress.
- Every target country appears in exactly one GeoIP list, and that currency exists in the shop.
- With a page cache: Geolocate (with page caching support), I am using cache plugin on my site, a per-visitor storage and checkout excluded.
- Tests made as a new visitor for the storage you use, not in a browser that already has a stored currency.
If the test page shows the right country for a new visitor and the currency is still wrong with caches disabled, open a topic on the FOX support forum with the test page link, your GeoIP rules and the storage setting, and we will look at it.