You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@httpd.apache.org by Mark Montague <ma...@catseye.org> on 2014/08/23 04:50:30 UTC

[RFC] enhancement: mod_cache bypass

I've attached a proof-of-concept patch against httpd 2.4.10 that allows 
mod_cache to be bypassed under conditions specified in the conf files.  
It adds an optional fourth argument to the CacheEnable directive:

CacheEnable cache_type [url-string] [expr=expression]

If the expression is present, data will only be served from the cache 
for requests for which the expression evaluates to true.  This permits 
things such as:

# Only serve cached data if no (login or other) cookies are present in 
the request:
CacheEnable disk / "expr=-z %{req:Cookie}"

# Do not serve cached pages to our testing network:
<Location /some/path>
         CacheEnable disk "expr=! ( %{REMOTE_ADDR} -ipmatch 
192.168.0.0/16 )"
</Location>

Is there interest in such an enhancement?  If so, I'll make any 
requested changes to the implementation, port the patch forward to 
trunk, put in real APLOGNOs, make sure it passes the test suite, create 
a documentation patch, and create a bugzilla for all this.

-- 
   Mark Montague
   mark@catseye.org


Re: [RFC] enhancement: mod_cache bypass

Posted by Mark Montague <ma...@catseye.org>.
On 2014-08-23 12:36, Graham Leggett wrote:
> On 23 Aug 2014, at 3:40 PM, Mark Montague <ma...@catseye.org> wrote:
>
>> [root@sky ~]# httpd -t
>> AH00526: Syntax error on line 148 of /etc/httpd/conf/dev.catseye.org.conf:
>> CacheEnable cannot occur within <If> section
>> [root@sky ~]#
> The solution here is to lift the restriction above. Having a generic mechanism to handle conditional behaviour, and then having a special case to handle the same behaviour in a different way is wrong way to go.

I've looked into allowing CacheEnable directives within <If> sections.  
This can be done by removing the NOT_IN_FILES flag from the call to 
ap_check_cmd_context() in modules/cache/mod_cache.c:add_cache_enable()

The problem is that <If> sections are currently walked only during 
mod_cache's normal handler phase, not during the quick handler phase.  
It looks easy enough to add a call to ap_if_walk() to 
cache_quick_handler(), but this would add significant extra processing 
to the quick handler phase, as all <If> expressions for the enclosing 
context would be evaluated, and I think that at the end we'd have to 
discard the results that ap_if_walk() caches in the request record so 
that they can be recomputed later during normal request processing after 
all information about the request is available.   Is this acceptable?

Also, the proof of concept patch that I sent yesterday, which adds an 
expr= clause to the CacheEnable directive, records cache bypasses in the 
request notes (for logging), in the X-Cache and X-Cache-Detail headers, 
in the cache-status subprocess environment variable, and adds a new 
subprocess environment variable named cache-bypass.  If we enable using 
CacheEnable in <If> sections, conditional cache bypasses will no longer 
be called out explicitly to the server administrator; they will need to 
infer a bypass from comparing the URL path to their configuration.  I do 
not see this as a large problem, but I thought I would mention it for 
consideration.

Given these things, what thoughts does the developer community have?  
Would a patch to allow CacheEnable within <If> sections have a better 
chance of being accepted than one that adds a expr= clause to the 
CacheEnable directive?

Or should mod_cache not allow cache bypassing at all?  "Use NGINX ( 
http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_cache_bypass 
) if you want that" or "use Varnish ( 
https://www.varnish-cache.org/docs/4.0/users-guide/increasing-your-hitrate.html#cookies 
) if you want that" are answers I'm fine with, if there's no interest in 
this feature for httpd's mod_cache.

-- 
   Mark Montague
   mark@catseye.org


Re: [RFC] enhancement: mod_cache bypass

Posted by Mark Montague <ma...@catseye.org>.
On 2014-08-23 17:43, Mark Montague wrote:
>> > - Back-end sets response header "Cache-Control: max-age=0, 
>> s-maxage=14400" so that mod_cache
>> > caches the response, but ISP caches and browser caches do not.  
>> (mod_cache removes s-maxage
>> > and does not pass it upstream).
>> mod_cache shouldn’t remove any Cache-Control headers.
>
> It apparently does, although I haven't found where in the code yet. I 
> would be interested to see if anyone can reproduce my experience. As 
> far as I know, I don't have any configuration that would result in this.

Please ignore this part of my previous reply, I found out what was going on:

When the content is first requested, mod_cache has a miss and it stores 
the content.  But when it sends it on to the client, it does so without 
any Cache-control header at all:

GET /test.php HTTP/1.1
Host: dev.catseye.org

HTTP/1.1 200 OK
Date: Sat, 23 Aug 2014 22:01:26 GMT
Server: Apache/2.4
X-Cache: MISS from dev.catseye.org
X-Cache-Detail: "cache miss: attempting entity save" from dev.catseye.org
Transfer-Encoding: chunked
Content-Type: text/html;charset=UTF-8

The second time the resource was requested, it is served by mod_cache 
from the cache with the original Cache-Control header (I added "foo=1" 
to the header track this when I generated the page):

GET /test.php HTTP/1.1
Host: dev.catseye.org

HTTP/1.1 200 OK
Date: Sat, 23 Aug 2014 22:02:21 GMT
Server: Apache/2.4
Cache-Control: max-age=0, foo=1, s-maxage=14400
Content-Security-Policy: default-src 'self'; script-src 'self' 
'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 
'self' data: ; font-src 'self' data: ; report-uri /csp-report.php
Age: 54
X-Cache: HIT from dev.catseye.org
X-Cache-Detail: "cache hit" from dev.catseye.org
Content-Length: 33
Content-Type: text/html;charset=UTF-8

What was happening was that led me to assume that mod_cache was 
"editing" the header (which, I now see, it wasn't) was because I had the 
following directives that escaped my attention when I was composing my 
previous reply:

     ExpiresActive on
     ExpiresDefault "access plus 1 week"
     ExpiresByType text/html "access plus 0 seconds"

This resulted in a "Cache-control: max-age=0" header being 
unconditionally added to the response headers, even if another header 
was already there.  So for a cache miss, I would see:

Cache-control: max-age=0

while for a cache hit I would see

Cache-control: max-age=0
Cache-Control: max-age=0, foo=1, s-maxage=14400

Mystery solved.  I apologize for the red herring and waste of people's 
time and attention.

I'm still looking for a solution to the original problem:  how to 
indicate to mod_cache that cached content for a particular URL path 
should be served to some clients (ones without login cookies), but not 
to other clients (ones with login cookies).

-- 
   Mark Montague
   mark@catseye.org


Re: [RFC] enhancement: mod_cache bypass

Posted by Mark Montague <ma...@catseye.org>.
On 2014-08-23 12:36, Graham Leggett wrote:
> On 23 Aug 2014, at 3:40 PM, Mark Montague <ma...@catseye.org> wrote:
>
>> AH00526: Syntax error on line 148 of 
>> /etc/httpd/conf/dev.catseye.org.conf: CacheEnable cannot occur within 
>> <If> section 
> The solution here is to lift the restriction above. Having a generic mechanism to handle conditional behaviour, and then having a special case to handle the same behaviour in a different way is wrong way to go.

I assumed this would be OK because the Header directive has a similar 
expr=expression clause.

But, I'll look into whether if restriction on If could be removed. If I 
rewrite things to use the If directive, do you see bypass functionality 
as something worth including?  I ask because from your points below I 
get the impression that the answer is "no".


>> The proposed enhancement is about the server deciding when to serve items from the cache.  Although the client can specify a Cache-Control request header in order to bypass the server's cache, there is no good way for a web application to signal to a client when it should do this (for example., when a login cookie is set). The behavior of other caches is controlled using the Cache-Control response header.
> There is - use “Cache-Control: private”. This will tell all public caches, including mod_cache and ISP caches, not to cache content with cookies attached, while at the same time telling browser caches that they should.

The problem is not whether the content should be cached:  it should.  
The problem is, to which clients should the cached content be served?  
If the client's request does not contain a login cookie, that client 
should get the cached copy.  If the client's request does contain a 
login cookie, the cache should be bypassed and the client should get a 
copy of the resource generated specifically for it.

"Cache-Control: private" cannot be used in a request, only in a 
response, where it works as you said.  The problem is that the first 
request for a given resource where the client includes a login cookie 
gets intercepted by mod_cache and served from the cache (if you assume 
that other clients without login cookies have already requested it).  
There must therefore be some way to tell mod_cache that this client 
needs something different. One way to do this would be by having 
different URL paths for logged in versus non-logged in users, but this 
is awkward, user-visible, and may not be feasible with all web application.


> > - Back-end sets response header "Cache-Control: max-age=0, s-maxage=14400" so that mod_cache
> > caches the response, but ISP caches and browser caches do not.  (mod_cache removes s-maxage
> > and does not pass it upstream).
> mod_cache shouldn’t remove any Cache-Control headers.

It apparently does, although I haven't found where in the code yet. I 
would be interested to see if anyone can reproduce my experience. As far 
as I know, I don't have any configuration that would result in this.

httpd 2.4.10 with mod_proxy_fcgi (Fedora 19 build)
PHP 5.5.5 with PHP-FPM

Relevant configuration:

CacheEnable disk /
CacheDefaultExpire 86400
CacheIgnoreHeaders Set-Cookie
CacheHeader on
CacheDetailHeader on
     # We'll be paying attention to "Cache-Control: s-maxage=xxx" for all
     # of our caching decisions.  The browser will use max-age=yyy for its
     # decisions.  So we drop the Expires header. See the following page
     # from Google which says, "It is redundant to specify both Expires and
     # Cache-Control: max-age"
     # https://developers.google.com/speed/docs/best-practices/caching?hl=sv
Header unset Expires
RewriteRule ^(.*\.php)$ 
fcgi://127.0.0.1:9001/www/dev.catseye.org/content/$1 [P,L]

File test.php, containing:

<?php
   header( "Cache-Control: max-age=0, s-maxage=14400" );
   header( "Content-type: text/html" );
?>
<html><body>Hello!</body></html>

Browser transaction for https://dev.catseye.org/test.php:

GET /test.php HTTP/1.1
Host: dev.catseye.org
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:31.0) 
Gecko/20100101 Firefox/31.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
DNT: 1
Connection: keep-alive

HTTP/1.1 200 OK
Date: Sat, 23 Aug 2014 20:11:00 GMT
Server: Apache/2.4
Cache-Control: max-age=0
X-Cache: MISS from dev.catseye.org
X-Cache-Detail: "cache miss: attempting entity save" from dev.catseye.org
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: text/html;charset=UTF-8

And mod_cache definitely receives s-maxage from the backend:

[root@sky cache]# cat ./J/k/WPiKG0bwW@R_H4YvSOdw.header
(binary data omitted)https://dev.catseye.org:443/test.php?Cache-Control: 
max-age=0
Cache-Control: max-age=0, s-maxage=14400
Content-Security-Policy: default-src 'self'; script-src 'self' 
'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 
'self' data: ; font-src 'self' data: ; report-uri /csp-report.php
Content-Type: text/html;charset=UTF-8

Host: dev.catseye.org
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:31.0) 
Gecko/20100101 Firefox/31.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
DNT: 1

[root@sky cache]# cat ./J/k/WPiKG0bwW@R_H4YvSOdw.data
<html><body>Hello!</body></html>
[root@sky cache]#


>> - When back-end content changes (e.g., an author makes an update), the back-end invokes "htcacheclean /path/to/resource" to invalidate the cached page so that it is regenerated the next time a client requests it.
> Set your max-age correctly and this becomes unnecessary. If you have long lived resources that you want caching for a very long time, and you want to change that resource, place the version number of the resource in the URL and refer to the new URL after the change.

This is fine for JavaScript, CSS files, and images, but I'd rather have 
users see nice, human-friendly URLs in their browsers location bar, like

https://example.com/latest-news

Rather than

https://example.com/latest-news?20140823T164300

...and I certainly don't want them bookmarking the latter one.


>> - Clients have multiple cookies set.  Tracking cookies and cookies used by JavaScript should not cause a mod_cache miss.
>> - Dynamic pages that are generated when a login cookie is set should not be cached.  This is accomplished by the back-end setting the response header "Cache-Control: max-age=0”.
> This is incorrect, max-age=0 means that a cache is welcome to cache the content, but the content must be declared stale immediately and revalidated.

I checked the code and what is actually getting set for all pages 
dynamically generated for logged-in users is:

Cache-Control: no-cache, must-revalidate, max-age=0

I apologize for being sloppy and not verifying this before sending my 
previous reply.

>> - However, when a login cookie is set, dynamic pages that are currently cached should not be served to the client with the login cookie, while they should still be served to all other clients.
> All of the above is handled by HTTP already, just follow the protocol.
>
> Make sure you separate your cacheable content from your uncacheable content. Ensure that you use HTTP conditional requests so that expensive calls can be made cheap. Properly declare the request headers you vary on using the Vary header, but keep in mind that headers with many variations will DoS a cache. Cache long-lived content and change the URL if the content is updated. Use max-age (and s-maxage) on short lived content to make the generation of it cheap.

The only thing I see above that will actually help is having separate 
URL paths for cachable and non-cachable content, but I'd have to hack 
that in using mod_rewrite (since I'm limited to the scope of changes I 
can make to the code of 3rd party web applications).  I'd prefer to 
avoid having logged in and non-logged in users seeing different URLs in 
their browser location bars.

Thanks for all of your replies!

-- 
   Mark Montague
   mark@catseye.org


Re: [RFC] enhancement: mod_cache bypass

Posted by Graham Leggett <mi...@sharp.fm>.
On 23 Aug 2014, at 3:40 PM, Mark Montague <ma...@catseye.org> wrote:

>> Does this not duplicate the functionality of the If directives?
> 
> No, not in this case:
> 
> <If "-z %{req:Cookie}">
>        CacheEnable disk /
> </If>
> 
> [root@sky ~]# httpd -t
> AH00526: Syntax error on line 148 of /etc/httpd/conf/dev.catseye.org.conf:
> CacheEnable cannot occur within <If> section
> [root@sky ~]#
> 
> Also, any solution has to work within both the quick handler phase and the normal handler phase of mod_cache.

The solution here is to lift the restriction above. Having a generic mechanism to handle conditional behaviour, and then having a special case to handle the same behaviour in a different way is wrong way to go.

> The proposed enhancement is about the server deciding when to serve items from the cache.  Although the client can specify a Cache-Control request header in order to bypass the server's cache, there is no good way for a web application to signal to a client when it should do this (for example., when a login cookie is set). The behavior of other caches is controlled using the Cache-Control response header.

There is - use “Cache-Control: private”. This will tell all public caches, including mod_cache and ISP caches, not to cache content with cookies attached, while at the same time telling browser caches that they should.

> Here is a more detailed example scenario, in case it helps.  There are also many other scenarios in which conditionally bypassing mod_cache is useful.
> 
> - Reverse proxy setup using mod_proxy_fcgi
> - Static resources served through httpd front-end with response header "Cache-Control: max-age=14400" so that they are cached by mod_cache, ISP caches, and browser caches.
> - Back-end pages are dynamic (PHP), but very expensive to generate (1-2 seconds).
> - Back-end sets response header "Cache-Control: max-age=0, s-maxage=14400" so that mod_cache caches the response, but ISP caches and browser caches do not.  (mod_cache removes s-maxage and does not pass it upstream).

mod_cache shouldn’t remove any Cache-Control headers.

> - When back-end content changes (e.g., an author makes an update), the back-end invokes "htcacheclean /path/to/resource" to invalidate the cached page so that it is regenerated the next time a client requests it.

Set your max-age correctly and this becomes unnecessary. If you have long lived resources that you want caching for a very long time, and you want to change that resource, place the version number of the resource in the URL and refer to the new URL after the change.

> - Clients have multiple cookies set.  Tracking cookies and cookies used by JavaScript should not cause a mod_cache miss.
> - Dynamic pages that are generated when a login cookie is set should not be cached.  This is accomplished by the back-end setting the response header "Cache-Control: max-age=0”.

This is incorrect, max-age=0 means that a cache is welcome to cache the content, but the content must be declared stale immediately and revalidated.

> - However, when a login cookie is set, dynamic pages that are currently cached should not be served to the client with the login cookie, while they should still be served to all other clients.

All of the above is handled by HTTP already, just follow the protocol.

Make sure you separate your cacheable content from your uncacheable content. Ensure that you use HTTP conditional requests so that expensive calls can be made cheap. Properly declare the request headers you vary on using the Vary header, but keep in mind that headers with many variations will DoS a cache. Cache long-lived content and change the URL if the content is updated. Use max-age (and s-maxage) on short lived content to make the generation of it cheap.

Regards,
Graham
—


Re: [RFC] enhancement: mod_cache bypass

Posted by Tim Bannister <is...@jellybaby.net>.
On 23 August 2014 14:40:36 GMT+01:00, Mark Montague <ma...@catseye.org> wrote:
>On 2014-08-23 5:19, Graham Leggett wrote:
>> On 23 Aug 2014, at 03:50, Mark Montague <mark@catseye.org 
>> <ma...@catseye.org>> wrote:
>>
>>> I've attached a proof-of-concept patch against httpd 2.4.10 that 
>>> allows mod_cache to be bypassed under conditions specified in the 
>>> conf files.
>>
>> Does this not duplicate the functionality of the If directives?
>
>No, not in this case:
>
><If "-z %{req:Cookie}">
>         CacheEnable disk /
></If>
>
>[root@sky ~]# httpd -t
>AH00526: Syntax error on line 148 of
>/etc/httpd/conf/dev.catseye.org.conf:
>CacheEnable cannot occur within <If> section
>[root@sky ~]#
>
>Also, any solution has to work within both the quick handler phase and 
>the normal handler phase of mod_cache.
>
>
>>> # Only serve cached data if no (login or other) cookies are present 
>>> in the request:
>>> CacheEnable disk / "expr=-z %{req:Cookie}"
>>
>> As an aside, trying to single out and control just one cache using 
>> directives like this is ineffective, as other caches like ISP caches 
>> and browser caches will not be included in the configuration.
>>
>> Rather control the cache using the Cache-Control headers in the
>formal 
>> HTTP specs.
>
>The proposed enhancement is about the server deciding when to serve 
>items from the cache.  Although the client can specify a Cache-Control 
>request header in order to bypass the server's cache, there is no good 
>way for a web application to signal to a client when it should do this 
>(for example., when a login cookie is set). The behavior of other
>caches 
>is controlled using the Cache-Control response header.
>
>This functionality is provided by Varnish Cache: 
>https://www.varnish-cache.org/docs/4.0/users-guide/increasing-your-hitrate.html#cookies
>
>Squid does not currently provide this functionality, but it seems like 
>there is consensus that it should: 
>http://bugs.squid-cache.org/show_bug.cgi?id=2258
>
>Here is a more detailed example scenario, in case it helps.  There are 
>also many other scenarios in which conditionally bypassing mod_cache is
>
>useful.
>
>- Reverse proxy setup using mod_proxy_fcgi
>- Static resources served through httpd front-end with response header 
>"Cache-Control: max-age=14400" so that they are cached by mod_cache,
>ISP 
>caches, and browser caches.
>- Back-end pages are dynamic (PHP), but very expensive to generate (1-2
>
>seconds).
>- Back-end sets response header "Cache-Control: max-age=0, 
>s-maxage=14400" so that mod_cache caches the response, but ISP caches 
>and browser caches do not.  (mod_cache removes s-maxage and does not 
>pass it upstream).
>- When back-end content changes (e.g., an author makes an update), the 
>back-end invokes "htcacheclean /path/to/resource" to invalidate the 
>cached page so that it is regenerated the next time a client requests
>it.
>- Clients have multiple cookies set.  Tracking cookies and cookies used
>
>by JavaScript should not cause a mod_cache miss.
>- Dynamic pages that are generated when a login cookie is set should
>not 
>be cached.  This is accomplished by the back-end setting the response 
>header "Cache-Control: max-age=0".
>- However, when a login cookie is set, dynamic pages that are currently
>
>cached should not be served to the client with the login cookie, while 
>they should still be served to all other clients.

A web application can and should use
Cache-Control: private
or
Vary:
headers on its responses, to avoid having them be incorrectly served from a shared cache.

I can see a case for webapps having better control over invalidation but I wouldn't do it like this.

If there's still demand, why not arrange for CacheEnable to be valid within <If>?

Tim
-- 
Tim Bannister – isoma@jellybaby.net

Re: [RFC] enhancement: mod_cache bypass

Posted by Mark Montague <ma...@catseye.org>.
On 2014-08-23 5:19, Graham Leggett wrote:
> On 23 Aug 2014, at 03:50, Mark Montague <mark@catseye.org 
> <ma...@catseye.org>> wrote:
>
>> I've attached a proof-of-concept patch against httpd 2.4.10 that 
>> allows mod_cache to be bypassed under conditions specified in the 
>> conf files.
>
> Does this not duplicate the functionality of the If directives?

No, not in this case:

<If "-z %{req:Cookie}">
         CacheEnable disk /
</If>

[root@sky ~]# httpd -t
AH00526: Syntax error on line 148 of /etc/httpd/conf/dev.catseye.org.conf:
CacheEnable cannot occur within <If> section
[root@sky ~]#

Also, any solution has to work within both the quick handler phase and 
the normal handler phase of mod_cache.


>> # Only serve cached data if no (login or other) cookies are present 
>> in the request:
>> CacheEnable disk / "expr=-z %{req:Cookie}"
>
> As an aside, trying to single out and control just one cache using 
> directives like this is ineffective, as other caches like ISP caches 
> and browser caches will not be included in the configuration.
>
> Rather control the cache using the Cache-Control headers in the formal 
> HTTP specs.

The proposed enhancement is about the server deciding when to serve 
items from the cache.  Although the client can specify a Cache-Control 
request header in order to bypass the server's cache, there is no good 
way for a web application to signal to a client when it should do this 
(for example., when a login cookie is set). The behavior of other caches 
is controlled using the Cache-Control response header.

This functionality is provided by Varnish Cache: 
https://www.varnish-cache.org/docs/4.0/users-guide/increasing-your-hitrate.html#cookies

Squid does not currently provide this functionality, but it seems like 
there is consensus that it should: 
http://bugs.squid-cache.org/show_bug.cgi?id=2258

Here is a more detailed example scenario, in case it helps.  There are 
also many other scenarios in which conditionally bypassing mod_cache is 
useful.

- Reverse proxy setup using mod_proxy_fcgi
- Static resources served through httpd front-end with response header 
"Cache-Control: max-age=14400" so that they are cached by mod_cache, ISP 
caches, and browser caches.
- Back-end pages are dynamic (PHP), but very expensive to generate (1-2 
seconds).
- Back-end sets response header "Cache-Control: max-age=0, 
s-maxage=14400" so that mod_cache caches the response, but ISP caches 
and browser caches do not.  (mod_cache removes s-maxage and does not 
pass it upstream).
- When back-end content changes (e.g., an author makes an update), the 
back-end invokes "htcacheclean /path/to/resource" to invalidate the 
cached page so that it is regenerated the next time a client requests it.
- Clients have multiple cookies set.  Tracking cookies and cookies used 
by JavaScript should not cause a mod_cache miss.
- Dynamic pages that are generated when a login cookie is set should not 
be cached.  This is accomplished by the back-end setting the response 
header "Cache-Control: max-age=0".
- However, when a login cookie is set, dynamic pages that are currently 
cached should not be served to the client with the login cookie, while 
they should still be served to all other clients.

-- 
   Mark Montague
   mark@catseye.org


Re: [RFC] enhancement: mod_cache bypass

Posted by Graham Leggett <mi...@sharp.fm>.
On 23 Aug 2014, at 03:50, Mark Montague <ma...@catseye.org> wrote:

> I've attached a proof-of-concept patch against httpd 2.4.10 that allows mod_cache to be bypassed under conditions specified in the conf files.  It adds an optional fourth argument to the CacheEnable directive:
> 
> CacheEnable cache_type [url-string] [expr=expression]
> 
> If the expression is present, data will only be served from the cache for requests for which the expression evaluates to true.  This permits things such as:

Does this not duplicate the functionality of the If directives?

http://httpd.apache.org/docs/current/mod/core.html#if

> # Only serve cached data if no (login or other) cookies are present in the request:
> CacheEnable disk / "expr=-z %{req:Cookie}"

As an aside, trying to single out and control just one cache using directives like this is ineffective, as other caches like ISP caches and browser caches will not be included in the configuration.

Rather control the cache using the Cache-Control headers in the formal HTTP specs.

Regards,
Graham
--