Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
Problem/Motivation
Discovered by @dawehner at #2752325-11: Automatically provide HEAD support when a REST resource supports GET.
\Drupal\Core\PageCache\DefaultRequestPolicy
uses \Drupal\Core\PageCache\RequestPolicy\CommandLineOrUnsafeMethod
which uses \Symfony\Component\HttpFoundation\Request::isMethodSafe()
, which considers both HTTP HEAD and GET safe. This is correct.
However, the Page Cache only caches based on URL and format.
Hence a cached HEAD response will be served to GET requests and vice versa.
Proposed resolution
Fix this.
Remaining tasks
Fix.- Tests
User interface changes
None.
API changes
None.
Data model changes
None.
Comment | File | Size | Author |
---|---|---|---|
#14 | 2753741-14.patch | 2.6 KB | Wim Leers |
Comments
Comment #2
Wim LeersThis still needs tests.
Comment #3
dawehner+1 for this fix
Comment #4
Wim LeersGreat!
NW for tests.
Comment #6
dawehnerUrgs, we hardcode the cache ID in there :(
\Drupal\system\Tests\Cache\PageCacheTagsTestBase::verifyPageCache
Comment #7
Wim LeersThat's easy to fix. Just add
'GET'
to$cid_parts
.Comment #8
znerol CreditAttribution: znerol commentedBack in D7 the page cache expected that a full response is always generated. The response body was removed upon delivery. This is a bit like gzip encoding works: Always store the response encoded but if a client does not understand that, then unzip upon delivery.
If I'm not completely mistaken, this is also how the Symfony HttpCache works regarding
HEAD
requests. So I'm not completely convinced that this is a problem.Comment #9
znerol CreditAttribution: znerol commentedIn fact
HttpFoundation\Response::prepare()
completely takes care of that.Comment #10
znerol CreditAttribution: znerol commentedI checked what Varnish does in this case. Upon a cache-miss, Varnish fetches from the backend using
GET
, even if the client did aHEAD
request.I think that application code (such as REST resources) should not care at all about whether a request was GET or HEAD, but instead always deliver the whole response. Symfony will take care of removing the body upon
prepare()
if necessary. Also if the site is behind a reverse proxy, we can assume thatHEAD
requests will not reach to the backend at all. Therefore there is no point in differentiate betweenGET
andHEAD
on controller level.I'm tempted to close this as won't fix. What do you think @Wim?
Comment #11
Wim LeersI think it'd then be valuable to convert this issue to a test-only issue to encode what you wrote in a test, to ensure that expectation is never broken.
Comment #12
dawehnerDoes it? I tried it on the command line and saw an actual response body, do we miss something by bypassing HTTP kernel?
+1
Comment #14
Wim LeersIt does.
Here's the requested test coverage.
Comment #16
Wim LeersDrupalCI fail.
Retesting.
Comment #17
dawehnerI'm still confused about what I've seen, but well, we have a test which proves the opposite.
Comment #18
alexpottIt works for me... not sure what @dawehner is seeing...
curl --head -i http://drupal8alt.dev
curl -i http://drupal8alt.dev
Comment #19
alexpottCommitted and pushed cae82ac to 8.3.x and 781e927 to 8.2.x. Thanks!