Possible cache_bypass_bug
John Ferlito
johnf at inodes.org
Mon Sep 26 01:10:07 UTC 2011
Howdy,
I'd like to confirm what I think is a bug before I lodge it in the bug
tracker.
I'm seeing pages that shouldn't be cached be cached.
I have a config that looks something like
upstream app_pool {
server app.inodes.org;
fair;
}
proxy_cache_path /srv/www/reverse_proxy/cache levels=1:2 keys_zone=application_cache:8m max_size=1000m inactive=600m;
proxy_temp_path /srv/www/reverse_proxy/cache/tmp;
server {
listen 80;
server_name inodes.org;
location / {
proxy_pass http://app_pool;
proxy_pass_header Set-Cookie;
proxy_cache application_cache;
proxy_cache_key "$scheme$host$request_uri$query_string";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_cache_valid 200 302 301 60m;
proxy_cache_valid 404 1m;
proxy_cache_bypass $http_pragma;
}
}
Now the behaviour I'm seeing is specifically for responses with a header like
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
That is they shouldn't be cached. They only get cached after a request with
Pragma: no-cache
set.
If I create a cache log I see something like this.
MISS "GET /mospace/team/ HTTP/1.1" (200) # First load
BYPASS "GET /mospace/team/ HTTP/1.1" (200) # Shift Reload
HIT "GET /mospace/team/ HTTP/1.1" (200) # Standard load
I've done some following of the code path and I think I understand
where the bug lies.
In ngx_http_upstream_cache.c we essentially have the following code (snipped
for clarity)
static void
ngx_http_upstream_send_response(ngx_http_request_t *r, ngx_http_upstream_t *u)
{
switch (ngx_http_test_predicates(r, u->conf->no_cache)) {
case NGX_DECLINED:
u->cacheable = 0;
break;
default: /* NGX_OK */
if (u->cache_status == NGX_HTTP_CACHE_BYPASS) {
r->cache->min_uses = u->conf->cache_min_uses;
r->cache->body_start = u->conf->buffer_size;
r->cache->file_cache = u->conf->cache->data;
if (ngx_http_file_cache_create(r) != NGX_OK) {
ngx_http_upstream_finalize_request(r, u, 0);
return;
}
u->cacheable = 1;
}
break;
}
}
So from my understanding if cache_bypass has been set we mark the page as being
able to be cached.
Now this function is called in ngx_http_upstream_process_header, which does the
following
if (ngx_http_upstream_process_headers(r, u) != NGX_OK) {
return;
}
if (!r->subrequest_in_memory) {
ngx_http_upstream_send_response(r, u);
return;
}
>From what I can tell ngx_http_upstream_process_headers will go through
the headers and when it sees the appropriate Cache-Control header it
will set cacheable to 0.
But later on in ngx_http_upstream_send_response we are setting it to
1. Hence we start caching private pages but only when a bypass header
has been set.
Perhaps we should set cacheable to 1 much earlier in the process when
BYPASS is set? That is in ngx_http_upstream_cache
switch (ngx_http_test_predicates(r, u->conf->cache_bypass)) {
// snip
case NGX_DECLINED:
u->cache_status = NGX_HTTP_CACHE_BYPASS;
u-> cachable = 1; // Add this here maybe?
return NGX_DECLINED;
}
This is the first time I've looked at the nginx source so perhaps the
above would have some other unintended effect. If not let me know and
I'll submit a patch.
Cheers,
John
--
John
Blog http://www.inodes.org
LCA2012 http://lcaunderthestars.org.au
More information about the nginx-devel
mailing list