Magento Monday: LiteMage, CDNs, and Tokens
Welcome to the first installment of Magento Monday! We’ve enjoyed bringing you WordPress Wednesday every week, and so we thought it would be fun to also share some helpful Magento and LiteMage tips here sometimes.
Let’s kick off this series by talking about Content Delivery Networks, how the custom CDN cache rules you have defined may be breaking your Magento 1.x store, and what you can do to fix the problem.
Note: This particular issue is only a Magento 1 issue. It does not apply to Magento 2.
It’s all about the Tokens
Magento 1.x uses a session variable called form_key
as a CSRF token to prevent CSRF injection. The form_key
value is stored in the user session and is unique for every user. When a shopper adds a new product to the cart, or a new item to compare, Magento looks for the form_key
token in the dynamically-generated HTML, and uses it in the validation process.
If there is no caching solution enabled at all, then this process works. It’s slow and painful, but it works.
If LiteMage is caching the dynamic pages, then this process also works. LiteMage is smart enough to know that it must use ESI to “hole-punch” the correct unique form_key
token value for each user into the generated HTML, so that it may be used for validation.
If a CDN is caching the dynamic pages, then this process falls apart. Content Delivery Networks are not smart enough for hole-punching. They cache only one version of the page, which means they save only one form_key
token for every shopper to share. This can’t possibly work, and validation is broken.
We recommend that you turn off all caching of dynamic pages in your CDN. CDNs are great for static content, like CSS, JavaScript, and image files, so by all means, leave that type of caching enabled, but let LiteMage handle the dynamic page-caching duties.
Diagnosing and Fixing the Problem
How can you tell whether your page is being served from CDN cache or LiteMage Cache? Take a look at the Response Headers.
What you want to see is something like this:
X-LiteSpeed-Cache: hit,litemage
This shows that LiteMage is in control of caching the page.
If you see headers like the following:
cache-control:public, max-ago=14400 cf-cache-status: HIT
then the CDN (in this case Cloudflare) is doing the caching.
Here’s how to stop that from happening:
From the Magento admin area, navigate to System > Configuration > General > Web and open the Unsecure and Secure sections.
While it may be easier to just set Base URL to the CDN’s address, and let the CDN sort out what content should be served from where, doing so is highly inefficient and will slow down your site. Why send a request to the CDN only to have it bounced back to your own site for fulfillment?
Setting it up properly in this section ensures that requests are routed to the proper locations right from the start, with no time wasted with unnecessary travel back-and-forth.
Set the Base URL to your site’s address, not the CDN’s.
The Skin, Media, and JavaScript URLs refer to static content, and those may point to your CDN.
A note about the Unsecure section: Technically, you can use http://
in these fields, but with LiteSpeed’s emphasis on HTTP/2 and QUIC, you have the ability to use https://
everywhere. These days security is a big deal, so why wouldn’t you use it?
So, Why Don’t CDN Cache Rules Work?
If you know about Magento 1.x’s session cookies (frontend
for HTTP and frontend_cid
for HTTPS), you may have tried to use them to configure the CDN’s cache rules as follows: if there is no session cookie, it means the user hasn’t started shopping yet, and the page can be cached by the CDN. If there is a session cookie, then the request must be sent to the Magento backend.
It’s a clever idea, but unfortunately it doesn’t work, and this is why:
The form key is irrelevant while the user is simply browsing, and so it seems fine to serve the page from CDN cache. However, as soon as they try to act (add an item to the cart, or to compare), the form key becomes important. And because of the fact that they are performing that first action from the cached page, they will be looking at the cached form_key
value instead of a form key that’s been generated specifically for them. As a result, the action will fail.
If the user reloads the page, the CDN recognizes that they now have a session key, and so their request hits the backend, the correct form key is generated, and they are able to shop normally.
The problem works itself out on the second attempt, but the damage to user experience has already been done. Some shoppers will reload and keep going, but others will leave after the first error. Can you afford to lose those customers?
Even if the session cookie idea had worked, it’s hard to see what would be gained from having the CDN handle the caching of that page. LiteMage, which was built specifically for Magento, can manage your shop’s cache in a much more efficient way than a Content Delivery Network can. As we’ve already seen, LiteMage can punch holes for private content, making it possible to store a single copy of the page in public cache, and then serve it with the personalized information in the punched holes.
With the CDN in charge, users who are actively shopping cannot benefit from caching, but with LiteMage, they can.
Additionally, LiteMage can take advantage of tag-based purging, making sure that all of the necessary pages (and only the necessary pages) are purged when a product is updated.
If you are running LiteMage, it’s best to let LiteMage handle all of the page-caching that it was built to handle. Your Content Delivery Network can easily deal with the rest.
—
Have some of your own ideas for future Magento Monday topics? Leave us a comment!
If you enjoyed this, here are a few other things you can do:
- Subscribe to the Magento Monday RSS feed
- Download LiteMage Cache for Magento 1
- Download LiteMage Cache for Magento 2 from the GitHub Repository
- Learn more about the plugin on our website
Comments