Results 1 to 7 of 7

Thread: Images + caching

  1. #1
    Join Date
    May 2005
    Posts
    17
    Thanks
    0
    Thanked 0 Times in 0 Posts

    Default Images + caching

    I'm trying to force the browsers to cache the images I'm displaying through a PHP script.

    So far, I have this... and it's not quite working. For some reason the browser will redownload them every couple times, instead of every 3 days:

    (BTW, ob_get_contents() holds the image data.)

    Code:
    	$expires = 60 * 60 * 24 * 3;
    	$exp_gmt = gmdate("D, d M Y H:i:s", time() + $expires )." GMT";
    	$mod_gmt = gmdate("D, d M Y H:i:s", time() + (3600 * -5 * 24 * 365) )." GMT";
    
    
    	@header("Content-type: image/jpg");
    	@header("Content-Disposition: inline; filename=purrun.jpg");
    	@header("Expires: {$exp_gmt}");
    	@header("Last-Modified: {$mod_gmt}");
    	@header("Cache-Control: public, max-age={$expires}");
    	@header("Pragma: !invalid");
    	@header("Content-Length: {$size}");
    	@header("ETag: " . md5( ob_get_contents() ) );

    Anyone mind giving me a hand? Thanks.

  2. #2
    Join Date
    Jun 2005
    Location
    英国
    Posts
    11,878
    Thanks
    1
    Thanked 180 Times in 172 Posts
    Blog Entries
    2

    Default

    It's the curly braces ( {} ).
    Using double quotes ( "" ) automatically parses variables defined with $. There's no need to put the braces in. The header is sent as Expires: {Mon, 26 Jul 1997 05:00:00 GMT}, and the browser becomes confused by the curly braces and ignores the Expires header.
    /edit: could be wrong about what this does. In any case, the braces are unnecessary.

    P.S. silencing everything is silly. If you must, define a blank error-handler like so:
    PHP Code:
    function errorHandler ($errno$errstr$errfile$errline$errcontext) {
    }
    set_error_handler('errorHandler'); 
    ... or, better yet, actually fix the errors.
    Last edited by Twey; 07-08-2005 at 04:41 PM.
    Twey | I understand English | 日本語が分かります | mi jimpe fi le jbobau | mi esperanton komprenas | je comprends franšais | entiendo espa˝ol | t˘i Ýt hiểu tiếng Việt | ich verstehe ein bisschen Deutsch | beware XHTML | common coding mistakes | tutorials | various stuff | argh PHP!

  3. #3
    Join Date
    May 2005
    Posts
    17
    Thanks
    0
    Thanked 0 Times in 0 Posts

    Default

    I'll check on that. The curly braces were part of the original script I saw for caching, so I decided to keep 'em.


    I also changed it to Cache-Control: public, max-age=(something, must-revalidate... seems to be working better now, though I'm not completely sure.


    Thanks.

  4. #4
    Join Date
    Jun 2005
    Location
    英国
    Posts
    11,878
    Thanks
    1
    Thanked 180 Times in 172 Posts
    Blog Entries
    2

    Default

    You can check the headers by opening a command prompt/shell and telneting to your web server, then requesting the page. Like so:

    Code:
    > telnet
    telnet> open myhost.com 80
    
    Trying x.x.x.x...
    Connected to myhost.com (x.x.x.x).
    
    window waits for input
    GET /path/mypage.php HTTP/1.1
    Accept: */*
    Host: myhost.com
    Connection: Close
    two linebreaks
    
    
    HTTP/1.1 200 OK
    Transfer-Encoding: chunked
    Date: Fri, 08 Jul 2005 16:48:29 GMT
    Content-Type: text/html; charset=iso-8859-1
    Server: Apache/1.3.33 (Unix) mod_auth_passthrough/1.8 mod_log_bytes/1.2 mod_bwlimited/1.4 PHP/4.3.10 FrontPage/5.0.2.2635 mod_ssl/2.8.22 OpenSSL/0.9.6b
    X-Powered-By: PHP/4.3.10
    
    <html>
    ...
    Text in bold is user input, text in italic is comments by me, ignore. Headers obviously won't be the same as from my server.

    You can also get various browser plugins, such as the header monitor for Firefox.
    Alternatively, you can use a packet sniffer. But if you can type fast enough to avoid your server disconnecting you, telnet's easiest and looks coolest if you've got someone looking over your shoulder
    Last edited by Twey; 07-08-2005 at 05:32 PM.
    Twey | I understand English | 日本語が分かります | mi jimpe fi le jbobau | mi esperanton komprenas | je comprends franšais | entiendo espa˝ol | t˘i Ýt hiểu tiếng Việt | ich verstehe ein bisschen Deutsch | beware XHTML | common coding mistakes | tutorials | various stuff | argh PHP!

  5. #5
    Join Date
    Dec 2004
    Location
    UK
    Posts
    2,358
    Thanks
    0
    Thanked 0 Times in 0 Posts

    Default

    Quote Originally Posted by Twey
    It's the curly braces ( {} ).
    Using double quotes ( "" ) automatically parses variables defined with $. There's no need to put the braces in.
    Whilst you're correct that the braces aren't necessary, you're wrong in that they are the problem. The braces are part of the variable expansion scheme, and are sometimes very necessary. See the section on complex expansion syntax in the PHP documentation.


    As for the actual problem, I can't really pin anything specific down but I haven't tried experimenting yet (been distracted ). There are a few minor issues, though. Some may have an effect, some certainly won't.

    Quote Originally Posted by holobyted
    header("Content-type: image/jpg");
    The MIME type is wrong. This should be:

    Code:
    header('Content-Type: image/jpeg');
    header("Content-Disposition: inline; filename=purrun.jpg");
    A disposition type of inline has no meaning in HTTP. This header should be removed.

    $mod_gmt = gmdate("D, d M Y H:i:s", time() + (3600 * -5 * 24 * 365) )." GMT";
    [...]
    header("Last-Modified: {$mod_gmt}");
    The last modified date shouldn't vary unless the resource actually changes. That the date is in the past doesn't matter - the variation still invalidates the resource. Use a fixed date.

    header("Cache-Control: public, max-age={$expires}");
    Using the public directive is unnecessary. Using the max-age directive tends to imply public anyway.


    After reading the above, you might think that the obvious cause would be the changing last modified date, but it really shouldn't matter. As the expiration information will indicate a freshly cached copy, the browser shouldn't get as far as trying to revalidate.

    Are there any particular patterns to this behaviour? Is it a specific browser that keeps downloading, and do you know if the server is sending a complete response, or perhaps just 304 Not Modified?

    Cache-Control: public, max-age=(something, must-revalidate
    You don't want to send the must-revalidate directive. It won't harm cache behaviour as such, but it doesn't provide the benefits you're hoping for. Read RFC 2616 for more information (search for 'must-revalidate' in section 14.9.4).

    Mike

  6. #6
    Join Date
    May 2005
    Posts
    17
    Thanks
    0
    Thanked 0 Times in 0 Posts

    Default

    Regarding the ...
    Code:
    header("Content-Disposition: inline; filename=purrun.jpg");
    ...part, I added it so people have a "nice" default filename other than random gibberish (long story).

    Since the original posting, I decided to add an image cache and use that last modified date and base everything on that, instead of "random" values - somehow that helped.

    Thanks for all the help.


    @Twey: nothing beats telnet w/ a green-on-black theme. "Follow the white rabbit"

  7. #7
    Join Date
    Jun 2005
    Location
    英国
    Posts
    11,878
    Thanks
    1
    Thanked 180 Times in 172 Posts
    Blog Entries
    2

    Default

    Darn right
    Twey | I understand English | 日本語が分かります | mi jimpe fi le jbobau | mi esperanton komprenas | je comprends franšais | entiendo espa˝ol | t˘i Ýt hiểu tiếng Việt | ich verstehe ein bisschen Deutsch | beware XHTML | common coding mistakes | tutorials | various stuff | argh PHP!

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •