{"id":396375,"date":"2026-11-04T11:50:00","date_gmt":"2026-11-04T11:50:00","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/wonderful-geo-page-cache\/"},"modified":"2026-11-04T15:30:27","modified_gmt":"2026-11-04T15:30:27","slug":"wonderful-geo-page-cache","status":"publish","type":"plugin","link":"https:\/\/wordpress.org\/plugins\/wonderful-geo-page-cache\/","author":23357214,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"1.0.8","stable_tag":"1.0.8","tested":"7.1.4","requires":"6.9","requires_php":"7.4","requires_plugins":null,"header_name":"Wonderful Geo Page Cache","header_author":"Wonderful Plugins","header_description":"Full-page cache that keeps one copy per visitor country and per cookie you name \u2014 so shops with country-dependent VAT prices or geo redirects can be cached without showing anyone the wrong price.","assets_banners_color":"b9bbff","last_updated":"2026-11-04 15:30:27","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"https:\/\/wonderfulplugins.eu\/wonderful-geo-page-cache","header_author_uri":"https:\/\/wonderfulplugins.eu","rating":0,"author_block_rating":0,"active_installs":0,"downloads":111,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"1.0.5":{"tag":"1.0.5","author":"wonderfulplugins","date":"2026-11-04 11:49:29","revision":3783834},"1.0.6":{"tag":"1.0.6","author":"wonderfulplugins","date":"2026-11-04 12:03:55","revision":3783870},"1.0.7":{"tag":"1.0.7","author":"wonderfulplugins","date":"2026-11-04 12:32:44","revision":3783936},"1.0.8":{"tag":"1.0.8","author":"wonderfulplugins","date":"2026-11-04 15:30:27","revision":3784249}},"upgrade_notice":[],"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3783834,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-150x150.png":{"filename":"icon-150x150.png","revision":3783834,"resolution":"150x150","location":"assets","locale":"","width":150,"height":150},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3783834,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256},"icon.svg":{"filename":"icon.svg","revision":3783834,"resolution":false,"location":"assets","locale":false}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3783834,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3783834,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["1.0.5","1.0.6","1.0.7","1.0.8"],"block_files":[],"assets_screenshots":{"screenshot-1-de.jpg":{"filename":"screenshot-1-de.jpg","revision":3784249,"resolution":"1","location":"assets","locale":"de","width":1375,"height":1275},"screenshot-1.jpg":{"filename":"screenshot-1.jpg","revision":3784249,"resolution":"1","location":"assets","locale":"","width":1375,"height":1275},"screenshot-2-de.jpg":{"filename":"screenshot-2-de.jpg","revision":3784249,"resolution":"2","location":"assets","locale":"de","width":1600,"height":800},"screenshot-2.jpg":{"filename":"screenshot-2.jpg","revision":3784249,"resolution":"2","location":"assets","locale":"","width":1600,"height":800}},"screenshots":{"1":"Settings: cached pages, lifetime, prewarming, visitor country, and which cookies and URL parameters change a page.","2":"Toolbar menu: cached pages, last prewarm, switch the cache off, empty it."}},"plugin_section":[],"plugin_tags":[146,4124,3853,19126,286],"plugin_category":[45,49,52],"plugin_contributors":[247385],"plugin_business_model":[],"class_list":["post-396375","plugin","type-plugin","status-publish","hentry","plugin_tags-cache","plugin_tags-geolocation","plugin_tags-page-cache","plugin_tags-vat","plugin_tags-woocommerce","plugin_category-ecommerce","plugin_category-maps-and-location","plugin_category-performance","plugin_contributors-wonderfulplugins","plugin_committers-wonderfulplugins"],"banners":{"banner":"https:\/\/ps.w.org\/wonderful-geo-page-cache\/assets\/banner-772x250.png?rev=3783834","banner_2x":"https:\/\/ps.w.org\/wonderful-geo-page-cache\/assets\/banner-1544x500.png?rev=3783834","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":"https:\/\/ps.w.org\/wonderful-geo-page-cache\/assets\/icon.svg?rev=3783834","icon":"https:\/\/ps.w.org\/wonderful-geo-page-cache\/assets\/icon.svg?rev=3783834","icon_2x":false,"generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/wonderful-geo-page-cache\/assets\/screenshot-1.jpg?rev=3784249","caption":"Settings: cached pages, lifetime, prewarming, visitor country, and which cookies and URL parameters change a page."},{"src":"https:\/\/ps.w.org\/wonderful-geo-page-cache\/assets\/screenshot-2.jpg?rev=3784249","caption":"Toolbar menu: cached pages, last prewarm, switch the cache off, empty it."}],"raw_content":"<!--section=description-->\n<p>https:\/\/www.youtube.com\/watch?v=WEbKAD3ByMg<\/p>\n\n<p>Most page caches store one copy of a page. That breaks every site that renders a page differently per country: a WooCommerce shop that shows prices including the VAT rate of the visitor's country, or a site that sends first-time visitors from abroad to its English version. Cache such a site and every visitor sees the prices of whoever happened to fill the cache.<\/p>\n\n<p><strong>Wonderful Geo Page Cache<\/strong> keeps a separate copy per visitor country, looked up from the GeoLite2-Country database WooCommerce already maintains, and per value of the cookies you name. A visitor from Germany gets the copy rendered for Germany, a visitor from Austria the one for Austria \u2014 and bots and scanners, who hit the same pages over and over, cost about a millisecond instead of a full WordPress render.<\/p>\n\n<h4>What makes it different<\/h4>\n\n<ul>\n<li><strong>One copy per country<\/strong> \u2014 no country lookup in JavaScript, no flash of wrong prices, no \"geolocate with page caching\" URL parameters.<\/li>\n<li><strong>Cookies that vary the page<\/strong> \u2014 name the cookies your site uses for a chosen country or language; each value gets its own copy.<\/li>\n<li><strong>A country the visitor chose<\/strong> \u2014 when your site keeps a chosen delivery country in a cookie, name it as the country cookie: a visitor who picked Austria shares the copy of visitors from Austria instead of getting one of their own.<\/li>\n<li><strong>Cookies that count only when set<\/strong> \u2014 a \"first visit\" marker your site only checks for gives one copy with and one without it, whatever its value.<\/li>\n<li><strong>Cookies that may be replayed<\/strong> \u2014 a \"first visit\" cookie the site sets identically for everyone is stored with the page and sent again, so first visits are cached too. A response that sets any other cookie is never stored.<\/li>\n<li><strong>Redirects are cached<\/strong> \u2014 a language or country redirect for first-time visitors is stored like a page.<\/li>\n<li><strong>Safe against cache poisoning<\/strong> \u2014 requests whose client address is ambiguous (several forwarded addresses) are not cached, so a page rendered for one country cannot be stored for another.<\/li>\n<li><strong>Pages stay cached until something changes<\/strong> \u2014 the security tokens (nonces) WordPress puts into a page are taken out when it is stored and computed afresh every time it is served, so a cached page never carries an expired token.<\/li>\n<li><strong>Prewarm<\/strong> \u2014 after the cache was emptied, the plugin requests again the pages and variants visitors used in the last 30 days and every other public page, in every language of WPML or Polylang, so visitors rarely meet a page that has to be rendered first. What people use decides what is warmed; crawlers do not.<\/li>\n<\/ul>\n\n<h4>How it works<\/h4>\n\n<ul>\n<li>An <code>advanced-cache.php<\/code> drop-in answers cached requests before WordPress loads.<\/li>\n<li>Pages are captured with the template output buffer of WordPress 6.9 and later.<\/li>\n<li>Visitors with a login, cart or session cookie, POST requests, URLs with unknown parameters, cart, checkout and account pages are never cached.<\/li>\n<li>Visitors with a cart can be allowed to read the cache: move the WooCommerce cart cookies to \"Cookies that only read the cache\" and they get cached pages, while pages rendered for them are never stored. Use it only when your mini cart refreshes after the page has loaded (WooCommerce cart fragments).<\/li>\n<li>The whole cache is emptied when content, menus, widgets, stock, scheduled sales, tax rates or plugins change, and through \"Empty page cache\" in the toolbar menu \"Geo Page Cache\". Otherwise pages stay until a lifetime you set, or for good with lifetime 0.<\/li>\n<li>Nonces are renewed with the keys from wp-config.php. Every nonce is checked against WordPress when a page is stored; if one cannot be rebuilt \u2014 or the page contains a nonce WordPress did not report \u2014 that page keeps its nonces and expires after 12 hours.<\/li>\n<\/ul>\n\n<!--section=installation-->\n<ol>\n<li>Install and activate the plugin.<\/li>\n<li>Add <code>define( 'WP_CACHE', true );<\/code> to <code>wp-config.php<\/code>.<\/li>\n<li>Go to Settings \u2192 Geo Page Cache, name the cookies and URL parameters your site varies on, and enable the cache.<\/li>\n<li>Check a page with your browser's developer tools: the response header <code>X-Wonderful-Geo-Page-Cache<\/code> says <code>MISS<\/code> on the first request and <code>HIT<\/code> on the next one.<\/li>\n<\/ol>\n\n<!--section=faq-->\n<dl>\n<dt id=\"where%20does%20the%20country%20come%20from%3F\"><h3>Where does the country come from?<\/h3><\/dt>\n<dd><p>From a GeoLite2-Country database on your server. If WooCommerce geolocation is enabled, its database is used automatically; otherwise enter the path of any MaxMind DB file. Nothing is sent to an external service.<\/p><\/dd>\n<dt id=\"my%20server%20sits%20behind%20a%20proxy.\"><h3>My server sits behind a proxy.<\/h3><\/dt>\n<dd><p>If the web server already puts the client address into <code>REMOTE_ADDR<\/code> (mod_remoteip, as in the official WordPress Docker image), keep <code>REMOTE_ADDR<\/code> \u2014 mod_remoteip removes the forwarded header it has read. Otherwise choose the header that carries the client address (for example <code>HTTP_X_FORWARDED_FOR<\/code>). If the configured source is missing on a request, that request is not cached rather than filed under an unknown country.<\/p><\/dd>\n<dt id=\"how%20does%20prewarming%20render%20a%20page%20for%20another%20country%3F\"><h3>How does prewarming render a page for another country?<\/h3><\/dt>\n<dd><p>The prewarmer requests the page from the site itself with a header signed by a key derived from the salts in <code>wp-config.php<\/code>, which only the site knows and which is never written to a file. The drop-in accepts that header only with a valid signature and then treats the request as coming from an address in the wanted country (taken from the GeoLite2 database), so WordPress renders exactly what a visitor from there would get. It sends at most two requests a second.<\/p><\/dd>\n<dt id=\"where%20are%20cached%20pages%20stored%3F\"><h3>Where are cached pages stored?<\/h3><\/dt>\n<dd><p>In <code>wp-content\/uploads\/wonderful-geo-page-cache\/<\/code>: the cached pages, the configuration the drop-in reads and the prewarm state, all as plain data files, never as code. An <code>.htaccess<\/code> file closes the folder to web requests on Apache and LiteSpeed. On nginx, add a rule such as <code>location ^~ \/wp-content\/uploads\/wonderful-geo-page-cache\/ { deny all; }<\/code>.<\/p><\/dd>\n<dt id=\"does%20it%20work%20with%20another%20cache%20plugin%3F\"><h3>Does it work with another cache plugin?<\/h3><\/dt>\n<dd><p>No. Only one plugin can own <code>advanced-cache.php<\/code>; the plugin never overwrites another plugin's drop-in and tells you when one is installed.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>1.0.8<\/h4>\n\n<ul>\n<li>Video in the readme, new screenshots in English and German.<\/li>\n<\/ul>\n\n<h4>1.0.7<\/h4>\n\n<ul>\n<li>The cache is switched on and off with a toggle in the first row of the settings page and in the toolbar menu, right away and without saving the form; switching it off ends a running prewarm.<\/li>\n<li>The settings checkboxes are shown as toggles.<\/li>\n<\/ul>\n\n<h4>1.0.6<\/h4>\n\n<ul>\n<li>New setting \"Cookies that only read the cache\": visitors carrying one of these cookies, such as the WooCommerce cart cookies, get cached pages, but pages rendered for them are never stored. Pages with nonces WooCommerce ties to the session are rendered fresh for them.<\/li>\n<\/ul>\n\n<h4>1.0.5<\/h4>\n\n<ul>\n<li>The update to 1.0.4 no longer empties the cache when it adds the new ignored parameters.<\/li>\n<\/ul>\n\n<h4>1.0.4<\/h4>\n\n<ul>\n<li>Cached pages, configuration and prewarm state are stored as plain data files instead of PHP files; the files of earlier versions are converted or removed on the first request after the update.<\/li>\n<li>Prewarm requests are signed with a key derived from the salts in wp-config.php instead of a secret stored in the database and in the configuration file.<\/li>\n<li>Google Ads parameters (gad_source, gad_campaignid) and other click IDs (dclid, yclid, twclid, ttclid, li_fat_id, igshid, HubSpot, Marketo) are ignored by default, so ad clicks get the cached page; sites with the default list get them added on update.<\/li>\n<\/ul>\n\n<h4>1.0.3<\/h4>\n\n<ul>\n<li>Prewarm: only requests with a cookie set by the site's scripts (consent banner, analytics, language switcher) count as people; crawlers that keep the site's own cookies no longer decide which variants are warmed.<\/li>\n<\/ul>\n\n<h4>1.0.2<\/h4>\n\n<ul>\n<li>Country cookie: visitors without the cookie now share the copy of visitors who chose their country, as intended.<\/li>\n<li>Prewarm: a run whose batches stopped handing over is continued by the next request WordPress handles, instead of waiting for the hourly cleanup; the hand-over request gets enough time for HTTPS.<\/li>\n<li>The settings show when and by what the cache was last emptied.<\/li>\n<li>The daily scheduled-sales event no longer empties the cache; products whose sale starts or ends still do.<\/li>\n<li>Prewarm queries every language of WPML or Polylang on its own, so pages that exist in one language only are warmed too.<\/li>\n<\/ul>\n\n<h4>1.0.1<\/h4>\n\n<ul>\n<li>New \"Country cookie\": a country code the visitor chose (for example in a delivery country banner) replaces the address lookup, so these visitors share the copy of their country.<\/li>\n<li>New \"Cookies that count only when set\": one copy with and one without such a cookie, whatever its value.<\/li>\n<li>Prewarm also warms the translations of every page with WPML and Polylang.<\/li>\n<li>Prewarm follows people: requests from crawlers are no longer recorded, requests of returning browsers come first, and a first visit from the shop country is always warmed.<\/li>\n<\/ul>\n\n<h4>1.0.0<\/h4>\n\n<ul>\n<li>First stable release: page cache per visitor country and cookie, nonces renewed on every delivery, prewarming, toolbar menu.<\/li>\n<li>Settings: cached pages and \"Empty page cache\" sit in the settings table.<\/li>\n<\/ul>\n\n<h4>0.9.5<\/h4>\n\n<ul>\n<li>\"Cached pages\" instead of \"Cached responses\"; the toolbar shows a spinner and live progress while prewarming; \"Prewarm now\" removed again (runs start by themselves).<\/li>\n<\/ul>\n\n<h4>0.9.4<\/h4>\n\n<ul>\n<li>Toolbar menu \"Geo Page Cache\": cached pages and their size, prewarm progress with a spinner that updates while a run is in progress, \"Empty page cache\" and the settings.<\/li>\n<\/ul>\n\n<h4>0.9.3<\/h4>\n\n<ul>\n<li>Prewarm right after installation: before any visitor was seen, every public page is warmed for a first visit from the shop's own country.<\/li>\n<\/ul>\n\n<h4>0.9.2<\/h4>\n\n<ul>\n<li>Nonces are renewed on every delivery, so pages can stay cached until something changes (lifetime 0, the new default).<\/li>\n<li>Prewarm: refills the cache in the background after it was emptied.<\/li>\n<li>The visitor country is read from the \"country\" field only, like WooCommerce does.<\/li>\n<\/ul>\n\n<h4>0.9.1<\/h4>\n\n<ul>\n<li>The settings menu entry is now called \"Geo Page Cache\"; the settings page keeps the full name.<\/li>\n<\/ul>\n\n<h4>0.9.0<\/h4>\n\n<ul>\n<li>First public version: one cached copy per visitor country and per named cookie, cached redirects, replayed first-visit cookies, purge on content, product, stock and tax changes.<\/li>\n<\/ul>","raw_excerpt":"Page cache with one copy per visitor country and per named cookie, for shops whose VAT prices or redirects depend on the visitor's country.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/396375","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=396375"}],"author":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/wonderfulplugins"}],"wp:attachment":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=396375"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=396375"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=396375"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=396375"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=396375"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=396375"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}