Viewed   141 times

I have a PHP 5.1.0 website (actually it's 5.2.9 but it must also run on 5.1.0+).

Pages are generated dynamically but many of them are mostly static. By static I mean the content don't change but the "template" around the content can change over time.

I know they are several cache systems and PHP frameworks already out there, but my host don't have APC or Memcached installed and I'm not using any framework for this particular project.

I want the pages to be cached (I think by default PHP "disallow" cache). So far I'm using:

session_cache_limiter('private'); //Aim at 'public'
header("Content-type: $documentMimeType; charset=$documentCharset");
header('Vary: Accept');
header("Content-language: $currentLanguage");

I read many tutorials but I can't find something simple (I know cache is something complex, but I only need some basic stuff).

What are "must" have headers to send to help caching?



You might want to use private_no_expire instead of private, but set a long expiration for content you know is not going to change and make sure you process if-modified-since and if-none-match requests similar to Emil's post.

$tsstring = gmdate('D, d M Y H:i:s ', $timestamp) . 'GMT';
$etag = $language . $timestamp;

$if_modified_since = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? $_SERVER['HTTP_IF_MODIFIED_SINCE'] : false;
$if_none_match = isset($_SERVER['HTTP_IF_NONE_MATCH']) ? $_SERVER['HTTP_IF_NONE_MATCH'] : false;
if ((($if_none_match && $if_none_match == $etag) || (!$if_none_match)) &&
    ($if_modified_since && $if_modified_since == $tsstring))
    header('HTTP/1.1 304 Not Modified');
    header("Last-Modified: $tsstring");
    header("ETag: "{$etag}"");

Where $etag could be a checksum based on the content or the user ID, language, and timestamp, e.g.

$etag = md5($language . $timestamp);
Saturday, November 19, 2022

This should be put in the end (moved for better look).

$anyTagMatched = anyTagMatched() ;
if( $anyTagMatched || ( ( null === $anyTagMatched ) && !isExpired() ) ) {
    notModified() ;
// Output content

Pseudocode (review needed):


 * Calculates eTag for the current resource.
function calculateTag() {

 * Gets date of the most recent change.
function lastChanged() {

 * TRUE if any tag matched
 * FALSE if none matched
 * NULL if header is not specified
function anyTagMatched() {
    $if_none_match = isset($_SERVER['HTTP_IF_NONE_MATCH']) ?
        stripslashes($_SERVER['HTTP_IF_NONE_MATCH']) : 
        false ;

    if( false !== $if_none_match ) {
        $tags = split( ", ", $if_none_match ) ;
        $myTag = calculateTag() ;
        foreach( $tags as $tag ) {
            if( $tag == $myTag ) return true ;
        return false ;
    return null ;

function isExpired() {
    $if_modified_since = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ?
        stripslashes($_SERVER['HTTP_IF_MODIFIED_SINCE']) :

    if( false !== $if_modified_since ) {
        // Compare time here; pseudocode.
        return ( $if_modified_since < lastChanged() ) ;

    return true ;

function notModified() {
    header('HTTP/1.0 304 Not Modified');
    exit ;

Main answer here.

Sunday, August 28, 2022

The key is must-revalidate: This means, that the client is asking the server if the file has changed. If you don’t handle this case, the browser will fetch a new copy.

Read Mark Nottingham’s fantastic Caching Tutorial for more information. As an example for a PHP implementation you may use my code.

Look into $_SERVER['HTTP_IF_NONE_MATCH']and $_SERVER['HTTP_IF_MODIFIED_SINCE'] for validating clients. And be aware that both headers may contain malicious code. ;)

Sunday, August 28, 2022

HTTP_IF_MODIFIED_SINCE is the right way to do it. If you aren't getting it, check that Apache has mod_expires and mod_headers enabled and working properly. Borrowed from a comment on

$last_modified_time = filemtime($file); 
$etag = md5_file($file);
// always send headers
header("Last-Modified: ".gmdate("D, d M Y H:i:s", $last_modified_time)." GMT"); 
header("Etag: $etag"); 
// exit if not modified
if (@strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) == $last_modified_time || 
    @trim($_SERVER['HTTP_IF_NONE_MATCH']) == $etag) { 
    header("HTTP/1.1 304 Not Modified"); 

// output data
Thursday, November 3, 2022

Regardless of which database you are using, it can be more efficient to put multiple queries into one statement. If you perform the queries separately, you have to make a call to the database across the network (or at least, between processes if on the same machine), get a connection to it (including autheticating), pass in the query, return the resultset, and release the connection for each query.

Even if you use connection pooling, you are still passing more requests back and forth than is necessary.

So, for example, if you combine two queries into one, you have saved all of that extra calling back and forth for the second query. The more queries you combine, then, the more efficient your app can become.

For another example, many apps populate lists (such as for dropdownlists) when they start up. That can be a number of queries. Performing them all in one call can accelerate startup time.

Saturday, October 15, 2022
Only authorized users can answer the search term. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :