Skip to content

Instantly share code, notes, and snippets.

@edisoncosta
Forked from rahul286/moved.md
Created March 27, 2016 17:54
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save edisoncosta/6d512411921771b38dd8 to your computer and use it in GitHub Desktop.
Save edisoncosta/6d512411921771b38dd8 to your computer and use it in GitHub Desktop.
woo-commerce fastcgi-cache session-conflict solution (attempt)

You can jump to code directly

How it works?

1. Cache product pages

Following line does't have /products.* page. This tells Nginx to cache all product pages by default.

Idea is fast loading product pages will improve scalability of a store and also conversion.

	# Don't cache uris containing the following segments
	if ($request_uri ~* "(/shop.*|/cart.*|/my-account.*|/checkout.*|/addons.*|/wp-admin/|/xmlrpc.php|wp-.*.php|/feed/|index.php|sitemap(_index)?.xml|[a-z0-9_-]+-sitemap([0-9]+)?.xml)") {
		set $skip_cache 1;
	}

2. Stop caching as soon as a visitor adds something to cart

WooCommerce uses a cookie woocommerce_items_in_cart to track if something is added to cart. It is 0 (zero) by default. The value changes to "1" (one) as soon as a visitor add first product to cart. The value stays "1" even if a user adds more items to cart. The cookie is used as binary flag.

	if ( $cookie_woocommerce_items_in_cart = "1" ){
		 set $skip_cache 1;
	}

Above code snippet starts skipping cache as soon as first item is added to the cart.

3. Prevent unique WooCommerce session for "window shoppers"

WooCommerce sets a session cookie for every new visitor by default.

If a cookie header get's cached in Nginx's fastcgi-cache, that cookie will be delivered to all visitors creating "session-conflict" or "cart-conflict".

In simple words, it he do not take care of cookies, items added by one visitor might appear in another visitors cart.

So we need to prevent cookies for "window shoppers".

more_clear_headers is a NGINX module which clears Set-Cookie header from upstream PHP/WordPress' HTTP response.

		if ($skip_cache = 0 ) {
			more_clear_headers "Set-Cookie*";
			set $rt_session "";
		}

4. Keep track of WooCommerce sessions for "real shoppers"

The third line in above code block has a variable $rt_session. This is a custom NGINX variable which will have null (same) value when cached pages are supposed to be served.

The same value is acceptable because all cached pages will look same. The custom variable is used inside NGINX fastcgi-cache key on line:

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

The custom variable $rt_session is used further to set to a unique value for each shopping session. This is needed because different users might have different cart content. So they must not see each other's cache.

This is done by following lines:

if ($http_cookie ~* "wp_woocommerce_session_[^=]*=([^%]+)%7C") {
			set $rt_session wp_woocommerce_session_$1;
	}

Above basically set $rt_session to wp_woocommerce_session_ id.

This variable is set on each request, but line set $rt_session ""; in previous step ensures we do not have unique cache key for "window shoppers". This avoids cache fragmentation.

Notes

  1. This technique might break some WooCommerce addons which depends on wp_woocommerce_session from beginning. In reality I haven't seen a single addon breaking. But I hardly tested a dozen WooCommerce addons.

TODO

  1. Simplify.
  2. Test it with redis full-page cache.
# WPSINGLE BASIC NGINX CONFIGURATION
server {
server_name woo.rtcamp.net www.woo.rtcamp.net;
access_log /var/log/nginx/woo.rtcamp.net.access.log rt_cache;
error_log /var/log/nginx/woo.rtcamp.net.error.log debug;
root /var/www/woo.rtcamp.net/htdocs;
index index.php index.htm index.html;
fastcgi_cache_use_stale error timeout invalid_header http_500;
set $skip_cache 0;
# POST requests and urls with a query string should always go to PHP
if ($request_method = POST) {
set $skip_cache 1;
}
if ($query_string != "") {
set $skip_cache 1;
}
if ( $cookie_woocommerce_items_in_cart = "1" ){
set $skip_cache 1;
}
# Don't cache uris containing the following segments
if ($request_uri ~* "(/shop.*|/cart.*|/my-account.*|/checkout.*|/addons.*|/wp-admin/|/xmlrpc.php|wp-.*.php|/feed/|index.php|sitemap(_index)?.xml|[a-z0-9_-]+-sitemap([0-9]+)?.xml)") {
set $skip_cache 1;
}
# Don't use the cache for logged in users or recent commenters
if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in") {
set $skip_cache 1;
}
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
set $rt_session "";
if ($http_cookie ~* "wp_woocommerce_session_[^=]*=([^%]+)%7C") {
set $rt_session wp_woocommerce_session_$1;
}
if ($skip_cache = 0 ) {
more_clear_headers "Set-Cookie*";
set $rt_session "";
}
fastcgi_cache_key "$scheme$request_method$host$request_uri$rt_session";
try_files $uri =404;
include fastcgi_params;
fastcgi_pass php;
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
fastcgi_cache WORDPRESS;
fastcgi_cache_valid 60m;
}
include /etc/nginx/common/wpcommon.conf;
include /etc/nginx/common/locations.conf;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment