Nginx Full Page Caching

Hello,

First of all I'd like to note that using this code is at your own responsibility, therefore I sincerely recommend using some sort of backup site first as this is still in testing fase.

Known issues (feel free to help out)

- Cache can not be cleared for now, I am currently working on it but if you have any ideas on how to empty a cache directory feel free to help out. For now you can do the following:

  1. 'sudo rm -rf /etc/nginx/cache'
  2. 'sudo mkdir /etc/nginx/cache'
  3. 'sudo chmod 777 /etc/nginx/cache'

- Adding content changing buttons through code is annoying, I am looking for a way to simplify this but I havent found one yet.

Feel free to help me out with this project so that we can all enjoy it. But nonetheless, let's get right into it!

Why Nginx Full Page Caching?

Nginx full page caching basically is the same as varnish full page caching. The only two differences here are that NGinx is the reverse proxy webserver and can decide immediately wether a URL needs to be cached or not, instead of (like varnish) passing the url to a different webserver. And, the fact that it can cache large PHP queries, which will speed up the server as well.

What is the average speed you can gain?

In my testing environments I achieved results that were mindblowing, they showed that the store could achieve response times in under 100ms which resulted in page loads under 300ms!

Requirements?

- Server knowledge (Unix in particular, this was also the OS I used for testing)

- Some coding skills (for linking the buttons to javascript code)

- NGinx as your webserver

- CS-Cart 4.5.2 (only version that was tested, it might be compatible with older versions as well).

- MAKE SURE THAT THE URL'S YOU USE ARE LANGUAGE DEPENDENT. EG.

- demostore.com/en/beautiful_categorie/product-en

- demostroe.com/de/schoene_kategorie/produkt-de

- demostore.com/nl/mooie-categorie/product-nl

Make CS-Cart ready!

1. Upload the provided package

2. Add the following class 'nginx-no-cache' to buttons who can change content (think about buttons who will cause a permanent change in the page, e.g. login button.)

3. Install some sort of cookie finder on the page and check if the cookie 'no-cache' is there whenever you press a button, or do anything on the page (this is very important, otherwise clients might receive old cache).

Make NGinx ready!

1. Make a backup of your config file (etc/nginx/sites-available/default)

2. Make the cache directory: (sudo mkdir /etc/nginx/cache) and give it 777 permissions.

3. Add the following code to the top of the file (!!!! not inside the 'server {' !!!!) (You can change the inactive time in case you want to)

fastcgi_cache_path /etc/nginx/cache levels=1:2 keys_zone=microcache:1024m inactive=2h;
add_header X-Cache $upstream_cache_status;

4. Make sure to add the following nginx config in the 'server {'. Furthermore you should add your own exception URL's (THIS IS VERY IMPORTANT, IF YOU HAVE LANGUAGES SHARING THE SAME URL INCLUDE IT HERE), this is very important as I do not know what seo changes you might have done

    #   Cache everything by default
    set $no_cache 1;
#   Only cache GET requests
if ($request_method != GET){
    set $no_cache 1;
}

#   Don't cache the following URLs
if ($request_uri ~* "/(YOUR ADMIN URL|dispatch=checkout.checkout|dispatch=checkout.cart)"){
    set $no_cache 1;
}

#   Don't cache peeps with cookies (addon)
if ($http_cookie ~* "no_cache"){
    set $no_cache 1;
}

5. Now we have to add something to the following segment:

    #   Processing PHP scripts
    location ~ \.php$ {

It should look like this in the end

# Processing PHP scripts
location ~ \.php$ {     
        root /data/www/****;
        proxy_read_timeout 61;
        fastcgi_read_timeout 61;
        try_files $uri $uri/ =404;
    #   The path to the PHP-FPM daemon socket
    fastcgi_pass unix:/run/php/php7.1-fpm.sock;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include fastcgi_params;

    # fastcgi caching
    fastcgi_cache microcache;
    fastcgi_cache_key $scheme$host$request_uri$request_method;
    fastcgi_cache_valid 200 301 302 3h;
    fastcgi_cache_use_stale updating error timeout invalid_header http_500;

    fastcgi_pass_header Set-Cookie;
    fastcgi_pass_header Cookie;
    fastcgi_ignore_headers Cache-Control Expires Set-Cookie;

    fastcgi_cache_bypass $no_cache;
    fastcgi_no_cache $no_cache;

}

6. Now save the config file (and double check it)

7. Restart nginx (sudo service nginx restart)

What will the final version include?

1. Cache will be cleared whenever you want to (just like normal, demostore.com/admin.php?cc&ctpl).

2. Buttons will be manageable through a nice UI in the backend.

3. Toggle the nginx proxy cache through the settings menu

4. And many more things to come!

Some final words

First of all I'd like to thank hungryweb for sharing some of his knowledge with me. Secondly, I'd like to remark that this addon is in beta and that I will release an official version with many more features soon. Feel free to help me out developing it and give me any tips and tricks! I ofcourse will be happy to assist you if you have any questions regarding this topic!

Have a good one,

PoppedWeb (and HungryWeb)

pp_nginx_cache.zip

Should the ".well-known" folder be $no_cache as well? This is the folder CERTS use for ACME challenges (ie letsencrypt/certbot) so fresh hashes are required on re-up.

Also another thought: perhaps $no_cache the "PageSpeed=off" querystring (yes it uses caps lol), and make another for dev/debug called "fcgi_cache=off".

Should the ".well-known" folder be $no_cache as well? This is the folder CERTS use for ACME challenges (ie letsencrypt/certbot) so fresh hashes are required on re-up.

Also another thought: perhaps $no_cache the "PageSpeed=off" querystring (yes it uses caps lol), and make another for dev/debug called "fcgi_cache=off".

Thanks for the extra information. I will add the .well-know folder as well to the $no_cache, I will add support for PageSpeed and I will add an option to enable / disable dev / debug mode.

Also I'd like to say that the next update (which can only be purchased) will include 'hole-punching' similar to the LiteMage cache addon for Magento. It will also include an solution for clearing the cache through the CS-Cart backend and many more new features.

Sweet, thank you for the inclusion.

In regards to hole punching in a different regard, with .well-known there should be a port 80 route specifically for that in an eventual nginx conf. $no_cache is one thing, but if you are going to be offering an eventual config template, be sure to include a portal via port 80 to the non-www version of the domain, exclusively for the .well-known folder (or others such as the cpanel comodo-thing). Like a whitelist.

Are you eventually planning a github? If so, I would like to help your addon/project out when we encounter similar spots in our servers :)

Sweet, thank you for the inclusion.

In regards to hole punching in a different regard, with .well-known there should be a port 80 route specifically for that in an eventual nginx conf. $no_cache is one thing, but if you are going to be offering an eventual config template, be sure to include a portal via port 80 to the non-www version of the domain, exclusively for the .well-known folder (or others such as the cpanel comodo-thing). Like a whitelist.

Are you eventually planning a github? If so, I would like to help your addon/project out when we encounter similar spots in our servers :)

Hello,

Eventually I do want to do this but I want to make it exclusive to buyers and / or current clients of mine. And If I do happen to come across any problems I will be sure to contact you!

Best wishes,

Thanks a lot!

Let me also add, that it can work easily with apache being main server and Nginx merely being a proxy caching server. It would just require adding some proxy_pass directives, but the core solution (eg. deciding which requests should be cached) would be the same.

Okay lets do a quick status update,

So first of all, all the hooks are in place and the writing / reading of the cache seems to be stable now. The performance gain seems to be starting at the 25% mark and goes up all the way till 65% on category pages. I however do have a few questions for the forum members.

1. Would you be interested in this solution if it also supports it without using NGINX?

2. What would be the maximim price for you if you would want to purchase this?

3. How many blocks would you cache? Would the option to cache grids also be welcome?

4. Would you be willing to change core file to reach optimum performance if neccessary (this is only in case you want to make it ~ 5% faster, though the performance gain is a fair amount already)?

Thanks a lot!

Let me also add, that it can work easily with apache being main server and Nginx merely being a proxy caching server. It would just require adding some proxy_pass directives, but the core solution (eg. deciding which requests should be cached) would be the same.

Yes that is true, Apache would we supported as well.

Okay lets do a quick status update,

So first of all, all the hooks are in place and the writing / reading of the cache seems to be stable now. The performance gain seems to be starting at the 25% mark and goes up all the way till 65% on category pages. I however do have a few questions for the forum members.

1. Would you be interested in this solution if it also supports it without using NGINX?

2. What would be the maximim price for you if you would want to purchase this?

3. How many blocks would you cache? Would the option to cache grids also be welcome?

4. Would you be willing to change core file to reach optimum performance if neccessary (this is only in case you want to make it ~ 5% faster, though the performance gain is a fair amount already)?

Yes that is true, Apache would we supported as well.

Here's my take:

1) I'm not clear currently about drawbacks of using Apache vs Nginx as a main webserver. I like nginx better, but some hosts only offer apache, so both options would be ideal.

2) The varnish plugin seems to be rather expensive, I'd say ~100 USD would be reasonable.

3) I'd probably cache almost everything, except for the login box with user's details (the only personalised part).

4) Yes, if needed, 5% doesn't seem worthy though.

Here's my take:

1) I'm not clear currently about drawbacks of using Apache vs Nginx as a main webserver. I like nginx better, but some hosts only offer apache, so both options would be ideal.

2) The varnish plugin seems to be rather expensive, I'd say ~100 USD would be reasonable.

3) I'd probably cache almost everything, except for the login box with user's details (the only personalised part).

4) Yes, if needed, 5% doesn't seem worthy though.

Okay, so after a brief testing period I have added the following features (and removed a few);

[+] Full page caching works insanely good, TTFB is now only 16 m/s. (This is not on a local server by the way, but on a fully fledged web server almost 100 miles (not kilometres) from my office).

[+] No manual core file changes are needed, only 1 rule of code is edited (an argument is added to a hook).

[+] Same applies for server configuration, this is not needed. Just install it and you are ready to go.

[+] In the block manager you can select your blocks (a table with options is added in the settings menu of each block).

[+] In the block manager you can see the cache status of each block. You can see if it is cached, enabled, or needs to be cached. You can also remove the cache of a block manually if needed.

[+] A cache manager dispatch is added. Now you can see storage space, cache invalidations, etc.

[+] Compatible with all other add-ons on the market-place (except for the other caching add-ons, but this makes sense I suppose).

[+] It can automatically detect whether a block is session dependent (basic core functionality, turns out it is an existing scheme in CS-Cart).

[+] If any type of POST function is used it will automatically switch to normal cache (instead of FPC).

[-] NGINX has been deprecated from the entire proces. Now it just uses PHP.

[-] It no longer has 'hole-punching', instead it has selective cache.

I expect to release the add-on to the public in the next 2 weeks with a price tag of just €100,00. Please let me know if any more features need to be added.

Wow! Just php meaning that you dump and load rendered layout fragments to files using php? Do you use system configured cache engine for that? Could you say a little more about the technical part?

Seems like a great simplification from a deployment/infrastructure setup point of view!

> It no longer has 'hole-punching', instead it has selective cache.

As I understand it's just a consequence of not using SSI anymore, right?

Wow! Just php meaning that you dump and load rendered layout fragments to files using php? Do you use system configured cache engine for that? Could you say a little more about the technical part?

Seems like a great simplification from a deployment/infrastructure setup point of view!

> It no longer has 'hole-punching', instead it has selective cache.

As I understand it's just a consequence of not using SSI anymore, right?

Yes the rendered layout fragments are dumped to files. It does not however use the system configured cache engine, instead I designed a seperate cache engine for just this purpose.

My addon works as follows:

1. You enable the addon for your selected controllers (I am thinking about adding support for custom controllers).

2. You load the page once (so that it can write the page to the cache).

3. Whenever you load the page you will receive a static HTML page.

4. If a post request is made it will automatically set a cookie and you will only receive 'fresh' content during this time (the blocks you have selected in the block manager however are still static. Think about product descriptions and such).

As I understand it's just a consequence of not using SSI anymore, right?

This is not the reason. I just refuse to call it holepunching as it is selective cache now. Holepunching was close to impossible to achieve without major server reconfiguration thus I decided to throw this idea into the bin as I want my solution to be as user friendly as possible (KISS).

Hi poppedweb!

As far as i understand you made full page cache similar to http://marketplace.cs-cart.com/add-ons/customer-experience/full-page-cache.html ?

In this case main disadvantage is dropping NGINX from process.

Hi poppedweb!

As far as i understand you made full page cache similar to http://marketplace.cs-cart.com/add-ons/customer-experience/full-page-cache.html ?

In this case main disadvantage is dropping NGINX from process.

No not similar, if you look at the previous information I have given you can conclude the following:

- It is almost 9 times as fast (in the video of CS-Commerce you can see the TTFB is ~225 ms, the TTFB for my addon is ~25 ms).

- It features selective cache. This allows you to reduce the amount of database calls even when the full page cache has been disabled. (e.g. after a user adds something to their cart).

- An intuitive settings panel. You can enable / disable FPC for every controller, also it is possible to see the filesize. Furthermore, selective cache is very easy to set up. You just go to the template editor, go to the settings of the block and add it to selective cache.

Also, in case you might compare mine to CS-Cart's one:

- No server configuration is needed.

- You can use your desired web proxy server.

- Language detection works

- TTFB is roughly the same, however, it is not possible to achieve the same speed by using PHP vs Varnish. You can thus expect the same speeds as on http://highload.demo.cs-cart.com/

Hopefully I have answered your questions,

Best wishes,

Hi poppedweb!

Thanks for detailed explanation.

When do you plan to release module?

It will be nice to test it on demo stand similar to http://highload.demo.cs-cart.com/

also ssl is working ?

also ssl is working ?

Yes, it supports SSL.

I'm very interested in this add-on. I wonder though how will it handle blocks that load "random" products? (for example, out of a selection of 40, 8 products are shown at random). Would that work?

Just curious on the server side level. What kind of setup on the server side are we talking about in your case Poppedweb ?
What are you running in production and or in development with this caching addon ?

I'm very interested in this add-on. I wonder though how will it handle blocks that load "random" products? (for example, out of a selection of 40, 8 products are shown at random). Would that work?

No this would not work, this would only work with the 'selective cache' option. The entire point of full page cache is not to have unique blocks that need to be rendered as this slows down the initial load.

Just curious on the server side level. What kind of setup on the server side are we talking about in your case Poppedweb ?
What are you running in production and or in development with this caching addon ?

I use a system with PHP 7.0, NGINX, Ubuntu 16.04 LTS together with 2 cores and 4 gb of RAM. I use this as this is as it is a fair baseline for common webservers.