Caching image requests through PHP - If-Modified-S

2019-02-17 11:04发布

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);

}

5条回答
仙女界的扛把子
2楼-- · 2019-02-17 11:23

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.

查看更多
聊天终结者
3楼-- · 2019-02-17 11:27

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);
    }

?>
查看更多
Melony?
4楼-- · 2019-02-17 11:35

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);
        }

    ?>
查看更多
虎瘦雄心在
5楼-- · 2019-02-17 11:46

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.

查看更多
Viruses.
6楼-- · 2019-02-17 11:47

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.

查看更多
登录 后发表回答