Created attachment 24037 [details] PHP script to demonstrate the problem If you make a conditional request for a cached document, but the document is expired in the cache, mod_cache correctly passes on the conditional request to the backend. If the backend responds with a "304 Not Modified" response that indicates that the cached copy is still up to date, mod_cache serves the contents of the cache to the client with a 200 code. This is a standards-conforming valid way of responding. But couldn't it just send a "304 Not Modified" code instead? RFC2616 14.26 says "instead, if the request method was GET or HEAD, the server SHOULD respond with a 304 (Not Modified) response, including the cache- related header fields (particularly ETag) of one of the entities that matched." The current behaviour unnecessarily sends a response body to the client. This ends up wasting bandwidth in the case where you press refresh on an unmodified object (at least in Firefox,) which sends these request headers: If-None-Match="My ETag" Cache-Control=max-age=0 I do not want the behaviour given by the "CacheIgnoreCacheControl yes" directive. I still want mod_cache to validate the request against the backend, but I don't want it to waste bandwidth by sending a 200 response code. To test it, I have these cache-related lines in my virtual host definition: CacheRoot C:/temp CacheEnable disk / My index.php is the attached file. My web browser with an empty cache requests index.php, the (trimmed) response is: Status=OK - 200 Date=Mon, 20 Jul 2009 07:16:05 GMT Expires=Wed, 19 Aug 2009 07:16:05 GMT Etag="ComputedETag" The log performed by index.php indicates: Mon, 20 Jul 2009 19:16:05 +1200 - Response: 200. Generated document. So far so good. But now I press refresh in my web browser. This makes a conditional request for the document: If-None-Match="ComputedETag" Cache-Control=max-age=0 With the max-age of 0, the cache will be bypassed, which is the desired behaviour. The cache passes this conditional request onto the backend, and the backend logs it: Mon, 20 Jul 2009 19:16:12 +1200 - Response: 304 Not Modified So the backend is trying to tell the client that it already has an up-to-date body. But the response sent to the client by mod_cache is: Status=OK - 200 Date=Mon, 20 Jul 2009 07:16:12 GMT Etag="ComputedETag" Expires=Wed, 19 Aug 2009 07:16:12 GMT My Apache configuratiion is: Apache/2.2.11 (Win32) DAV/2 mod_ssl/2.2.11 OpenSSL/0.9.8i SVN/1.6.3 PHP/5.3.0
This is a bug in php or more specific in the httpd module version of php. In sapi_apache2.c::php_apache_request_ctor (line 463 for php 5.3.0) it sets r->no_local_copy to 1 for an unknown reason which causes to return a 200 instead of a 304. If you comment this line everything works as expected by you. httpd itself only set this struct member to 1 for subrequests error pages in both cases it is not desired that even conditional requests return a 304. For subrequests as they only deliver fragments of a page that is processed internally and for error pages this is obvious. In order to fix your problem do one of the following things: 1. Use the CGI/FASTCGI version of PHP. 2. Comment the line in the PHP code as described above (no idea which further sideeffects this has as I am not a php developer). 3. Open a bug report at bugs.php.net to get this fixed.
For anyone following this bug, this is now PHP bug #49106: http://bugs.php.net/bug.php?id=49106
Confirming that this defect still exists in Apache 2.2.21 with mod_cache enabled. Browser sends requests with If-None-Match headers that correspond with ETag for content. mod_cache is configured to ignore all headers from request and response that could cause a cache miss. mod_cache returns cache hit from the cache, in my test case disk cache, however also reproducible with a mem cache. The cache hit despite matching the If-None-Match in the request returns with a response code of 200 and sends the full body in the response, instead of a terse/concise 304. This has nothing to do with PHP which it looks like Nicholas Sherlock had originally attached to recreate the problem. I can recreate the same problem with Fiddler.
Workaround to this bug in mod_cache: Disable ETags and If-None-Match headers for 304 checking Instead use If-Modified-Since and Last-Modified headers. #To deal with caches already in my users browser I used this config before hitting #mod_cache to ensure they don't get included in the request and confuse mod_cache RequestHeader unset If-None-Match #This cleans ETag headers from any apps downstream from my Apache incase they generated them. Header unset ETag With the If-Modified-Since and Last-Modified headers mod_cache appears to be properly writing back files from its cache and also writing back 304 HTTP Status codes when serving files from its cache that already match with the ones on the browser.
this solution works RequestHeader unset If-None-Match Header unset ETag but the problem still exist on 2.2.25 Ubuntu 14.04 Thx for the solution above :)