Prevent Fastcgi_cache from caching specific functions or WP shortcodes

We are writing a geolocation plugin which uses the MaxMind.com Web Service API to get location data about a user based on their IP address (Country, State/Province, City, Zip/Postal Code, etc.).

We want to display this same information to the user when they visit our site (running on WP Multisite w/ Subdomains + Domain Mapping, powered by EasyEngine)

Our code uses the built-in wp_remote_get() function to request data from Maxmind.com, and then saves the data in a cookie in the user’s browser.

We are then using simple WordPress shortcodes like [country] or [state] or [city] to display the information to the user as desired.

If the user is in California, the [state] shortcode should return “California”.
If the user is in New York, the [state] shortcode should return “New York”.

THE PROBLEM: CACHING
Our problem is that the entire page is being cached by NGINX, including the request, the cookie, etc. Users are not seeing dynamic data about themselves. Instead, they are seeing cached data from a prior user.

Several times now, I have visited the site and seen information that was totally different than mine.

I deactivated the W3TC plugin, so the only caching mechanism left is the Fastcgi_cache that EasyEngine implements.

POSSIBLE SOLUTIONS (AND CHALLENGES)

  1. Disable Fastcgi_cache - which is not good if the site begins to get a lot of traffic.

  2. Modify NGINX config to skip cache based on a certain rule - which is difficult because I cannot come up with a valid rule.

  3. Use JavaScript/AJAX to dynamically replace the shortcodes on the page, and do not use built-in WordPress do_shortcode() function.

Can you provide some insights here as to the recommended method to move forward?

1 Like

To expand on my solution #2 above, I read in this article the following

Content can also be excluded from caching based on the request method, URL, cookies, or any other server variable.

I wish I could skip cache using one of these rules, but there are challenges:

  • Request method - We're just using a standard GET (not POST) to load the page
  • Query string - Not using a query string
  • Cookies - First-time user has no cookies when they first load the site/page

One more note…
We’re outputting the user’s [state] in the WP theme header. So the shortcode needs to either read from saved cookie or do API lookup on every page load.

Assuming cookie in which state name is stored is called “state” itself.

You can add this cookie to fastcgi cache key as a parameter.

By default your nginx config will have something like:

  
fastcgi_cache_key "$scheme$request_method$host$request_uri";  

Just change it to:

  
fastcgi_cache_key "$scheme$request_method$host$request_uri$cookie_state";  

This will create cached version of page for every unique state value. Users from same state will benefit from cache.

If you want get city involved, you can create:

  
fastcgi_cache_key "$scheme$request_method$host$request_uri$cookie_state$cookie_city";  

This will create a lot more versions for every page. Nginx can handle them nicely.

Only issue - nginx-helper cannot purge all cached version when a page gets modified. So we need to purge entire cache.

We are using this method on a large where entire site content changes based on marketplace. But we have 8-9 marketplaces (very limited).

In your case, at city-level, cache may get fragmented as it will benefit returning users from same city only.

Ideally, I would have solved this using javascript on client side. I would have maintained single cached copy of a page with shortcode as placeholder tags. Then fetched county, state and city using maxmind’s javascript API in browser.

If you need server side solution strictly, you may use nginx’s geoip module - http://nginx.org/en/docs/http/ngx_http_geoip_module.html and substituion module - http://nginx.org/en/docs/http/ngx_http_sub_module.html

Please let me know which you take and if you need any additional inputs in getting it working.

Very cool, thanks for the helpful insights, Rahul! I didn’t know you could add a cookie to the fastcgi cache key as a parameter. I also didn’t know there was an NGINX module for GeoIP and Substitution.

In the end, we chose to use MaxMind’s javascript API in browser and maintain a single cached copy of each page with shortcodes – exactly like you’ve suggested.

The only caveat was that we couldn’t use the WordPress do_shortcode() function, since that happens server-side (and therefore gets cached). Instead, we’re just doing a simple jQuery find-and-replace for each of these strings:

  • [state]
  • [city]
  • [zipcode]

The easiest solution we found was using the jQuery replaceText plugin, which allows the following code:

  
jQuery('body *').replaceText( '[city]', data.cityName );  

Great. :slight_smile:

Your jQuery solution is the best one in this case.

Thanks for sharing your workaround. :slight_smile:

@nick1 @rahul286 This is a great post!

I got into the exact same situation. I want to display the city on the home page based on client’s ip address. I used shortcode. Currently it only displays the last visitor’s city, not mine. I don’t have commercial MaxMind GeoIP2 license, but use Nginx geoip module instead.

I am fairly new to wordpress. Would you mind to tell me where can I put the following code into wordpress

jQuery(‘body *’).replaceText( ‘[city]’, data.cityName );

In my case, it would be

jQuery(‘body *’).replaceText( ‘[city]’, getenv(‘GEOIP_CITY’) );

I tried to put the code above to the active theme’s functions.php. But it breaks the whole blog.

Can anyone please help?

up up up