Magento 1.x Community Edition is painfully lacking one of the features of its Enterprise Counterpart: a Full Page Cache (“FPC”). An FPC is absolutely essential to running a fully optimized storefront — as most merchants come to realize, even with a perfectly optimized theme and few extensions, you can still end up with a 1 – 2 second load time on your product and category pages.
Intro: What is an FPC?
It’s true that finding a high quality hosting partner like Rackspace or Nexcess can give you an ideal environment, but it can’t fix one basic fact: Magento’s time-to-first-byte is slow. The reason is Magento’s extremely powerful codebase — the platform is highly abstracted and capable of handling almost any eCommerce scenario (and can even be adapted to fulfill functions outside the sphere of eCommerce). To do that, the Magento core team wrote over 750,000 lines of code in Magento 1.x — a lot of which are run on ever page load. That means that every time you load a page on your Magento store, you’re running through millions of function calls and tens of thousands of lines of code.
For operations like checkout or login, there is no getting around this — you have a unique customer performing an action specific to them. However, for most of the page renders on your site (which are just displaying product, CMS, or category information) Magento is doing identical work over-and-over again. This is where a Full Page Cache comes in. The FPC will keep Magento from having to repeat that work. So once a static page has been rendered, Magento stores that HTML in an easily-accessed cache, from which the page can be displayed in the future without having to run any of the code behind it.
There’s an important distinction here: the rendered page (all the html you can see under View Source in your browser) is no big deal to store and retrieve. The time consuming part is the code that generates that HTML (decides what html gets output).
Enter Lesti FPC!
As Magento CE doesn’t provide an FPC out the door, you will need to purchase a paid extension, or download a free one. Our favorite is Lesti FPC by Gordon Lesti: https://github.com/GordonLesti/Lesti_Fpc.
This extension is heavily tested, very well thought out, and highly adaptable to a store with numerous extensions and customizations.
A little extra background: Dynamic Content
As with all FPC’s, the main challenge is providing an effective way to generate dynamic content. It’s true that a product page or homepage is identical for every user — however, there are usually parts of the page that may differ from one customer session to another. A great example is a cart indicator in the header of the page. For a user with several items in their shopping cart, this indicator may show a number such as 3 Items! But for everyone else, it should show No Items. So, how do we do this? We have cached the entire page, but now we’re reneging on our promise to not perform any rendering, as we’ve realized we need to!
Lesti solves this problem through Hole Punching. Hole Punching allows the Lesti system to replace parts of the cached HTML document with their dynamically rendered counterparts. So in the above example, Lesti is intelligent enough to render only the part of the Magento codebase responsible for the cart indicator and then insert that dynamic content into the cached HTML document.
Sounds really hard right?? You’re correct — such challenges are precisely why you use a prebuilt extension versus reinventing the wheel. And Lesti’s method for implementing dynamic content is one of the best:
Lesti works by caching the entire HTML output of a page. By default it only caches CMS pages, Category Pages, and Product pages. There is no reason to cache other pages unless they are slow to render and highly trafficked.
Lesti provides 2 methods to update the cache with dynamic content:
Dynamic blocks are all blocks which should be regenerated on every page view (even for cached pages). These blocks are usually message blocks (e.g., Global Messages). Lesti updates them through the aforementioned hole punching process.
These are most of the blocks you’ll interface with. Dynamic blocks are blocks which are different for each user, but can be cached in the user session. For example, the top links are a lazy block — they contain the number of items in the cart and the logged in / logged out links. Lesti regenerates all Lazy Blocks whenever a Refresh Action occurs.
See here for more details: https://gordonlesti.
Visit the Lesti github and see his instructions for using modman. If you want to install manually, simply:
- Download the latest release from the github repo
- Copy the entire app directory into your Magento root.
- Clear cache, reload, and it should be installed!
- Verify installation by visiting System -> Configuration -> System -> Lesti FPC — if you see the configuration options, it’s installed!
Lesti Configuration — Files
There is a file in app/etc/fpc.xml which should be excluded from source control (i.e., it’s unique to each environment). This file lets you set the lifetime of the cache. You do not need to do anything with this file if you’re using files as the cache type. However if you’re using memcache or redis you have to update this file with the cache info so the FPC will utilize it (separate from local.xml ‘s cache setup).
For example, a store running redis might have the below fpc.xml
<?xml version="1.0"?> <!-- /** * Lesti_Fpc (http:gordonlesti.com/lestifpc) * * PHP version 5 * * @link https://github.com/GordonLesti/Lesti_Fpc * @package Lesti_Fpc * @author Gordon Lesti <firstname.lastname@example.org> * @copyright Copyright (c) 2013-2014 Gordon Lesti (http://gordonlesti.com) * @license http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) */ --> <config> <global> <fpc> <lifetime>86400</lifetime> <backend>Mage_Cache_Backend_Redis</backend> <backend_options> <server>/var/tmp/redis-cache-socket.sock</server> <port>0</port> <database>0</database> <password></password> <force_standalone>0</force_standalone> <connect_retries>1</connect_retries> <automatic_cleaning_factor>0</automatic_cleaning_factor> <compress_data>1</compress_data> <compress_tags>1</compress_tags> <compress_threshold>20480</compress_threshold> <compression_lib>gzip</compression_lib> <persistent></persistent> </backend_options> </fpc> </global> </config>
Lesti Configuration — Admin
There are a lot of configuration settings in System -> Config -> System -> Lesti FPC.
For starters, the cached pages, the dynamic blocks, the lazy blocks, the refresh actions are all listed here where you can modify them. You want to ensure all module blocks in your layout that may have some form of dynamic content are accounted for in dynamic blocks or lazy blocks. Use the list of module controller routes and refresh actions to handle which pages Lesti attempts to cache, and when it flushes its lazy blocks.
There are all parameters to exclude from cache (prevent lesti from caching a page with the given parameter) or qualify the cache (for example, the page number in a category result set tells lesti that page 1 is different from page 2).
Other settings you may need to use:
- Customer group caching — tells Lesti that pages will all have a different cache based on the customer group.
- Recently Viewed Products — if enabled Lesti makes an ajax request on certain pages to track recently viewed products. So the list of recently viewed is hole punched, but tracking that a user just viewed the product is handled via a supplemental ajax request.
A full tutorial on every individual setting is beyond the scope of this article, however the code is open source, so you should be able to track how each configuration option is used in the module.
Lesti – Verify & Test
The FPC may cause a number of problems — you need to heavily test a site. Generally you want to test in the following manner:
Have 2 browsers (sessions) open, A & B
- Perform actions in browser A as a non-logged in user (log in, add items to cart).
- Visit the site as a non-logged in user in B and see if you are seeing cached data meant only for the session started on A
You may also need to examine 3rd party modules. Any module that does an IF…THEN check based on browser user agent or a cookie will need to be hole punched or rewritten. Lesti is not made to support random if then logic based on user meta information.
Finally, you need to verify that the page is actually getting cached. Usually you can tell just by an increase in response time, that said if you need to debug:
Line: 84, add this line before the sendResponse()
// Add this line @mod Header("X-Cached-Fpc: HIT"); $response = Mage::app()->getResponse(); $response->setBody($body); Mage::dispatchEvent( 'fpc_http_response_send_before', array('response' => $response) ); $response->sendResponse(); exit;
This will cause an extra header to get sent with cache hits, so you can verify it’s working.
Again, you should immediately notice a huge improvement in your site speed. We see most Magento sites using Lesti having cached page time-to-first-byte of 150ms – 450ms. If you’re seeing times beyond that without significant hole punching, you will want to double check everything is working.
Need Help Installing an FPC or Optimizing Your Site?
Here at Cadence Labs, we’ve optimized dozens of Magento sites and have gotten pretty good at it. We know how to audit your site for inefficient code, install extensions like Lesti, and verify search engine’s like Google are not penalizing you for a performance issue.
If you would like help optimizing your Magento site, call (719)-286-0751 or visit our contact page to have a real person contact you today!