HTTP cache control on a dynamic PHP page

HTTP Cache control and PHP pages cachingIn the general case the website caching happens on the server side where the PHP output or database objects are cached in static files or in the server RAM. That could work pretty good but we could improve that and add one extra layer and more specifically define HTTP header for cache control so to utilize browser cache on the client side.

Procedural PHP implementation

[cc lang=”php”]
function set_headers($file, $timestamp) {
$gmt_mtime = gmdate(‘r’, $timestamp);
header(‘ETag: “‘.md5($timestamp.$file).'”‘);
header(‘Last-Modified: ‘.$gmt_mtime);
header(‘Cache-Control: public’);
if ($_SERVER[‘HTTP_IF_MODIFIED_SINCE’] == $gmt_mtime
|| str_replace(‘”‘, ”, stripslashes($_SERVER[‘HTTP_IF_NONE_MATCH’])) == md5($timestamp.$file)) {
header(‘HTTP/1.1 304 Not Modified’);
The above method receives a file path to an existing file on the server and as a second parameter the time stamp of last change of the file. Example call of the method:
[cc lang=”php”]set_headers (__FILE__, filemtime(__FILE__));[/cc]
Now let’s have a look in more details how it works. The most important bit that allows us to validate the file change is the ETagwhich stores a hash of the file and the timestamp last change so it of ensures that if the file is updated at a later point we will set different ETag to validate against.

The second HTTP header “Last-Modified” is obvious but it’s important because it tells the browser to set proper headers according the file state.

Next directive Cache-ControlĀ  public explicitly tells to the browser and any proxies transferring the request that the content could be cached and force the browser to check the ETag and Lat-Modified headers to determine file state. You can read more about Cache-Control directive and its states here.

Once we’ve set our headers next step is to check the headers from the previous request and decide what to output based on the file state. We will use the $_SERVER global variable which holds all of the data we’ll need. The first parameter is HTTP_IF_MODIFIED_SINCE which should be which should be same as last modified date of the file, second is HTTP_IF_NONE_MATCH which should match the file and time stamp hash and if both parameters are valid we can set the HTTP header to 304 – Not modified and allow the browser to server the file from the local cache instead of transferring the data again making whole experience a faster and smoother for the end user.

In case where the two conditions don’t match we simply server the request and output the original PHP content and allow this to be cached for subsequent requests.

Zend Framework cache implementation

In ZF you can take advantage of the preDispatch() method of Controllers and set the header according your own logic and conditions:
[cc lang=”php” escaped=”true”]
class TestController
public function preDispatch()
// Your logic here
$response = $this->getResponse()
$response->setHeader(‘ETag’, md5(filemtime(__FILE__).__FILE__), $replace = true);
$response->setHeader(‘Last-Modified’, gmdate(‘r’, filemtime(__FILE__)), $replace = true);
$response->setHeader(‘Cache-Control’, ‘public’, $replace = true);
if($fileNotExpired === true) {
// Set 304 header
} else {
// Serve the file
That is rough and dirty example but shows how to utilize that functionality in your scripts so it can be further adapted to your specific needs and logic.

Further reading



Just a guy with strong interest in PHP and Web technologies

Tagged with: , , ,

Leave a Reply

Your email address will not be published. Required fields are marked *


This site uses Akismet to reduce spam. Learn how your comment data is processed.