304: Not modified and front end caching
Asked Answered
E

6

15

I am using a PHP script to serve files. I would like to be able to send back a 304 not modified header in my http response if the file has not changed since the client last downloaded it. This seems to be a feature in Apache (and most other web servers), but I have no clue how this can be implemented through PHP.

I have heard of using $_SERVER['HTTP_IF_MODIFIED_SINCE'], but this variable does not seem to appear in my $_SERVER super array.

My question is not how to return a 304 header, but how to know that one should be returned.


Edit: The problem is that my $_SERVER['HTTP_IF_MODIFIED_SINCE'] is not set. This is the content of my .htaccess file:

ExpiresActive On 
ExpiresByType image/jpeg "modification plus 1 month"
ExpiresByType image/png "modification plus 1 month"
ExpiresByType image/gif "modification plus 1 month"
Header append Cache-Control: "must-revalidate" 


<IfModule mod_rewrite.c>
   RewriteEngine On
   RewriteCond $1 !^(controller\.php)
   RewriteRule (.*\.jpg|.*\.png|.*\.gif) controller.php/$1
</IfModule>

HTTP_IF_MODIFIED_SINCE still does not appear in the $_SERVER super array.

Enamel answered 18/10, 2009 at 1:38 Comment(0)
C
27

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 PHP.net:

$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"); 
    exit; 
}

// output data
Cesura answered 18/10, 2009 at 1:46 Comment(8)
I have seen this way of doing this, but $_SERVER['HTTP_IF_MODIFIED_SINCE'] is always empty for me...Enamel
Check my recent edit -- I left out a piece of important Apache config info.Neolith
Tried to modify my htaccess.txt file as per webmasterworld.com/forum23/2300.htm, but no go. Any ideas?Enamel
There's no such thing as htaccess.txt file. It must be .htaccess filePompano
Just a small comment, if any have the same problem as I had : you must ALWAYS define lastModify header. I was doing it after the if statement. (I know it sounds weird, because then the cache will never gets deleted, but that's the plan :) )Adrianaadriane
Thanks c_harm, one issue is that i needed to escape the trim() too. If you force-reload in Chrome (shift-reload) then chrome won't send the IF_MODIFIED header to fetch fresh data. The if() will then check the trim() statement but returns an error on SERVER[NONE_MATCH] not being set (Chrome and others don't send the Etag header). This was somewhat hard to debug at first, because i was sending raw image data.Wirth
The very important part of this good answer is "always send headers" about the "Last-Modified". Without sending this header, you will NEVER get HTTP_IF_MODIFIED_SINCE in the request because the browser will don't send it.Hajj
Good! The Last-Modified header made it work in my case.Adularia
C
1

This article will answer all your questions on caching

I found that adding

RewriteRule .* - [E=HTTP_IF_MODIFIED_SINCE:%{HTTP:If-Modified-Since}]
RewriteRule .* - [E=HTTP_IF_NONE_MATCH:%{HTTP:If-None-Match}]

To the bottom of my htaccess file (below all rewriterule) worked.

Cerebrospinal answered 18/12, 2011 at 19:20 Comment(0)
C
1

I had this problem and it turned out to simply be that I had Firebug open. This has an option under the Net tab "Disable Browser Cache" that is ticked by default. There is a similar option in Chrome's developer tools, one of the tick boxes on the bar under the menu bar.

Unticking these options resulted in the browser correctly sending HTTP_IF_MODIFIED_SINCE and everything working fine after all (even with Firebug or Chrome Dev Tools open).

Choanocyte answered 7/12, 2016 at 13:21 Comment(0)
E
0

$_SERVER['HTTP_IF_MODIFIED_SINCE'] is usually empty when register_globals is off.

Check whether that's the case, and if so try getenv('HTTP_IF_MODIFIED_SINCE')

Embranchment answered 18/10, 2009 at 2:59 Comment(0)
V
0

There are also some others parameters to check .. in my case I didn't had both of those headers :

$_SERVER['HTTP_IF_NONE_MATCH'] && $_SERVER['HTTP_IF_MODIFIED_SINCE']

which are required to return a proper 304 header, as my system clock was a little late, It'll interpret those pages as expiring in the future, then not sending those values at all

Also check that header which is returned by apache, or at least override it to a bigger value

Cache-Control: max-age=3600

As it won't send previous headers if

Last-Modified previous sent header < ( NOW - 3600 )

So in my case I've set this pretty handy function function lastModified($file){ $x=filemtime($file); while($x>time())$x-=86000;}#reduce by one day if touched in future date $date=gmdate('D, j M Y H:i:s',$x).' GMT'; header('Cache-Control: max-age=86000',1); if($_SERVER['HTTP_IF_NONE_MATCH'] == $x || $_SERVER['HTTP_IF_MODIFIED_SINCE']==$date){ header('HTTP/1.1 304 Not Modified',1,304);die;} header('Etag: '.$x,1);header('Last-Modified: '.$date,1); }

Vasoconstrictor answered 22/5, 2015 at 15:37 Comment(0)
H
0

Note that $_SERVER["HTTP_IF_NONE_MATCH"] can contain quotes and -gzip suffix.

$server_etag = str_replace("-gzip", "", str_replace('"', '', stripslashes($_SERVER['HTTP_IF_NONE_MATCH'])));

if ($server_etag == $etag) ...
Helyn answered 26/2, 2019 at 15:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.