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
- Many hosting environments (and proxies and reverse proxies and CDNs) impose limits on the header size, and for a sufficiently complex Drupal 8 page, the number of cache tags on that page (i.e. the number of things that page depends on) can cause the cache tags header to go beyond 4 KB, causing WSODs.
- For sites without a reverse proxy or CDN, Drupal sending the
X-Drupal-*
headers by default takes up bandwidth so slows down communication between client and server, even when the headers don't exceed the fixed limits. - For sites using a commercial CDN service, Drupal sending the
X-Drupal-*
headers by default is not a benefit, because those services expect different, non-Drupal, headers (Fastly:Surrogate-Keys
, CloudFlare:Cache-Tag
). - For sites using a configurable/scriptable reverse proxy (e.g., Varnish) that could be configured to use
X-Drupal-*
headers, you would still need a Drupal integration module for sending the invalidation command, so there is little benefit to Drupal core sending the headers vs. letting the integration module do it.
Proposed resolution
- Change core to only add the
X-Drupal-Cache-Tags
andX-Drupal-Cache-Contexts
headers when thehttp.response.debug_cacheability_headers
container parameter is set. - Set this parameter in
WebTestBase
, to preserve the way in which test assertions for cacheability information work. - Change
PageCache
to only cacheCacheableResponseInterface
responses rather than extracting cache tags from theX-Drupal-Cache-Tags
header.
Remaining tasks
None.
User interface changes
None.
API changes
Item #3 from the proposed resolution means that contrib modules that were returning non-CacheableResponseInterface responses (see #126) and expecting them to be cached in Drupal's page cache anyway will no longer get that benefit. An "Http headers" section was added to https://www.drupal.org/node/2562903 to clarify that such expectations are not part of the public API.
Data model changes
None.
Comment | File | Size | Author |
---|---|---|---|
#160 | interdiff.txt | 4.01 KB | effulgentsia |
#160 | 2527126-160.patch | 18.4 KB | effulgentsia |
#152 | 2527126-152.patch | 16.57 KB | dawehner |
#152 | interdiff.txt | 940 bytes | dawehner |
#152 | 2527126-151.patch | 15.79 KB | dawehner |
Comments
Comment #1
dawehnerWhile I not necessarily agree with the DDOS argument, there are so many ways anyway by default, I totally agree with the performance point.
#2241377: [meta] Profile/rationalise cache tags actually has currently that problem
Comment #2
hass CreditAttribution: hass commentedComment #3
hass CreditAttribution: hass commentedComment #4
hass CreditAttribution: hass commentedComment #5
hass CreditAttribution: hass commentedComment #6
dawehnerThis for sure will fail a LOT, but it this basically what we want?
Comment #7
Wim LeersI fear that people only set that setting in case they want the logging to set the right IP. So I think it needs further scrutiny.
But yes, it sure seems that simple :)
Comment #8
Wim LeersAlso, does this mean you propose having
WebTestBase
set thereverse_proxy
setting to TRUE? Or do you want to introduce a separate flag (config perhaps?) to expose it anyway?Comment #9
Wim LeersWell, we want these headers when debugging anyway, so we want a flag either in Settings or in Config to turn it on. Then
settings.local.php
can turn it on again.Indeed! Especially concerning is that this header is in the first 14 KB of the TCP packet, so that means less space for more valuable information.
No. By that argument, literally everything in Drupal 7 is DDoSable, with the sole exception of image styles.
Comment #10
dawehnerTalked with wim about the name of that debugging flag.
Comment #12
olli CreditAttribution: olli commentedManually tested the patch and it seems to work!
Why not only a simple flag to enable the headers without checking reverse_proxy setting?
Comment #13
Fabianx CreditAttribution: Fabianx for Acquia commented#12: As soon as you use a reverse proxy in 99% of the cases you want the cache tags and cache contexts.
RTBC, we have enough test coverage for that.
Comment #14
jibranThis needs change record and also tests for this change.
Comment #16
Wim LeersSome rewording for consistency/clarity and nitpicking.
I also agree with Fabianx that we don't need tests, because WebTestBase sets this new setting, and we have dozens upon dozens of tests doing cacheability header assertions, so this'd fail immediately otherwise.
As for a CR, I've updated https://www.drupal.org/node/2259531.
Comment #17
Wim LeersRewrote the IS.
Comment #18
Fabianx CreditAttribution: Fabianx as a volunteer commentedBack to RTBC.
Comment #19
Wim LeersActually, shouldn't we do this using a container parameter instead of a setting?
Comment #20
dawehnerIn case we want that we should move all of the reverse proxy settings to the container.
Comment #21
Wim LeersPerhaps I'm misremembering, but wasn't general rule we don't add NEW things to
settings.php
? Don't we favor container parameters over that now?Comment #22
dawehnerComment #23
Wim LeersComment #24
alexpottThis issue addresses a major bug and is allowed per https://www.drupal.org/core/beta-changes. Committed 7115108 and pushed to 8.0.x. Thanks!
Comment #26
hass CreditAttribution: hass commentedDo we have recommended configurations for frontend caches somewhere? I think we need to document this so varnish admins do not miss to remove the headers in their cache servers, too.
Comment #27
dawehnerThis does not belong into this issue. Please create a new one.
Comment #28
Wim LeersUpdated https://www.drupal.org/developing/api/8/render/arrays/cacheability (diff: https://www.drupal.org/node/2456295/revisions/view/8437059/8721102).
Comment #29
Wim LeersThis broke the Page Cache. Page Cache cache items now have no cache tags, and thus are never invalidated.
Page Cache now only works if you are either behind a reverse proxy or have that new flag enabled. The reason tests pass is because we made web test enable the new "send cacheability headers" setting.
Comment #30
Fabianx CreditAttribution: Fabianx as a volunteer commentedUghhhh, lets revert for now and make page cache use the cacheable metadata on the response instead.
Comment #31
dawehnerIn other words it is a horrible idea to have a different test environment than actual code running in production.
I think we should enable the cache tags debug output in places where we need it.
Comment #32
Wim Leers+1000
Comment #33
catchReverted.
Comment #35
dawehnerFor now this removes the autoenabling out of WebTestBase, but this should also get some dedicated test.
Comment #37
janusman CreditAttribution: janusman at Acquia commentedPatch didn't apply. New patch for testing.
Comment #39
anavarreAttempting a straight reroll from #35.
Comment #40
dawehner@anavarre
Ha, good try to simply don't add the new code.
While talking with @Fabianx we realized that we can also strip after the page cache.
Comment #43
Wim LeersIndeed. That was the plan all along.
But it needs to be configurable. Very often, it's a good idea to still use Page Cache (and Dynamic Page Cache) even when you're using a CDN, because it still reduces the load on the origin: many edges may simultaneously request the same page from the origin, and then (Dynamic) Page Cache still helps.
Comment #44
Fabianx CreditAttribution: Fabianx as a volunteer commented#43: This has nothing to do with dynamic page cache vs. non-dynamic page cache.
It just means we remove the headers in the reverse proxy middleware instead of in the FinishResponseSubscriber.
Hence PageCache and all other things running in a Middleware or ResponseEvent subscriber are fine.
If you use a CDN or varnish, set reverse_proxy = TRUE or set the send_cacheability_headers = TRUE - easy enough.
I think this only needs the part of ensuring our tests have the cacheability headers enabled from #16 as there is nothing using headers after ReverseProxyMiddleware.
Comment #45
Wim LeersI only read #40, so I thought it was the Page Cache's middleware that was going to strip it.
Fixing a small oversight that caused those many failures.
Comment #46
dawehnerWow, this are actually quite a few failures for not returning a response at all.
Comment #51
dawehnerComment #54
Fabianx CreditAttribution: Fabianx as a volunteer commentedWhy not again unconditionally on WebTestBase?
Yes, we screwed up once, but only because we removed those headers too early.
Overall that seems like a way less invasive change and also not breaking contrib tests, which we can't do at this point in time anymore.
Comment #55
dawehnerWell, ulike the last time we should ensure that it still works without them
Comment #58
Fabianx CreditAttribution: Fabianx as a volunteer commentedShould be 'value' => $value ;).
Comment #59
dawehnerAh perfect, thank you for catching that!
First I had two methods but then considered that as a little bit too silly.
Comment #60
Fabianx CreditAttribution: Fabianx as a volunteer commentedFail: [Other] Line 0 of sites/default/files/simpletest/phpunit-1224.xml:
PHPunit Test failed to complete
Unit tests for ReverseProxyMiddlewareTest need to be updated, too - to account for the new calls to settings.
Comment #63
dawehnerSure
Comment #64
Fabianx CreditAttribution: Fabianx as a volunteer commentedBack to RTBC - finally.
Hope this can go in during RC.
Comment #65
Wim LeersSeems like a verb is missing at the beginning of this.
Needs language love.
s/
setCacheHeaders()
/setCacheabilityHeaders()
/Pointless comment.
Comment #66
dawehnerYes, there is certainly a word .
Agreed.
Comment #68
webchickShaddup, PIFT.
Comment #69
webchickAhem.
Comment #70
Wim LeersTwo nits that can be fixed on commit:
s/cacheblity/cacheability/
To avoid the "paper bag" release that we had several betas ago, when #16 got committed and turned out to break the Page Cache (see #29). The root cause for this going unnoticed back then was:
@dawehner described it very well why that was stupid in #31:
This time, however, we do it in the reverse proxy middleware that runs after the page cache (it wraps the Page Cache middleware). So we cannot possibly break the Page Cache anymore.
To make that absolutely certain, however, we now test that even when you explicitly set
send_cacheability_headers = FALSE
, it still continues to work:And finally, I did manual testing, verifying that both the Page Cache and the Dynamic Page Cache, and it all indeed still works out of the box, unlike last time.
Comment #71
effulgentsia CreditAttribution: effulgentsia at Acquia commentedWe discussed this on a committer call today and decided that it's better for this to go in during RC than just before, so tagging. While it's unfortunate that some RC testers will thus get WSOD on some hosting environments (according to the issue summary), we expect this to be a very small minority.
Comment #72
dawehner---
Well, I think you might underestimate which hosting environments this are.
Comment #73
catchIf they are big hosting environments that care about Drupal, then they (or we) will get a lot of bug reports until they raise the header limit.
Also if they offer reverse proxies, wouldn't you want to set $settings['reverse_proxy'] = TRUE to take advantage of that? So this ends up being a workaround where you disable all reverse proxy behaviour despite there actually being one on your hosting.
I think we should keep going with issues like #2542868: Allow a header value size limit to be specified regardless of this one.
Comment #74
Wim LeersYep, agreed with the sentiment of #73. This is just a band-aid for the typical use case. It helps improve front-end performance in the process.
But we still need #2542868: Allow a header value size limit to be specified together with this, because if your setup/architecture needs the cacheability headers, then you still need to ensure it doesn't exceed the supported header length.
Comment #75
Wim LeersDuring the D8 EU criticals call of this morning, we (Alex Pott, dawehner, Wim) discussed this issue, and its sister issue #2542868: Allow a header value size limit to be specified.
X-Drupal-Cache-Tags
header.)twig.config.debug
), which is a container parameter. i.e. add a new container parameter tocore.services.yml
, document it indefault.services.yml
.NW for that simpler, clearer, more sensible direction.
Comment #76
Wim LeersNote that with #75's approach, the necessity of #2542868: Allow a header value size limit to be specified becomes questionable.
Comment #77
dawehnerone thing we could do:
Sadly this is not the minimal change.
Comment #78
Wim LeersOne clarification: PageCache doesn't need the
X-Drupal-Cache-Tags
header anymore. It currently parses that header to get the cache tags of the response, but, ever since we gotCacheableResponse
, that's not necessary anymore. That's why it's totally fine to not even have theX-Drupal-Cache-(Tags|Contexts)
headers even when (Dynamic) Page Cache is enabled: because the response already carries the cacheability metadata in a more structured form.Comment #79
dawehnerYeah I agree this would not be the minimal change at all.
Comment #81
dawehnerSmall details matter.
Comment #82
Wim LeersI like the name :)
http
makes sense.cacheability_headers
makes sense.But perhaps
send
should becomedebug
? That'd matchtwig.config
'sdebug
parameter better. But that also doesn't sound quite as good.Hrm, not sure… Ideas?
Well, these changes don't really make sense anymore after #75's conclusion, IMO.
We can make it simpler still: don't generate these headers at all… unless the container parameter tells us to.
Then there is no reason for the reverse proxy middleware to strip them.
Nit: Rather than appending this to the end of the file, I think it makes more sense to put after Twig & Renderer's settings.
Working on addressing all that.
Comment #83
Fabianx CreditAttribution: Fabianx as a volunteer commented#78 is unfortunately wrong.
We specifically also support other responses right now - that send cache tags and max-age values themselves.
Overall I am okay to remove unless that container parameter or setting is set though - and making that opt-in.
Comment #84
Wim Leers#83: that's not intentional, that's pure coincidence. There's no test coverage verifying that. It's not supported.
Comment #85
dawehnerWell, just to be clear, I think people should be able to add the tags, if needed but we should simply not add them in the beginning.
Comment #86
znerol CreditAttribution: znerol commentedThe comment does not seem to match the implemented logic. How about:
Comment #87
Wim LeersImplemented #82.
This will cause any test relying on
X-Drupal-Cache-(Tags|Contexts)
to fail. I'll update the other tests in the next reroll. That keeps this patch easier to review, and it's this patch that contains the crucial changes.Comment #88
Wim LeersAnd now updated every test that uses
assert(No)Cache(Tag|Context)()
.Comment #89
Wim LeersI'm still not fully convinced by this name.
I wonder if it'd be better to stress the "debug" aspect of this more?
Then perhaps
http.cacheable_response.debug_headers: false
or something like that would make more sense?I think "set cacheability headers" is also relatively confusing, I think "send cacheability headers" would be better? Of course, the answer to this point depends on the feedback to the first point.
Comment #92
Wim LeersComment #94
dawehnerShould we move the rebuilderContainer() code into the set... call? I think this would make the code a little bit less repetitive.
Well, I don't care that much. Afaik it should mirror the parameter as much as possible and be done, so
set(Camelize($parameter_name))
Its a total odd thing that the cacheability information are part of the FinalResponseSubscriber, which makes it a little bit confusing to find a proper name here. I would argue that the cachability information should be moved to the list bit, as its about displaying cachability debug headers, not any kind of headers, so what about
http.response.debug_cacheability_headers
?Comment #95
catchThe setting being called cacheability headers suggests it'd include Cache-control, so something more specific sounds good.
Comment #96
Wim Leers+1
Hah, that's exactly what I had written locally in a patch already, before I decided to first gather feedback :) +1
Exactly! :)
Assigning to myself to address those points.
Comment #97
Wim LeersDone.
Also rebased, #92 didn't have a single fail like DrupalCI misleadingly suggested: the patch simply did not apply anymore.
The rest will follow tomorrow.
Comment #99
Wim LeersThis will hopefully be the first green patch.
The nastiest remaining problem was:
the
rebuildContainer()
call caused$this->moduleHandler->isLoaded()
to returnFALSE
. A call toresetAll()
fixes that (that's also howWebTestBase::setUp()
works).Comment #100
tstoecklerThis seems to be no longer true?
Comment #101
Wim LeersTo be able to link to proper documentation, I wrote https://www.drupal.org/developing/api/8/response and particularly created the https://www.drupal.org/developing/api/8/response/cacheable-response-inte... child page. I also updated https://www.drupal.org/developing/api/8/cache and https://www.drupal.org/developing/api/8/render/arrays/cacheability#headers accordingly. And I added https://www.drupal.org/developing/api/8/cache/cacheable-response-interface.
With all that out the way, I was able to finally able to rename the container parameter to
http.response.debug_cacheability_headers
and rename the method onWebTestBase
accordingly.Comment #103
Wim LeersWell that was rather stupid: I renamed it everywhere, except where it's actually being used.
Comment #104
tstoecklerse -> see
Comment #105
Wim Leers#104: Thanks, fixed now! #100 was fixed in #101.
Comment #106
moshe weitzman CreditAttribution: moshe weitzman at Acquia commentedFixed up the IS, after chatting with Wim. Looks solid to me.
Comment #107
Wim LeersForgot to adjust the issue title for the new direction since #75.
Minor update to IS.
Comment #108
alexpottIs there any reason why we're not turning the headers on in all test environments where they matter - this would limit the impact on any contrib or custom tests that depend on this.
Comment #109
dawehnerWell, I was originally in favour of doing that, but it required quite a lot of enabling, see https://www.drupal.org/files/issues/2527126-51.patch as a start (wasn't everything),
so it would not be the minimal change, this is for sure.
Comment #110
alexpott@dawehner the latest patch is doing this everywhere:
Why don't we just enable this for KernelTestBases, WebTestBase and BrowserTestBase - for all tests.
Comment #111
Wim Leers#110: Because then the tests don't match reality. And that's what caused Page Cache to be broken in beta 15 in the first place. See #29 and #31:
Of course…
So… I think I'm +1 to @alexpott's proposal. This is probably the most reasonable approach.
Comment #112
Wim LeersIn fact, this is now something that is necessary for only the most exotic tests. Nothing in core uses it.
So let's remove that too.
Comment #113
dawehner/me sighs loud.
We need a test for the parameter ... to ensure that a) we don't send out the header, unless needed (we had that in earlier versions of the patch, I'm quite sure about that)
b) page caching is still working if the parameter is set to FALSE (we had a critical regression, let's ensure this doesn't happen anymore).
Comment #114
Wim LeersIMO this is ready again now. With a smaller patch than ever before. And now with zero disruption.
IS updated again. https://www.drupal.org/node/2259531 updated.
Comment #115
Wim LeersOkay, adding an explicit regression test for Page Cache. I don't think that's particularly helpful now, but totally agreed: better safe than sorry in this regard :)
Fully agreed on adding a test that verifies the debugging cacheability headers are sent or not when the container parameter is set to true or false respectively.
Comment #116
dawehnerGreat, thank you!
Comment #117
Fabianx CreditAttribution: Fabianx as a volunteer commentedRTBC - I will miss looking at D8 websites and see their contexts online ;) - but yes using CachableResponseInterface is much simpler overall.
Comment #118
znerol CreditAttribution: znerol commented+1, a lot cleaner than before.
Comment #119
borisson_I like this solution, it's very clear.
I wonder if we should remove the
X-Drupal-Dynamic-Cache
header as well? While that one isn't as big as the contexts/tags header I think this serves the same purpose: debugging / development.We can do that in a followup though.
Comment #120
alexpottI think we might need an explicit change record for https://www.drupal.org/node/2527126 in case anyone has a reverse proxy that is using the tags OR is implementing a module
Comment #121
Wim Leers#117:
+1 :)
#119:
I see your point, but I think we don't want that, because:
X-Drupal-Cache
header for Page Cache, so it's consistent with that.#120: CR created: https://www.drupal.org/node/2592471
Again improved the IS, the title, and the issue tags.
Comment #122
dawehner@Wim Leers
Why is that no longer a security improvement? I mean of course its kind of small one but I think its not the worst to not shout out internal IDs to the public everywhere.
Comment #123
Wim LeersThis never was about security. The OP added that tag, and it should've been removed a long time ago. When we added this header originally, we had it thoroughly vetted by the security team. The conclusion was there was no security risk at all. If there were, we would never have sent this header by default.
Comment #124
dawehnerAh well, the original issue summary had it, but it got deleted ...
Comment #125
Wim Leers#124: it's still there, but crossed through, because it's wrong/silly.
It's super easy to find uncached things to "DDoS" sites: just append a random query string to any Drupal URL that returns a HTML response. You can fill up the Page Cache with millions of duplicate frontpage entries with just a different query string. This is well-known, and applies to all Drupal versions.
That's much more damaging than knowing which entities would cause a certain response to change, because an attacker would not be able to modify those entities. If they can modify those entities, they don't need these cache tags to know that anyway.
Therefore, that argument is IMHO entirely moot.
Comment #126
BerdirFrom request.module's RedirectRequestSubscriber:
This change *will* break that.
I'm a bit confused now, do we now have a cacheable redirect response object or not? I think that wasn't committed yet?
I could implement my own I guess but maybe we can find a way to keep BC there (we are in RC, after all..) by falling back to the header if it doesn't implement the interface?
Comment #127
Wim LeersNo, it was not: #2573923: Introduce a CacheableRedirectResponse and use it where appropriate.
But
redirect.module
could have its ownConfiguredRedirectResponse extends SecuredRedirectResponse implements CacheableResponseInterface {}
implementation so that it fully controls the response.I think you maintain the only module that does this.
You're right that we're breaking the ability to create any kind of response that has this header and have it work with Page Cache. But doing that allows us to have a very simple explanation: Page Cache and Dynamic Page Cache support cacheability metadata if the response contains cacheability metadata, i.e. if it implements
CacheableResponseInterface
. That's very easy to understand.Do you think there are (many) other modules that would be affected by this?
Comment #128
BerdirWhy do I hear that all the time ;)
I probably am, as far as contrib is concerned.. who knows what custom code is doing out there already.
Yes, I fully agree that is much easier. But... RC ;) I think we need to be very, very careful in regards to breaking things unless there is no other way. We've had plenty of freezes, beta's and so on, and every single time, within days/weeks we more or less fell back into the old pattern of breaking things all over the place. That really has to stop now.
And this wouldn't be a complicated to do in a way that keeps BC. Just have an if/else with a @todo to remove it in 9.x.
Comment #129
catchSo it is true that it breaks the module.
I'm not sure the implementation detail of 1. the naming of the header 2. internal page cache consuming that header is something we should consider an API though.
That is more for #2550249: [meta] Document @internal APIs both explicitly in phpdoc and implicitly in d.o documentation - clearly we did not document the contents/consumption of custom headers there.
Comment #130
Wim LeersFair.
Done.
IS updated accordingly.
Comment #131
Fabianx CreditAttribution: Fabianx as a volunteer commentedRTBC - the BC layer makes sense to me.
Comment #134
effulgentsia CreditAttribution: effulgentsia at Acquia commentedI don't like this combination. Both the name of the parameter and the first hunk setting it differently for tests than the production default implies that the 'X-Drupal-Cache-Tags' header is for debugging. But the second hunk means that there's a non-debugging aspect to the header as well. That then puts us into some fuzzy territory with potential for bugs and WTFs.
I see a couple ways out of this:
Not changing issue status in case another committer disagrees with me and wants to commit as-is.
Comment #135
Wim LeersI completely agree.
More middlewares mean worse performance.
I vote for option 1, of course.
Comment #136
catchDiscussed in irc with berdir.
I personally don't think the custom header counts as an API - if we'd thought of it prior to rc, it'd be documented in https://www.drupal.org/node/2562903 (which I've just added a note to for any similar issues that come up in the future).
However, before this issue lands (or as part of it) we should ensure that for path redirect, redirect responses stop getting cached altogether.
i.e. if we'd never had that header, then path_redirect would not have been able to use it, so the only option would have been to not have any redirect caching. That fact that we cache redirects in core now without #2573923: Introduce a CacheableRedirectResponse and use it where appropriate is a bug. That might have been a contributed project blocker, but it wouldn't have been any kind of API change then. Sometimes nothing is better than something...
Approached that way, it's a bug fix to stop caching any redirects, and an API addition to start caching some of them. And path_redirect then has some dead code that relied on a core implementation detail (and via berdir also broken tests that were testing the behaviour). Just breaking tests doesn't necessarily mean we changed an API though, a string change can break a test too.
For that reason I think we should not add a bc layer here, because it's an 'API that should never have been', but we need to think very carefully about this and make sure the documentation reflects any decisions taken like this. And it'd be good to make sure people on this issue actually agree with those arguments - this is going to come up more places than here and we need to be as consistent as we can.
Comment #137
Wim Leers#136++
Per #136, re-uploading #115. Interdiff compared to #129 is then of course the inverse of #129's interdiff.
Comment #138
alexpottIt looks like we've considered every possible angle. We've tried to maintain a BC layer but as per @effulgentsia this ends up being more confusing and breaking redirect caching is sensible given that this was relying on a side effect rather than an API. Given the discussions here I think we should agree that #2573923: Introduce a CacheableRedirectResponse and use it where appropriate is an RC target.
Comment #139
alexpott@catch had the idea of postponing this on #2573923: Introduce a CacheableRedirectResponse and use it where appropriate. I agree. That way we have no regression.
Comment #140
Fabianx CreditAttribution: Fabianx as a volunteer commented#126 is actually not cached in page cache, because of #2476407: Use CacheableResponseInterface to determine which responses should be cached, which went in before RC and did affect both page cache and caching in reverse proxies.
So unless contrib also modified the Expires and Cache-Control headers - which is not the case for redirect module, then the point of BC is very moot.
If however this continues to be cached - despite #2476407: Use CacheableResponseInterface to determine which responses should be cached then that is a bug in PageCache - however that is unlikely.
Comment #141
Fabianx CreditAttribution: Fabianx as a volunteer commentedOpened #2593887: Follow-up: Do not cache private or no-store responses in PageCache, berdirs tests passing is a bug. They should be failing by now.
Comment #142
effulgentsia CreditAttribution: effulgentsia at Acquia commentedThis doesn't quite implement my suggestion in #134.1. My suggestion there is that the
->set()
should not be done for a $response that isn't CacheableResponseInterface.Following on #140, I don't think #2476407: Use CacheableResponseInterface to determine which responses should be cached is enough of an equivalent to that. Because that issue still allows a Response that isn't a CacheableResponseInterface to use Cache-Control headers and therefore be cacheable by something, which is correct. But say such a custom Response also sets some other headers that manage tag-like invalidation (e.g.,
Surrogate-Keys
, if this is a custom module that's catered to a particular deployment architecture). In such a case, since PageCache doesn't know about such headers, it should simply not be in the business of trying to cache such a Response.Or in other words, modules that want to work with caching generically rather than deployment-specifically should return CacheableResponseInterface responses. Then PageCache can cache it and various contrib modules can provide additional middlewares to transform CacheableResponseInterface information to the headers needed by a corresponding proxy/CDN. But, if a module chooses to not return CacheableResponseInterface, but instead manages its own response headers, then PageCache should skip over it and leave caching to whatever software that module catered the headers to.
Comment #143
Wim Leers#142 is eminently sensible. It'd make the entire response caching infrastructure in Drupal 8 super simple to understand:
(i.e. Dynamic Page Cache already has this restriction.)
This patch implements that, and adds the necessary test coverage. (And fixes the broken bits in
\Drupal\page_cache\Tests\PageCacheTest::testCacheableResponseResponses()
.)#140: I found why #2476407: Use CacheableResponseInterface to determine which responses should be cached's test coverage did not do what you expected it to do: because the assertions do
… but
$this->drupalGetHeaders()
returns lower-cased header names.Comment #146
BerdirNot sure about this change, I think that should be done/discussed separately?
So that we can test what this actually means for a standard install or when you add statistics.module to the mix (which now defines a max age of 3600 by default if you can view the node counter. Should have test coverage and so on...
I think that comment is still weird, considering that we don't actually try to do that?
Other than that, looks great.
Comment #147
tstoecklerWould be cool to add this to development.services.yml with a true value IMO. (That might also mitigate some of @FabianX's sadness ;-))
Can be a follow-up, though, and should in case that's in any way controversial.
Comment #148
Wim Leers#146.1: can you explain what the problem with that is?
Comment #149
Wim LeersI can't reproduce the fail in https://www.drupal.org/files/issues/2527126-143.patch locally… re-testing.
Comment #151
Wim Leers#146
Can you explain in what way this could be problematic? Happy to add test coverage. Happy to defer to a follow-up too, but I want to understand it better.
#147: I'm afraid that is highly controversial. More so because if this would be in there, then
twig.config.debug
etc should be too. I totally see your reasoning though: it currently requires too many steps/is too confusing to enable any of those "debug" container parameters. Could you file a separate issue for that?Comment #152
dawehnerGreat idea!
You are totally right IMHO. This bit does NOT solve the issue we have, its a separate issue which needs separate tests. Due to the usage of time() vs. REQUEST_TIME the testbot might exceed the time a bit. When I debugged it locally it most of the time one test failure occured, but on some circumstances the debugger slowed it down even more, so that sometimes even two test failed. Let's revert that bit, its not needed in this particular issue.
Comment #153
Berdir(crosspost with #150/151)
1. I'm not sure there is a problem, but it is IMHO an unrelated change, one that needs a dedicated issue and testing. It changes behavior that is not related to this issue but #2352009: [pp-3] Bubbling of elements' max-age to the page's headers and the page cache.
To see one example of how this changes things: install statistics.module, enable content view counter, give permission to anon users. Without this patch, page cache doesn't care and caches forever. With this patch, the expire is now set to 1h. That's a great improvement, actually, but it has nothing to with this issue IMHO. Lets do that in the issue mentioned above where we should also discuss how Cache-Control: max-age should behave exactly, right now, that doesn't respect this either.
Comment #154
dawehnerWell, here is one: #2594543: Put http.response.debug_cacheability_headers into default.services.yml ...
Comment #155
Fabianx CreditAttribution: Fabianx as a volunteer commented+1 to this direction and thanks for fixing the test. Given this direction I think I can close #2593887: Follow-up: Do not cache private or no-store responses in PageCache as a duplicate of this :).
Or maybe we should do the cache only cacheable responses in its own issue first?
Comment #156
Wim Leers#152 + #153: ahh, of course! Thanks for the explanation, both of you! :)
#154: thanks for filing that!
#155: No, that is still necessary. Or is at least a valuable thing to discuss, come to consensus, and formalize the expectations in the form of a test. Because even if a response implements
CacheableResponseInterface
, if it's marked asprivate
orno-store
, it could be argued that Page Cache should in fact not cache it.#152 looks great to me.
Comment #157
znerol CreditAttribution: znerol commentedIf this is really the case, then please give some justification at least in the issue summary, but probably also in the comment.
Update: I understand that this comes from #142, but still needs documentation.
Comment #158
Wim Leers#2573923: Introduce a CacheableRedirectResponse and use it where appropriate landed a few hours ago, so that blocker is out of the way.
@effulgentsia is going to reply to #157.
Then we can hopefully go to RTBC.
Comment #159
dawehnerAs long the final response object can be used to extract somehow those caching information I'm happy with it, because well, I had usecases to fetch these information already to be honest.
Comment #160
effulgentsia CreditAttribution: effulgentsia at Acquia commentedThis adds the docs requested in #157. I'm open to feedback on how to improve it or make it more concise.
Comment #161
effulgentsia CreditAttribution: effulgentsia at Acquia commentedWhen I wrote this, I thought, hm, maybe this check should be moved to a response policy instead of being baked into this class. But I'm unclear on whether the
page_cache_response_policy
service tag is to only control what is cached by page_cache module, or whether those policies should also control what gets cached by reverse proxies and CDNs (via the corresponding integration module). If the latter, then this should not move to a response policy, since we only want this affecting page_cache itself. Do we have/need an issue to discuss/document this aspect of response policies?Comment #162
effulgentsia CreditAttribution: effulgentsia at Acquia commentedUpdated summary to reflect the #160 patch. The last update was in #130, so was behind by several patches.
Comment #163
Wim LeersDoing so would not be an API change. It would be a behavior change that results in the same exact behavior by default, but would open the door to sites removing or changing this policy. Which means this would be a pure API addition. So we could do so at a later time, when the need arises.
It's better to start simpler & more strict and add more abilities later.
I think we need to discuss that, yes. Given that these are used by both the
PageCache
middleware and theFinishResponseSubscriber
to determine theCache-Control
headers that are sent to the world, thepage_cache_response_policy
policies actually do affect both Drupal's internal page cache and external proxies.I think it's worth taking this aspect into consideration in #2540684: Rename Drupal\Core\PageCache namespace, so I propose we continue that discussion there.
Comment #164
Fabianx CreditAttribution: Fabianx as a volunteer and at Tag1 Consulting commented#163: Also the FinishResponseSubscriber already has a check hard-coded for CacheableDependencyInterface and marks the response as uncacheable then (unless headers have been modified).
And unless we want to support the full HTTP spec like Symfony's HttpCache middleware, I don't think it would be wise to store anything besides CacheableResponseInterface responses, where we can say:
Just like smart_cache, just like render_cache. _Only_ CacheableDependencyInterface information is taken into account.
That makes it (as Wim has already said several times) very simple and easy to understand.
So +1 to hard-coding for now.
Comment #165
Fabianx CreditAttribution: Fabianx as a volunteer and at Tag1 Consulting commentedRTBC + 1 for #160 - not sure what is missing here, so not setting status value, but patch looks good to me.
Comment #166
dawehnerSo can anyone explain me actually why anyone of this is in scope of this issue? The issue is that we sent out a too big HEADER, and this is all. Other changes should live in its own issue, IMHO
Comment #167
znerol CreditAttribution: znerol commented#157 is addressed, I think #153 and previous concerns from @Berdir are addressed as well according to #2573923-76: Introduce a CacheableRedirectResponse and use it where appropriate and the change record looks right.
Comment #168
Wim LeersThat means every single concern here has been addressed and carefully weighed against all other concerns. We have minimal changes, extremely carefully determined decisions.
What's holding back RTBC?
EDIT: cross-posted with #166. Reply to that: Because we are no longer sending the
X-Drupal-Cache-Tags
header always, which means PageCache cannot rely on it, which means it must rely onCacheableResponseInterface
responses' metadata, which led to further discussions that are inherently intertwined with that change, which then after carefully achieved consensus has resulted in this. Ideally, yes, it would live in a separate issue, but it's so closely intertwined that it doesn't really make sense to split it up IMO.EDIT: cross-posted with #167 too. Thanks @znerol.
Comment #169
alexpottThanks everyone - good discussion of what to do here with respect to backwards compatibility - and sorry @Berdir for breaking yet another of your modules but at least this time we had the solution in first.
Committed ff622b5 and pushed to 8.0.x. Thanks!
Comment #175
geerlingguy CreditAttribution: geerlingguy as a volunteer and at Acquia commentedI have some follow-up comments to the changes in this issue / change record, posted over in #2241377-77: [meta] Profile/rationalise cache tags; basically, if I'm using Varnish (or some other reverse proxy) and would like to have a header to use for cache tags, is it recommended I somehow build my own, or should I enable
http.response.debug_cacheability_headers
in production (which the documentation now in services.yml recommends against) and use the built-inX-Drupal-Cache-Tags
header?It seems like I would want to use Drupal's built-in header, but since the setting has the word debug in it and a scary warning about "Not recommended in production environments"... I'm kind of at a loss.
Comment #176
Wim LeersYes, send your own. The CR already explains that very explicitly. See https://www.drupal.org/node/2592471.
I'll state the reason *again* then: because different reverse proxies have different expectations wrt the name of the header and/or the format of the value of the header.
Comment #177
Wim LeersFollow-up created + committed at #2827047: Add http.response.debug_cacheability_headers: true to development.services.yml.
Comment #178
jeetmail72How to disable X-Drupal-Cache-Tags from Header in Drupal 8
Steps:
1. Check which services.yml file is being loading from your settings.php or settings.local.php
$settings['container_yamls'][] = DRUPAL_ROOT . '/sites/services.yml';
2. Open services.yml file and search
http.response.debug_cacheability_headers: false
It should be false for the Prod environment.
Now check your website headers.