Is your website a HIT? Make sure the full page cache works

Written: 2.5.2018 Updated: 2.5.2018

Do you expect you website to get a lot of visitors? No worries, if your site is hosted by Seravo, you are already well prepared. However, if you want to be extra well prepared and to make sure your WordPress site loads as fast as possible, you should check that the HTTP caching works for your site. The caching done on the HTTP level stores a copy of the entire HTML document your WordPress PHP code produces, and if that copy can be used instead of fetching the HTML document from PHP again, it will mean that the HTML page itself can load in just a few milliseconds. The HTTP level caching is based on common web standards and special cache controlling HTTP headers, so it is not Seravo specific in any way. Also visitors browsers and any intermediate network proxy server will store the HTML document and help your visitors get the pages loaded faster.

Let’s have a look at how HTTP headers look like. Below is an example of what HTTP headers the Swedish WordPress community website emits. The front page of wpsv.se is fetched with a command-line tool called curl, which is incidentally made by also Swedish open source developer Daniel Stenberg and at Seravo our admins use curl on daily basis to test customer sites.

There is a whole bunch of HTTP headers emitted and we have omitted a few ones for brevity. On the first line we see that the page returned code “200 OK” which tells everything went fine. The fields Date, Content-Type, Expires, Cache-Control and Link are all standard HTTP headers. The X-Proxy-Cache is a special field emitted only by Seravo’s servers. The fields Expires and Cache-Control tell the browser (and all intermediate HTTP proxies) what the lifetime of the contents is and how it can be cached. In this case they state that the contents has expired in the past, and that caching is forbidden. HTML documents sent with these HTTP headers will not be cached anywhere, ever.

At Seravo the HTTP headers are emitted either from WordPress PHP code (using the PHP function header()) or from the customer specific Nginx configuration at /data/wordpress/nginx/.

Now it turns out this site had define('WP_DEBUG', true); set in wp-config.php, and when the debug mode is on, WordPress also emits cache forbidding headers. Removing that stanza in this case was the solution to get rid of the cache bursting headers. Below are the headers after the change.

Note also that the value of the header X-Proxy-Cache. Now it says it is a HIT. This means that the response was served from the HTTP proxy and not from the PHP backend. As a developer, your goal is to make sure your visitors can get HIT as often as possible.

The server is not the only party emitting HTTP headers. Also the client side emits headers. In fact, if you have a cookie set in a browser, then the request headers will include a line starting with keyword Cookie followed by colon and a value. If the client HTTP request has a cookie set, then the response from the server is most likely never from the cache, as the cookie is a sign of a logged in user and WordPress does want to serve each visitor unique content, for example a page that renders “Howdy, Otto” in the upper right corner just for one visitor, and not show the same page to everybody. So cookies will burst the cache.

Another client side HTTP request header is Pragma: no-cache. In fact, if you in Chrome of Firefox press Ctrl+F5 to do a deep reload of a page, this is exactly the client header sent to the server. This can be replicated command line with curl with:

Note that here we see again a new value in X-Proxy-Cache. All possible values are HIT, MISS, EXPIRED, BYPASS and STALE. If you have MISS, it means the page does not cache on HTTP level at all. The other words tell about the state of the request cache. As a developer, you want to see that most of the time you get HIT and only occasionally something else.

Curl includes a huge amount of features and it is a great toolbox for a web developer to debug what happens on the HTTP level when the web browser and web server are interacting. One favourite option of ours is to print our the total time it took for the response to arrive.

This means that the request completed in 80 milliseconds when it came from the proxy cache and when served all the way from PHP it took a second. That is actually quite a lot, and next week we will blog about tools like XDebug that can be used to analyze what keeps so long for PHP that producing the HTML page took a second.

Note that if these tests are run from a laptop, then the figure will include the network lag of your local WLAN network and all hops between the laptop and the server. To always get comparable results, it is recommended to run curl from the server in a SSH terminal connection.

Since this is a common task, we have at Seravo created two handy commands for our customers to test how fast WordPress serves pages: wp-speed-test and wp-load-test. See example below.

wpsv@wpsv_1e5ea3:/data/wordpress/htdocs$ wp-speed-test
Testing speed URL https://wpsv.se...
For an explanation of the different times, please see docs at https://curl.haxx.se/docs/manpage.html
URL TOTAL NAMELOOKUP CONNECT APPCONNECT PRETRANSFER STARTTRANSFER = AVG
https://wpsv.se 1.174 0.125 0.126 0.132 0.132 1.174 1.174
https://wpsv.se 0.478 0.000 0.000 0.000 0.000 0.478 0.826
https://wpsv.se 1.017 0.000 0.000 0.000 0.000 1.017 0.890
...
https://wpsv.se 0.579 0.000 0.000 0.000 0.000 0.579 0.864
https://wpsv.se 1.163 0.000 0.000 0.000 0.000 1.163 0.880
https://wpsv.se 0.594 0.000 0.000 0.000 0.000 0.594 0.865
Test completed. If the values seems too high, please profile your PHP code to find potential bottle necks.
Note that this test tells how fast your site is in the sense of how long it takes for PHP to generate the
HTML output. To test how much load the site can handle, run wp-load-test.
wpsv@wpsv_1e5ea3:/data/wordpress/htdocs$ wp-load-test
Testing capacity of URL https://wpsv.se...
URL COUNT ELAPSED TIME RESPONSE TIME
https://wpsv.se 1 1 1.130
https://wpsv.se 2 2 1.040
https://wpsv.se 3 3 0.995
...
https://wpsv.se 38 29 0.571
https://wpsv.se 39 29 0.568
https://wpsv.se 40 30 0.567
Test ended after 30 seconds and 40 requests with an average of 1.3 requests per second.
This test only uses a single PHP worker and it bypasses the edge cache. The actual site
will be capable of handling much more traffic. If the response time of a single PHP
request is much above 0.500 seconds, please try to optimize the PHP code and run wp-speed-test.
If the response time is much below 0.100 seconds, then this test is likely to trigger the
flood protection and server will yield 429 responses.

You can also add the parameter –cache to see how fast the page loads if fetched from the outside where the HTTP proxy is effective: