Missing Vary: Cookie or buggy Browser
A web application may instruct a browser to store a page in its cache for N seconds by adding a Cache-Control: max-age=N header. As a result the browser will retrieve a page from the cache on subsequent requests. This may lead to problems upon user login/logout. The following failure mode is typical for this problem: A user visits the front page of a site (unauthenticated), then navigates to the user-login page, logs into the system, comes back to the front page and appears to be logged out again.
In order to mitigate this problem, Drupal adds the Vary: Cookie header to each cacheable response. This will instruct a browsers to only fetch a page from its cache when the value of the Cookie on the new request header is equal to the one on the original request. Because Drupal regenerates the session cookie upon login, this request header is never the same before and after login and therefore a browser never should present a page cached before login to an authenticated user.
However at least Safari 5 (iPad 1, last Windows version, some older iPhones) has a bug triggering the failure mode even when Vary: Cookie is set correctly by the web application. Therefore when serving to Safari it is necessary to avoid that a page gets cached by the browser by forcing the Cache-Control header to no-cache.
The VCL shipping with Authcache Varnish Cache Backend contains a snipped capable of disabling browser caching for Safari (see commit href="http://drupalcode.org/project/authcache.git/commit/d2af02a">d2af02a). However I feel that we also should implement something for the Authcache Bultin Cache Backend.
Steps to reproduce
In order to detect this failure mode, execute the following steps:
- Clear the browser cache and cookies
- Navigate to a node (e.g. node/1)
- Navigate to user/login and authenticate
- Navigate back to the node
Execute those steps once with the browser cache enabled and once with the browser cache disabled (e.g. by deactivating it using the developer tools of your browser).
Deficient cache revalidation because of bogus Etag
In order to save bandwidth, a browser may choose to just "ask" a web application whether it can reuse a cached copy of a resource. The mechanism relies on the following HTTP headers:
Set by the web application on the initial response:
Cache-Control: max-age=NDate: <current-date>, e.g. Sat, 30 Nov 2013 14:02:40 GMTEtag: "12345-1"Last-Modified: <cache-save-date>, e.g. Sat, 30 Nov 2013 13:11:48 GMT
Set by the browser on a subsequent request:
If-Modified-Since: Sat, 30 Nov 2013 13:11:48 GMTIf-None-Match: "12345-1"
The web application rebuilds the page (or retrieves it from the internal cache) and determines the appropriate response using the following rules:
if request.If-None-Match == response.Etag AND request.If-Modified-Since == response.Last-Modified:
response.status = "304 Not Modified"
unset(response.body)
else:
response.status = "200 OK"
return(response)
The important point here is that the Etag value must be unique for each variant of a resource. By default Drupal constructs the Etag from the unix timestamp when a cached entry was created and appends "-1" when delivering gzipped content and "-0" when sending out uncompressed content.
Authcache therefore should make sure that the Etag header added to a response always includes the authcache key, such that when browsers request a revalidation, the cache backend or the reverse proxy has a chance to validate against the correct variant.
| Comment | File | Size | Author |
|---|---|---|---|
| #9 | 2147147-fix-etag-and-safari.patch | 10.13 KB | znerol |
| #8 | 2147147-fix-etag.patch | 8.74 KB | znerol |
Comments
Comment #1
znerol commentedComment #2
znerol commentedComment #3
znerol commentedComment #4
znerol commentedComment #5
znerol commentedComment #6
znerol commentedComment #7
znerol commentedComment #8
znerol commentedOk, first try at solving the Etag-issue. Manual testing shows good results. Lets see whether some tests blow up.
Comment #9
znerol commentedAlso add workaround for Safari to Authcache Bultin Cache Backend.
Comment #10
znerol commentedFixed and pushed. Commits 1ed9e8a through 112cd51
Comment #12
damienmckennaFYI Safari 9.0.3 on OSX 10.11.3 passes the cache test page (https://iafastro.directory/iac/testcase/cache-vary/) suggesting this should not be an issue anymore.