php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #17098 apache sending 304 - not modified header
Submitted: 2002-05-08 10:36 UTC Modified: 2002-12-29 15:56 UTC
Votes:54
Avg. Score:4.8 ± 0.6
Reproduced:51 of 51 (100.0%)
Same Version:14 (27.5%)
Same OS:30 (58.8%)
From: pmoor at netpeople dot ch Assigned:
Status: Closed Package: Apache2 related
PHP Version: 4.0CVS-2002-10-17 OS: linux
Private report: No CVE-ID:
 [2002-05-08 10:36 UTC] pmoor at netpeople dot ch
using phpmyadmin with apache 2.0.35 and php4.3-dev results in pages not being reloaded, when once requested.

if i drop an entry in an mysql table, and re-list the table, the old entry is still showing up (the browser is displaying the old page instead of a reloaded one). this is, because apache sends a 304 nod modified header. verified with mozilla-latest-1.0.0.

it worked great with apache 1.3.24 though...

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2002-05-21 17:05 UTC] info at tphnet dot com
Same problem here. You can easily test it by taking one of your php files and removing a ';' somewhere. Load the page in the browser and you'll see an error. Correct the error in the php file and reload the page in you browser. You still get the same error. Manually clearing the browser cache is (as far as i have tested) the only solution.
Strange thing is, that when i use PHPmyAdmin, dynamic pages aren't updated (403 not modified). But when I use some of my own script, dynamic pages do get updated. They only get stuck in the cache if I edit the php file itself (as described above).

Running Apache 2.0.35 and PHP 4.2.0 on Windows NT 5.1. The same install of PHP running on Apache 1.3.24 doesn't give any problems.
 [2002-06-07 16:25 UTC] ck-phpbugs at newsclub dot de
See Apache2 bug #9673
http://nagoya.apache.org/bugzilla/show_bug.cgi?id=9673

Same problem here, seems to be a problem with Apache2 and the filter concept.

You can have a quick workaround for the moment -
just comment out the If-Modified-Since handling stuff in Apache2:

Comment out line 393 in modules/http/http_protocol.c, (return HTTP_NOT_MODIFIED). Now, Apache2 does never return 304 Not modified.

And hey, this is a preliminary hot fix only!

Please also check the suggestions of Justin Erenkrantz (see Apache bug report), I haven't had the time yet to do so.
 [2002-07-07 21:10 UTC] sniper@php.net
Does this happen with latest CVS checkouts of both Apache2 and PHP?

 [2002-07-10 13:31 UTC] pmoor at netpeople dot ch
still doesn't work with php4 and apache2 checkouts from July 10th.
 [2002-07-10 13:37 UTC] pmoor at netpeople dot ch
however: it doesn't work for phpmyadmin ( still displaying old query results ), BUT at least one of my regular scripts ( just some simple task ) does indeed work: apache's returning the expected 200 OK response.
 [2002-07-10 13:43 UTC] sander@php.net
Status -> Open
Apache devs are still argueing whether this is a bug in Apache or in PHP :)
 [2002-07-20 10:54 UTC] daniel dot eckl at gmx dot de
I'd like to post a workaround without patching apache or PHP....

Just edit your script(s) to send a 'header("Last-Modified: Mon, 26 Jul 1997 05:00:00 GMT");' or just some other date older than the mdate of your script file. This solves the problem.

Reason:
The bug causes Apache2 to look for the mdate of the .php file to determine if it has been modified.
If the browser first gets a header like above, it next time asks for the page with an 'If-Modified-Since: Mon, 26 Jul 1997 05:00:00 GMT'. Then, the httpd looks at the mdate of your script, which is always newer and says: Yes, it has been modified, "200 OK". The script will be served and it will response again with the header line from above. Round and round the story goes. :))

Greets, and have fun!

Daniel
 [2002-08-15 13:30 UTC] duke at mastre dot com
3+ months later and this still happens with Apache 2.0.40 and PHP snapshot php4-200208150600 !!!  Seems that PHP developers view Apache2 as pre-alpha and they don't want to touch it with a 10-foot stick.
 [2002-08-16 03:56 UTC] markonen@php.net
Sir, you are correct in your analysis.
 [2002-09-30 20:24 UTC] iliaa@php.net
Sorry, but the bug system is not the appropriate forum for asking
support questions. Your problem does not imply a bug in PHP itself.
For a list of more appropriate places to ask for help using PHP,
please visit http://www.php.net/support.php

Thank you for your interest in PHP.

This is an Apache 2 bug. 
According to http://nagoya.apache.org/bugzilla/show_bug.cgi?id=9673 this bug has been corrected in Apache 2.0.42 and later.
 [2002-10-14 11:00 UTC] rt at takagi-ryo dot ac
This is still not fixed... PHP code needs modifying.
I have tried PHP 4.2.3 and 4.3.0-pre1 with Apache 2.0.43
running on RedHat 6.2, and it still returns erroneous 304s.

The following is what I 'borrowed' from the Apache's
mod_include.c, which seems working within PHP 4.2.3.


--- sapi/apache2filter/sapi_apache2.c~  Fri Aug 16 07:27:03 2002
+++ sapi/apache2filter/sapi_apache2.c   Mon Oct 14 23:27:26 2002
@@ -558,14 +558,24 @@
        return OK;
 }

+static int includes_setup(ap_filter_t *f)
+{
+    /* We will ALWAYS set the no_local_copy value to 1 so
+     * that we will not send 304s.
+     */
+    f->r->no_local_copy = 1;
+
+    return OK;
+}
+
 static void php_register_hook(apr_pool_t *p)
 {
        ap_hook_pre_config(php_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
        ap_hook_post_config(php_apache_server_startup, NULL, NULL, APR_HOOK_MIDDLE);
        ap_hook_insert_filter(php_insert_filter, NULL, NULL, APR_HOOK_MIDDLE);
        ap_hook_post_read_request(php_post_read_request, NULL, NULL, APR_HOOK_MIDDLE);
-       ap_register_output_filter("PHP", php_output_filter, NULL, AP_FTYPE_RESOURCE);
-       ap_register_input_filter("PHP", php_input_filter, NULL, AP_FTYPE_RESOURCE);
+       ap_register_output_filter("PHP", php_output_filter, includes_setup, AP_FTYPE_RESOURCE);
+       ap_register_input_filter("PHP", php_input_filter, includes_setup, AP_FTYPE_RESOURCE);
 }

 AP_MODULE_DECLARE_DATA module php4_module = {
 [2002-10-17 17:54 UTC] yohgaki@php.net
Although, we need other patch, it seems the problem exists.
 [2002-10-17 19:27 UTC] yohgaki@php.net
It would be Apache2 bug

------------
On October 17, 2002 07:06 pm, Yasuo Ohgaki wrote:

> Andrei Zmievski wrote:

> > Hi,
> >
> > We have 10 critical bugs in the list currently. If you could please see
> > about fixing at least one of them, we'd be that much closer to a release
> > candidate.

>
> Summary: Apache2 sending 304 - not modified header
> http://bugs.php.net/bug.php?id=17098
>
> This is serious problem for serious sites.
> (Serious sites shouldn't use Apache2, though)


This looks like an Apache 2 bug, rather then aPHP one. I am guessing the fix 
they made did not work properly.

Ilia



 [2002-10-18 05:43 UTC] yohgaki@php.net
Ok. We should do something about this bug, I suppose.

Mail from Ryou Takagi
========= BEGIN ========
Yasuo Ohgaki wrote:

> Ilia A. wrote:

> >>Summary: Apache2 sending 304 - not modified header
> >>http://bugs.php.net/bug.php?id=17098
> >>
> >>This is serious problem for serious sites.
> >>(Serious sites shouldn't use Apache2, though)

> > 
> > 
> > This looks like an Apache 2 bug, rather then aPHP one. I am guessing the fix 
> > they made did not work properly.

> 
> Great!
> Then we can just wait their fix :)


I am afraid this is not the case. This is the report of the status.

As from Apache 2.0.40, the API of the filter registration functions
has changed. The changelog says:

--- From Apache Changelog: in section "Changes with Apache 2.0.40" ---
  *) Add a filter_init parameter to the filter registration functions
     so that a filter can execute arbitrary code before the handlers
     are invoked.  This resolves a problem where mod_include requests
     would incorrectly return a 304.  [Justin Erenkrantz]
--- End quotation from Apache Changelog ---

And the current mod_include.c in Apache 2.0.43 source tree uses this
API like this:

--- From modules/filters/mod_include.c in Apache 2.0.43 source tree ---
static int includes_setup(ap_filter_t *f)
{
    include_dir_config *conf =
               (include_dir_config *)ap_get_module_config(f->r->per_dir_config,
                                                          &include_module);

    /* When our xbithack value isn't set to full or our platform isn't
     * providing group-level protection bits or our group-level bits do not
     * have group-execite on, we will set the no_local_copy value to 1 so
     * that we will not send 304s.
     */
    if ((*conf->xbithack != xbithack_full)
        || !(f->r->finfo.valid & APR_FINFO_GPROT)
        || !(f->r->finfo.protection & APR_GEXECUTE)) {
        f->r->no_local_copy = 1;
    }

    return OK;
}

/* Note from TAKAGI: several lines omitted */

static void register_hooks(apr_pool_t *p)
{
    APR_REGISTER_OPTIONAL_FN(ap_ssi_get_tag_and_value);
    APR_REGISTER_OPTIONAL_FN(ap_ssi_parse_string);
    APR_REGISTER_OPTIONAL_FN(ap_register_include_handler);
    ap_hook_post_config(include_post_config, NULL, NULL, APR_HOOK_REALLY_FIRST);
    ap_hook_fixups(include_fixup, NULL, NULL, APR_HOOK_LAST);
    ap_register_output_filter("INCLUDES", includes_filter, includes_setup,
                              AP_FTYPE_RESOURCE);
}
--- End quotation from modules/filters/mod_include.c ---

And Justin Erenkrantz himself stated in the email as follows:
http://www.phpbuilder.com/mail/php-developer-list/2002062/0392.php
  "I will try to work on a more complete fix for PHP this weekend 
   as it is susceptable to the same invalid 304 problem mod_include 
   was. (Just create a simple filter_init function that always sets 
   r->no_local_copy to 1.)"

So, I do believe that some modification is necessary where the
sapi/apache2filter/sapi_apache2.c calls API functions
ap_register_{input|output}_filter(...). What I am not sure is whether it
is necessary to modify both the "input" and "output" functions or not.



To reproduce the problem:

The script:
--- Script test.phtml ---
<?php
$fmt = filemtime ( $_SERVER [ 'SCRIPT_FILENAME' ] ) ;
$ludate = $fmt + 600 ;
header ( "content-type: text/html; charset=ISO-8859-1" ) ;
header ( "Last-Modified: " . gmdate ( 'D, d M Y H:i:s \G\M\T', $ludate ) ) ;
print ( "<html><head><title>Test</title></head><body>\n" ) ;
print ( "File timestamp: " . gmdate ( 'D, d M Y H:i:s \G\M\T', $fmt ) ) ;
print ( "<br>" ) ;
print ( "Last updated: " . gmdate ( 'D, d M Y H:i:s \G\M\T', $ludate ) ) ;
print ( "</body></html>\n" ) ;
?>
--- End script ---

Set the timestamp of the script file properly.

If you access the script using a browser, it displays:

--- Browser displays ---
File timestamp: Fri, 18 Oct 2002 09:00:00 GMT
Last updated: Fri, 18 Oct 2002 09:10:00 GMT
--- End browser displays ---

Access the server using "telnet":

--- Telnet servername http requests and results before patch ---
[rt@ns rt]$ telnet localhost http
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
HEAD /test.phtml HTTP/1.1
Host: localhost.localdomain

HTTP/1.1 200 OK
Date: Fri, 18 Oct 2002 09:22:11 GMT
Server: Apache/2.0.43 (Unix) mod_ssl/2.0.43 OpenSSL/0.9.6g PHP/4.3.0-dev
Accept-Ranges: bytes
X-Powered-By: PHP/4.3.0-dev
Last-Modified: Fri, 18 Oct 2002 09:10:00 GMT
Content-Type: text/html; charset=ISO-8859-1

HEAD /test.phtml HTTP/1.1
Host: localhost.localdomain
If-Modified-Since: Fri, 18 Oct 2002 08:59:00 GMT

HTTP/1.1 200 OK
Date: Fri, 18 Oct 2002 09:22:21 GMT
Server: Apache/2.0.43 (Unix) mod_ssl/2.0.43 OpenSSL/0.9.6g PHP/4.3.0-dev
Accept-Ranges: bytes
X-Powered-By: PHP/4.3.0-dev
Last-Modified: Fri, 18 Oct 2002 09:10:00 GMT
Content-Type: text/html; charset=ISO-8859-1

HEAD /test.phtml HTTP/1.1
Host: localhost.localdomain
If-Modified-Since: Fri, 18 Oct 2002 09:01:00 GMT

HTTP/1.1 304 Not Modified
Date: Fri, 18 Oct 2002 09:22:41 GMT
Server: Apache/2.0.43 (Unix) mod_ssl/2.0.43 OpenSSL/0.9.6g PHP/4.3.0-dev
ETag: "8598-1e2-ca628400"

--- End Telnet ---

As is evident in the last response from the server, the
If-Modified-Since: header is not properly processed.

After the patch is applied, the problem disappears:

--- Telnet servername http requests and results after patch ---
[rt@ns rt]$ telnet localhost http
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
HEAD /test.phtml HTTP/1.1
Host: localhost.localdomain

HTTP/1.1 200 OK
Date: Fri, 18 Oct 2002 09:18:00 GMT
Server: Apache/2.0.43 (Unix) mod_ssl/2.0.43 OpenSSL/0.9.6g PHP/4.3.0-dev
Accept-Ranges: bytes
X-Powered-By: PHP/4.3.0-dev
Last-Modified: Fri, 18 Oct 2002 09:10:00 GMT
Content-Type: text/html; charset=ISO-8859-1

HEAD /test.phtml HTTP/1.1
Host: localhost.localdomain
If-Modified-Since: Fri, 18 Oct 2002 09:09:00 GMT

HTTP/1.1 200 OK
Date: Fri, 18 Oct 2002 09:18:19 GMT
Server: Apache/2.0.43 (Unix) mod_ssl/2.0.43 OpenSSL/0.9.6g PHP/4.3.0-dev
Accept-Ranges: bytes
X-Powered-By: PHP/4.3.0-dev
Last-Modified: Fri, 18 Oct 2002 09:10:00 GMT
Content-Type: text/html; charset=ISO-8859-1

--- End Telnet ---

The test result was obtained using CVS 20021018 (today's one). The
system used is Red Hat Linux 6.2.

Regards
Ryo

-- R Takagi <rt@takagi-ryo.ac> 
 [2002-10-18 06:34 UTC] mammal at vol dot cz
I tried a patch submited by rt@takagi-ryo.ac
and it seems to solve the problem. I don't know to what extent, but the logic of it seems ok to me...
 [2002-12-03 09:28 UTC] alex at taylexson dot org
Maybe I missed something.  What patch?  Is this still a php bug?  I have Apache 2.0.40 and php-4.4-2.
 [2002-12-13 18:24 UTC] daniel dot eckl at gmx dot de
This bug is _NOT_ fixed in 4.3.0 rc3!

In 4.3.0, the apache2 support should not be experimental anymore, so I think, this is a real showstopper IMHO.

I think, it's time to fix this issue now, it's so annoying and unneccessary. If this patch has any known drawbacks that I'm not aware of, then it's NOT the correct solution to simply ignore this subject as whole.

Daniel

Here is the patch again as diff against php 4.3.0 rc 3:

--- sapi/apache2filter/sapi_apache2.c.old       Thu Dec 12 21:48:58 2002
+++ sapi/apache2filter/sapi_apache2.c   Thu Dec 12 21:50:43 2002
@@ -619,14 +619,24 @@
        return OK;
 }

+static int includes_setup(ap_filter_t *f)
+{
+    /* We will ALWAYS set the no_local_copy value to 1 so
+     * that we will not send 304s.
+     */
+    f->r->no_local_copy = 1;
+
+    return OK;
+}
+
 static void php_register_hook(apr_pool_t *p)
 {
        ap_hook_pre_config(php_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
        ap_hook_post_config(php_apache_server_startup, NULL, NULL, APR_HOOK_MIDDLE);
        ap_hook_insert_filter(php_insert_filter, NULL, NULL, APR_HOOK_MIDDLE);
        ap_hook_post_read_request(php_post_read_request, NULL, NULL, APR_HOOK_MIDDLE);
-       ap_register_output_filter("PHP", php_output_filter, NULL, AP_FTYPE_RESOURCE);
-       ap_register_input_filter("PHP", php_input_filter, NULL, AP_FTYPE_RESOURCE);
+       ap_register_output_filter("PHP", php_output_filter, includes_setup, AP_FTYPE_RESOURCE);
+       ap_register_input_filter("PHP", php_input_filter, includes_setup, AP_FTYPE_RESOURCE);
 }

 AP_MODULE_DECLARE_DATA module php4_module = {
 [2002-12-25 18:03 UTC] daniel dot eckl at gmx dot de
... and it's not fixed in 4.3.0 RC4 either...

Daniel
 [2002-12-27 13:55 UTC] daniel dot eckl at gmx dot de
... and the bug is present in 4.3.0 release.
 [2002-12-27 14:13 UTC] iliaa@php.net
This bug has been fixed in CVS.

In case this was a PHP problem, snapshots of the sources are packaged
every three hours; this change will be in the next snapshot. You can
grab the snapshot at http://snaps.php.net/.
 
In case this was a documentation problem, the fix will show up soon at
http://www.php.net/manual/.

In case this was a PHP.net website problem, the change will show
up on the PHP.net site and on the mirror sites in short time.
 
Thank you for the report, and for helping us make PHP better.


 [2002-12-29 15:51 UTC] dk at webcluster dot at
Isn't fixed in php4-STABLE-200212290030

Daniel [datenPUNK] Khan
 [2002-12-29 15:56 UTC] derick@php.net
Try a NON stable snapshot, it's fixed in CVS>

Derick
 [2003-01-03 13:44 UTC] pmichaud at pobox dot com
As another possible temporary workaround, I tried adding the directive

   RequestHeader unset If-Modified-Since

into my Apache configuration when processing PHP scripts.  This removes the If-Modified-Since header entirely, causing Apache to always return a 200 response.  The resulting configuration looks like:

<Files *.php>
    SetOutputFilter PHP
    SetInputFilter PHP
    LimitRequestBody 524288
    RequestHeader unset If-Modified-Since
</Files>

Anyone know of a reason why this might be bad or otherwise won't work?

Pm
 [2003-01-09 14:45 UTC] daniel dot eckl at gmx dot de
I tried this and tested it with Horde's IMP which heavily needs correct caching behavior. So far it seems to behave very smoothly. I'm impressed of this idea.

I took some time but couldn't think of any drawbacks caused by this. Because the unsetting is only internal, there should be no problems with search spiders etc...

Any other opinions?

Greets,
Daniel
 [2003-06-24 20:13 UTC] davisneilp at mindspring dot com
All, 
IIS responds the same way. I think this is more a case of reading the "manual" (w3c http spec). The reason apache "worked" before was that there was probably a bug in apache that sent the content when it was supposed to send a 304 only.

When you do a browser refresh, you are sending a "If-None-Match: [etag]" header, if an etag was originally sent with the content, which it probably wasn't. You also send a "If-modified-since: [GMT HTTP DATE]" with a refresh. If the file date hasn't changed, and is within the expires time originally sent (usually a default is sent), the page won't "actually" refresh. 

This problem lies with the improper use (or lack thereof) of cache control headers. Try sending an etag with all of your content, and no expires header. For production make sure you send an Expires header: )

This Etag will change, if you set it based on the content, and inspire IE to refresh the page. It seems to go in http 1.0, then http 1.1 order, with IE, so if you want it to really work, make sure you aren't sending an expires header. Send an Etag only from your development server.

Clearing your cache works because it forces the server to send the content. It doesn't have the option of sending a 304 if you don't send an If-none-match, or If-modified-since headers, neither of which is possible if you have deleted the data and content from your box: )

An often misunderstood aspect of intentionally sending 304s is that Apache is actually "dumb" in this respect. If you are not using php's output buffer, it is impossible to stop the content from going out. You should really use a callback function and set it in ob_start("function name"). When sending a 304 return null out of your function and the 304 will be sent, but not the content. Apache happily sends out the content with your 304 header otherwise. 

Using a call back function gives you a lot more control of the flow of bytes out of your server.

Not doing this wastes bandwidth and server connection resources, especially considering that connection taken while the 14.4 modem user downloads your 40k page. If you capture the output and stop it, you only send the 304 and the connection is freed up. It is a lot faster to send, say, a 250 byte header than the entire page. This frees up the connection resource much faster and enables your server to scale more efficiently.

It would be nice if the php team made php do this 304 header/output stuff automatically. I can help if you need it: ) it will save your users some head scratching if you add this feature. It took me an hour or two to figure out what was really happening and coded it.

wget -S is your friend, use it!

l8,
neil
 
PHP Copyright © 2001-2014 The PHP Group
All rights reserved.
Last updated: Sat Apr 19 22:02:16 2014 UTC