5

I am serving images through php and having some problems setting it up to respond with 304 headers to save on load time.

Most of the code below I found on php.net. It works, however ALWAYS responds with 200. For some reason the If-Modified-Since header is not being received on any requests even though I am sending the Last-Modified header initially. This is being done on an apache server. Any idea what might be wrong?

Example here.

This page will load the image from disk and display it to browser, along with sending a Last-Modified header. If you refresh the page, the browser doesn't send a If-Modified-Since header like it should.

define('SITEPATH', (dirname($_SERVER['SCRIPT_NAME']) == '/') ? '/' : dirname($_SERVER['SCRIPT_NAME']).'/');

$load_path = $_SERVER['DOCUMENT_ROOT'] . SITEPATH . 'fpo_image.jpg';

// Get headers sent by the client.
$headers    = apache_request_headers(); 
$file_time  = filemtime($load_path);

header('Cache-Control: must-revalidate');
header('Last-Modified: '.gmdate('D, d M Y H:i:s', $file_time).' GMT');

if (isset($headers['If-Modified-Since']) && (strtotime($headers['If-Modified-Since']) == $file_time)) {

    header('HTTP/1.1 304 Not Modified');
    header('Connection: close');

} else {

    header('HTTP/1.1 200 OK');
    header('Content-Length: '. filesize($load_path));
    header('Content-type: image/jpeg');                         

    readfile($load_path);

}
Cyril Gandon
  • 16,830
  • 14
  • 78
  • 122
Louis W
  • 3,166
  • 6
  • 46
  • 76
  • +1 for question! A very similar problem with If-Modified Since header returning the full content instead of 304: http://stackoverflow.com/questions/4810806/cached-php-generated-thumbnails-load-slow-how-to-find-problem-solution-from-wat – Sam Feb 23 '11 at 18:19

5 Answers5

5

mandor at mandor dot net posted a solution at the PHP.net documentation for the header function which worked for me:

<?php

        // Test image.
        $fn = '/test/foo.png';

        // Getting headers sent by the client.
        $headers = apache_request_headers();

        // Checking if the client is validating his cache and if it is current.
        if (isset($headers['If-Modified-Since']) && (strtotime($headers['If-Modified-Since']) == filemtime($fn))) {
            // Client's cache IS current, so we just respond '304 Not Modified'.
            header('Last-Modified: '.gmdate('D, d M Y H:i:s', filemtime($fn)).' GMT', true, 304);
        } else {
            // Image not cached or cache outdated, we respond '200 OK' and output the image.
            header('Last-Modified: '.gmdate('D, d M Y H:i:s', filemtime($fn)).' GMT', true, 200);
            header('Content-Length: '.filesize($fn));
            header('Content-Type: image/png');
            print file_get_contents($fn);
        }

    ?>
Keith Hughitt
  • 4,860
  • 5
  • 49
  • 54
2

I believe it should be

if (isset($headers['If-Modified-Since']) && (strtotime($headers['If-Modified-Since']) >= $file_time)) {

Checking if the modified time is greater than or equal rather than just equal. Although I do understand the two values should be the same.

anveo
  • 345
  • 2
  • 6
  • Any idea why the 'If-Modified-Since' is not being received by php for subsequent requests? I am debugging the $headers value and it never shows up in there. Supposedly all you have to do is set a 'Last-Modified' and it should show up. – Louis W Jun 24 '09 at 19:07
  • Are you sure your browser is sending it? Use a plugin like LiveHTTPHeaders to verify that. Also when I do it I use $_SERVER[] rather than apache_request_headers() – anveo Jun 25 '09 at 21:56
  • Yea, I checked both _SERVER and apache_request_headers() very strange. Maybe I should put up an example to show. – Louis W Jun 26 '09 at 13:38
1

I had to use Keith's solution with combination of azkotoki's and Zsolti's posts above to make everything work as required.

so, final example would be:

<?php

    // Test image.
    $fn = '/test/foo.png';

    session_cache_limiter(false);
    header('Cache-Control: private');

    // Getting headers sent by the client.
    $headers = apache_request_headers();

    // Checking if the client is validating his cache and if it is current.
    if (isset($headers['If-Modified-Since']) && (strtotime($headers['If-Modified-Since']) == filemtime($fn))) {
        // Client's cache IS current, so we just respond '304 Not Modified'.
        header('Last-Modified: '.gmdate('D, d M Y H:i:s', filemtime($fn)).' GMT', true, 304);
    } else {
        // Image not cached or cache outdated, we respond '200 OK' and output the image.
        header('Last-Modified: '.gmdate('D, d M Y H:i:s', filemtime($fn)).' GMT', true, 200);
        header('Content-Length: '.filesize($fn));
        header('Content-Type: image/png');
        print file_get_contents($fn);
    }

?>
Satish Gadhave
  • 2,880
  • 3
  • 20
  • 27
  • This worked for me, combined with using $_SERVER["HTTP_IF_MODIFIED_SINCE"] instead of $headers – nsheff Sep 15 '13 at 20:25
1

After searching for a while, I've found the answer. The browser didn't cached anything (and didn't send the If-Modified-Since) until I sent the following header:

Cache-Control: private;

After doing this all worked fine.

Zsolti
  • 1,571
  • 1
  • 11
  • 22
0

Check if sessions are being used on that page, If so, try this:

session_cache_limiter(false);

If the above worked, here's the explanation:

Php's session mechanism sends some automatic cache-related headers in order to improve the session cookie privacy, avoiding it to be cached by intermediate proxyes:

http://php.net/manual/en/function.session-cache-limiter.php

These automatic headers cause the browser not to ever send the If-Modified-Since header, as they instruct it to not to perform any caching at all.

azkotoki
  • 2,357
  • 2
  • 22
  • 26