LiteSpeed Cache Vary
The Cache Vary is an important concept for caching. For simplicity's sake, the idea behind cache varies is that they allow the cache to save multiple versions of the same URL.
Using the public cache key as an example, KEY = Host + URI + Query String + VARY
This article describes the VARY portion of the cache key.
Examples of reasons to use cache vary:
- Differences between Desktop and Mobile views. Specifically, the html for the desktop and mobile views do not match. This could be because of a non-responsive theme, different widgets loading, or otherwise.
- Global audience/multi-lingual support. If the url must stay the same, it is possible to vary on the language.
Vary Types
There are two types of cache varies, vary cookie and vary value. Both are used when determining which version to return. Setting a Vary Cookie will notify the server about which cookies to keep an eye on. This will not actually set a cookie. Setting a Vary Value will set an environment value strictly for the use of varying the request.
Using the earlier cache key equation, VARY can be expanded to:
VARY = VARY_COOKIE + VARY_VALUE
Vary Cookie
LiteSpeed Servers can vary on cookies. The server needs to be instructed about the cookie NAME to vary on. The server will then use the cookie VALUE as part of the cache key.
For Example, suppose the server is instructed to vary on the cookie
my_cookie
.
- Request A has NO cookies.
VARY = ""
- Request B has the cookie my_cookie = Alabama
VARY = "my_cookie=Alabama"
- Request C has the cookie my_cookie = California
VARY = "my_cookie=California"
- Request D has the cookie my_cookie = Alabama
VARY = "my_cookie=Alabama"
For this example, Requests B and D will get the same cached version (or another way to see it: request B can generate the cache entry for request D). Their cache entry will differ from requests A and C, both of which have a unique cache entry. So there are 3 total cache entries.
By default, LiteSpeed servers will recognize any cookie that starts with
_lscache_vary
as a vary cookie.
Vary Environment Value
The Cache Vary Value is an environment value to vary the request. The Vary Value differs from Vary Cookies in that the Value set is the value used. There is no key to check and only one vary value may be used. If multiple environment values are set, only the last one will be used.
- Request A has no vary value
VARY = ""
- Request B sets vary value "ismobile"
VARY = "ismobile"
- Request C sets vary value "US"
VARY = "US"
- Request D sets vary value "US"
VARY = "US"
For this example, Requests C and D will get the same cached version (or another way to see it: request C can generate the cache entry for request D). Their cache entry will differ from requests A and B, both of which have a unique cache entry. So there are 3 total cache entries.
An example use case is the separate mobile view use case listed above.
Given the request's user agent, if it matches a mobile browser, one can
set vary value = ismobile
. This will be covered in the next section
below.
Vary Cookies and Vary Values can both be set. Setting one does not limit setting another.
Suppose the server is instructed to vary on the cookies my_cookie
and
my_cookie2
.
- Request A has no cookies and no vary value is set.
VARY = ""
- Request B has the cookie 'my_cookie=Alabama' and no vary value is
set.
VARY = "my_cookie=Alabama"
- Request C has the cookie 'my_cookie=Alabama' and vary value
'ismobile' is set.
VARY = "my_cookie=Alabama" + "ismobile"
- Request D has the cookies 'my_cookie=Alabama' and
'my_cookie2=Apple' and vary value 'ismobile' is set.
VARY = "my_cookie=Alabama&my_cookie2=Apple" + "ismobile"
- Request E has no cookies, but vary value 'ismobile' is set.
VARY = "ismobile"
The five requests will all be served a different cache entry, because their final VARY results are different.
How to instruct the server to use a custom vary
Custom varies should be used if the contents of the site change depending on controllable circumstances, such as when a User Agent matches a certain pattern or if a cookie exists and has a specific value. Custom cookie varies can also be used to prevent conflicting login situations, as described here.
Rewrite Rules
Rewrite Rules can be used to vary the request when it comes in. Varies
set by Rewrite Rules will be forwarded to the web application via
environment variables. In PHP, these are
$_SERVER['LSCACHE_VARY_COOKIE']
and $_SERVER['LSCACHE_VARY_VALUE']
respectively. For cookies, it will be the list of cookie names that the
server checked. For value, it will be the value used. This may be empty
if no vary values are set.
Vary Cookie
Example:
RewriteRule .? - [E=Cache-Vary:my_cookie]
This example rewrite rule will instruct the server to check for the
my_cookie
cookie NAME and if it exists, vary on it.
As of LSWS version 5.1.x and OLS 1.4.x, it is possible to add multiple vary cookies to the rewrite rule.
Example:
RewriteRule .* - [E="cache-vary:xf_style_id,xf_language_id"]
Vary Value
Example:
RewriteCond %{HTTP_USER_AGENT}
Mobile|Android|Silk/|Kindle|BlackBerry|Opera\ Mini|Opera\ Mobi [NC]
RewriteRule .* - [E=Cache-Control:vary=ismobile]
This example rewrite rule checks the user agent for a mobile browser
agent. If it matches, it will add the vary value ismobile
to the
request.
NOTICE: Examine the examples closely. Note that for the vary cookie, it
uses E=Cache-Vary:
, and for the vary environment value, it uses
E=Cache-Control:vary=
.
Response Header
Response headers can be used to instruct the server to cache the page with the listed varies in mind.
Note that the response headers should be used if the cache vary is meant to be set on a per page basis. For example, the WordPress plugin uses the response header to vary password protected pages on the password cookie. There is no reason to vary on the password cookie on non password protected pages, so to save space, only the password protected pages will use the vary.
Vary Cookie
Adding a vary header with cookie=
instructs the server to check for
the specified vary cookie(s) for the current URL only.
Example header:
X-LiteSpeed-Vary: cookie=my_cookie,cookie=my_cookie2
This response header will add two vary cookies, my_cookie
and
my_cookie2
, to the list of varies to check for this URL. On the next
request for this URL, the server will check for the vary cookies set by
the rewrite rules in addition to the two cookies above as if they were
part of the rewrite rule.
Vary Value
Adding a vary header with value=
instructs the server to add the
environment value to the cache entry. This is useful for situations
where the environment value should be set, but the rewrite rule missed
setting it.
Example header:
X-LiteSpeed-Vary: value=ismobile
This response header will add the ismobil
environment value to the
cache key for the URL. This may be set when desktop and mobile views
should be cached separately, but the rewrite rule did not match a mobile
user agent and the web application is building a mobile page. This vary
header will cache the page as a mobile page (as determined by the
environment value).
NOTE: This will only somewhat alleviate the problem described in the example. If the rewrite rule does not match the web application's definition of a mobile user agent, there will still be issues. The proper fix for this situation would be to correct the rewrite rule to match the web application.
It is also possible to set both vary cookies and vary values using a single response header, like so:
X-LiteSpeed-Vary: cookie=my_cookie,value=ismobile
This response header will vary the URL on the my_cookie
cookie and
add the ismobile
environment value to the current response.
Separated Mobile/Tablet/Desktop View Vary Example.
This is a simple example of cache vary based on devices, divided by desktop, mobile and tablet.
Chrome 76 on Windows 10:
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3163.100 Safari/537.36
Chrome 76 on Android 8 mobile:
Mozilla/5.0 (Linux; Android 8.0.0; LG-H870) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.12 Mobile Safari/537.36
Chrome 76 on Android 8 tablet:
Mozilla/5.0 (Linux; Android 8.0.0; SM-T825) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3786.0 Safari/537.36
Safari on iPhone:
Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/ 604.1.21 (KHTML, like Gecko) Version/ 12.0 Mobile/17A6278a Safari/602.1.26
Safari on iPad:
Mozilla/5.0 (iPad; CPU OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Mobile/16A5288q Safari/605.1.15
For the above exemplary user agent, we will use Android
or iPad
as
the user agent to detect tablets , iPhone
or Mobile
to detect
mobile, and consider all the rest desktop. This is a simple solution but
it should cover most cases. The rules can be further extended to match
more specific cases.
PHP example code:
<?php if (stripos($_SERVER['HTTP_USER_AGENT'],"iPhone")!==false || stripos($_SERVER['HTTP_USER_AGENT'],"Mobile")!==false ) { echo 'This is Mobile view'; } elseif (stripos($_SERVER['HTTP_USER_AGENT'],"iPad")!==false or stripos($_SERVER['HTTP_USER_AGENT'],"Android") !==false) { echo 'This is Tablet view'; } else { echo 'this is Desktop view'; }
.htaccess code:
CacheLookup public on RewriteRule .* - [E=cache-control:max-age=120] RewriteCond %{HTTP_USER_AGENT} iPad|Android [NC] RewriteRule .* - [E=Cache-Control:vary=istablet] RewriteCond %{HTTP_USER_AGENT} iPhone|Mobile [NC] RewriteRule .* - [E=Cache-Control:vary=ismobile]
The first rule will cache every page for 120 seconds.
The second rule will check the user agent. If it contains the keyword
iPad
or Android
it will set vary to istablet
. If the user agent
ALSO contains the keyword Mobile
though, such as from an Android
Mobile device, it will set vary to ismobile
instead of istablet
. All
other user agents are treated as desktop.
Test:
[root@test ~]# curl https://example.com/test.php -i -H "User-Agent: Android" HTTP/1.1 200 OK Connection: Keep-Alive Content-Type: text/html; charset=UTF-8 Etag: "62-1566258748;;;" X-Litespeed-Cache: hit Content-Length: 19 Date: Mon, 19 Aug 2019 23:52:28 GMT Server: LiteSpeed Alt-Svc: quic=":443"; ma=2592000; v="39,43,46", h3-22=":443"; ma=2592000 This is Tablet view [root@test ~]# curl https://example.com/test.php -i -H "User-Agent: Android Mobile" HTTP/1.1 200 OK Connection: Keep-Alive Content-Type: text/html; charset=UTF-8 Etag: "63-1566258751;;;" X-Litespeed-Cache: hit Content-Length: 19 Date: Mon, 19 Aug 2019 23:52:31 GMT Server: LiteSpeed Alt-Svc: quic=":443"; ma=2592000; v="39,43,46", h3-22=":443"; ma=2592000 This is Mobile view [root@test ~]# curl https://example.com/test.php -i -H "User-Agent: iPad" HTTP/1.1 200 OK Connection: Keep-Alive Content-Type: text/html; charset=UTF-8 Etag: "62-1566258748;;;" X-LiteSpeed-Cache: hit Content-Length: 19 Date: Mon, 19 Aug 2019 23:52:37 GMT Server: LiteSpeed Alt-Svc: quic=":443"; ma=2592000; v="39,43,46", h3-22=":443"; ma=2592000 This is Tablet view [root@test ~]# curl https://example.com/test.php -i -H "User-Agent: iPhone" HTTP/1.1 200 OK Connection: Keep-Alive Content-Type: text/html; charset=UTF-8 Etag: "63-1566258751;;;" X-LiteSpeed-Cache: hit Content-Length: 19 Date: Mon, 19 Aug 2019 23:52:40 GMT Server: LiteSpeed Alt-Svc: quic=":443"; ma=2592000; v="39,43,46", h3-22=":443"; ma=2592000 This is Mobile view [root@test ~]# curl https://example.com/test.php -i -H "User-Agent: Windows" HTTP/1.1 200 OK Connection: Keep-Alive Content-Type: text/html; charset=UTF-8 Etag: "64-1566258765;;;" X-Litespeed-Cache: hit Content-Length: 20 Date: Mon, 19 Aug 2019 23:52:45 GMT Server: LiteSpeed Alt-Svc: quic=":443"; ma=2592000; v="39,43,46", h3-22=":443"; ma=2592000 This is Desktop view [root@test ~]# curl https://example.com/test.php -i -H "User-Agent: others" HTTP/1.1 200 OK Connection: Keep-Alive Content-Type: text/html; charset=UTF-8 Etag: "64-1566258765;;;" X-LiteSpeed-Cache: hit Content-Length: 20 Date: Mon, 19 Aug 2019 23:52:48 GMT Server: LiteSpeed Alt-Svc: quic=":443"; ma=2592000; v="39,43,46", h3-22=":443"; ma=2592000 This is Desktop view
So each device hit cache and is showing varied cached content.