Intelligent PHP Cache Control

This code snippet checks if a page has been modified since it was last displayed. If so, it sends a "304 not modified" header and exits, otherwise the content is rendered. Prepend this snippet on top of every PHP file you want to apply this intelligent caching-mechanism. Especially useful if you (have to) serve static content via php and want it to be cached like ordinary HTML or CSS.

<?php
//get the last-modified-date of this very file
$lastModified=filemtime(__FILE__);
//get a unique hash of this file (etag)
$etagFile = md5_file(__FILE__);
//get the HTTP_IF_MODIFIED_SINCE header if set
$ifModifiedSince=(isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? $_SERVER['HTTP_IF_MODIFIED_SINCE'] : false);
//get the HTTP_IF_NONE_MATCH header if set (etag: unique file hash)
$etagHeader=(isset($_SERVER['HTTP_IF_NONE_MATCH']) ? trim($_SERVER['HTTP_IF_NONE_MATCH']) : false);

//set last-modified header
header("Last-Modified: ".gmdate("D, d M Y H:i:s", $lastModified)." GMT");
//set etag-header
header("Etag: $etagFile");
//make sure caching is turned on
header('Cache-Control: public');

//check if page has changed. If not, send 304 and exit
if (@strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])==$lastModified || $etagHeader == $etagFile)
{
       header("HTTP/1.1 304 Not Modified");
       exit;
}

//your normal code
echo "This page was last modified: ".date("d.m.Y H:i:s",time());

?>

Comments

  1. User Avatar
    Daniel
    Permalink to comment#

    Hi,
    does this code also work if i embed it in the header.php which is included in every file that could change?

    Thanks in advance!

    • User Avatar
      k
      Permalink to comment#

      To keep this snippet in separate file only including in at the top of other files, you should probably change all occurrences of __FILE__ to $_SERVER['SCRIPT_FILENAME'].

  2. User Avatar
    Greg
    Permalink to comment#

    Can you please leave a short step-by-step tut on what variables need to be replaced and how to implement?

    Much thanks!

  3. User Avatar
    Michael

    Line with ETag should be:

    header("ETag: \"$etagFile\"");

  4. User Avatar
    joe web site design
    Permalink to comment#

    i’ve added your code to the very top of my pages and tested my pages at http://web-sniffer.net

    they keep coming back with the Status: HTTP/1.1 200 OK no matter how many times i have web-sniffer.net revisit the page, without me touching my files on the server.

    if your code was working in my page, i would see 200 OK on the first hit but the subsequent visits should say 304 (not modified), correct?

    when i change the $ifModifiedSince= variable in your code from false to true, i get the 304 (not modified) every time, which does not seem correct since the first visit should return 200 OK, correct?

    thank you!

  5. User Avatar
    Jo
    Permalink to comment#

    This doesn’t work. The browser still shows the cached page even after an update has been made. The only way you can get the updated info is if you press F5

    • User Avatar
      SirTimmyTimbit
      Permalink to comment#

      Change this line:
      if (@strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])==$lastModified || $etagHeader == $etagFile)

      to this:
      if (@strtotime($ifModifiedSince)==$lastModified || $etagHeader == $etagFile)

      What I think happened was the checks in the line above would always return true because you’re quering the “Last-Modified” header after you’ve just set it.

      The OP did store the old “Last-Modified” header the variable before re-setting the header and just forgot to write it in code.

  6. User Avatar
    Rule
    Permalink to comment#

    So any one can make this thing to work? it seems interesting.
    Thank you

  7. User Avatar
    nitin
    Permalink to comment#

    is there anyone who has used this code so far…????

  8. User Avatar
    Private Identity
    Permalink to comment#

    Hello,

    Yes, I have used the code on a production server for some CDN caching over a balanced proxy, works like a Charm.

    You can test it easily with Chrome debugger or Firebug.
    – Enjoy

  9. User Avatar
    Emmanuel Higgins
    Permalink to comment#

    Got this code to work, but only after throwing in these functions, which stop PHP from automatically adding the no-cache header.

    session_cache_limiter(‘public’); //This stop php’s default no-cache

    session_cache_expire(5); // Optional expiry time in minutes

  10. User Avatar
    Cardei Catalin
    Permalink to comment#

    Hi,

    I put this code in a MVC app and works fine …
    I put the code in the bootstrap file …

    When I check with Chrome Debugger I notice that some of the resources get 200 OK status and almost everything get 304 Status …

    My question is how can I set witch resource should cache and witch not?
    This app has to update a the hits of a page each time somebody visit it … After the first time it does not update anymore …

    I need to somehow control what resources should cache and be able to set for cache only resources like css,js,jquery,images …

    Thanks

  11. User Avatar
    John Louie
    Permalink to comment#

    Good day to all… I recently reading about the md5_file function.. Is there a problem of using it.. because when I apply it to production site it cause very slow loading since md5_file fetching the file name and also i used the recommendation of “k” to change the

    md5_file(__FILE__) //to// md5_file($_SERVER['SCRIPT_FILENAME') )

  12. User Avatar
    Alan Guevara
    Permalink to comment#

    Thank you, work really well.

  13. User Avatar
    Chris D.

    Any use for this “$ifModifiedSince” variable?
    Code doesn’t seem to be using it.

  14. User Avatar
    Rob Schlüter
    Permalink to comment#

    Use this only when the web pages of a site directly map to corresponding PHP files.

    If a PHP file can generate different content, e.g. articles from a database, the ETag should be based on the actual content, not the PHP file generating it.

    Also, when the PHP script changes but the output remains the same the ETag should remain the same.

  15. User Avatar
    Adrian
    Permalink to comment#

    I’m using processwire cms framework to build website.

    I posted the snippet of code to a php template, but it doesn’t work. The X-Cache-Status always MISS

    This is the code, except the $lastModified which will update automatically when a content is updated.

    #etagFile will md5 content body

    last_modified;
    $etagFile = md5($page->body);
    $ifModifiedSince=(isset($_SERVER[‘HTTP_IF_MODIFIED_SINCE’]) ? $_SERVER[‘HTTP_IF_MODIFIED_SINCE’] : false);
    $etagHeader=(isset($_SERVER[‘HTTP_IF_NONE_MATCH’]) ? trim($_SERVER[‘HTTP_IF_NONE_MATCH’]) : false);

    header(“Last-Modified: “.gmdate(“D, d M Y H:i:s”, $lastModified).” GMT”);
    header(“Etag: $etagFile”);
    header(‘Cache-Control: public’);

    if (@strtotime($_SERVER[‘HTTP_IF_MODIFIED_SINCE’])==$lastModified || $etagHeader == $etagFile)
    {
    header(“HTTP/1.1 304 Not Modified”);
    exit;
    }

    ////// My cms code

    ?>

Posting Code

You may write comments in Markdown. This makes code easy to post, as you can write inline code like `<div>this</div>` or multiline blocks of code in triple backtick fences (```) with double new lines before and after.

Code of Conduct

Absolutely anyone is welcome to submit a comment here. But not all comments will be posted. Think of it like writing a letter to the editor. All submitted comments will be read, but not all published. Published comments will be on-topic, helpful, and further the discussion or debate.

Want to tell us something privately?

Feel free to use our contact form. That's a great place to let us know about typos or anything off-topic.

Submit a Comment

icon-closeicon-emailicon-linkicon-menuicon-searchicon-tag