See #2461087-19: Add 'no_cache' route option to mark a route's responses as uncacheable (was: Cron run response should not be cacheable).

Beta phase evaluation

Reference: https://www.drupal.org/core/beta-changes
Issue category Task because it is considered a to-do item.
Issue priority Major because it optimizes how page caching is performed, improves the DX around page caching.
Prioritized changes The main goal of this issue is performance for 8.0.0. By being able to enable page cache by default, it must be simple for modules to opt-out of caching. Pages using tokens usually never make sense to cache in the page cache - like system/cron?token=....
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Wim Leers’s picture

Status: Active » Needs review
Issue tags: +Needs tests
FileSize
1.66 KB

Hrm, do we want another response policy… or do we want a route enhancer that sets options.no_cache = TRUE? I think we actually want the latter. No unnecessary duplication.

(I only realized this after having written a policy already, which I've attached just in case.)

Wim Leers’s picture

FileSize
1.04 KB

So I wanted to give writing a route enhancer a quick try, only to see this:

\Symfony\Cmf\Component\Routing\Enhancer\RouteEnhancerInterface::enhance(array $defaults, Request $request);

i.e. we can only modify route defaults, and it's only supposed to work at request time.

Routing system terminology & docs: sigh…


So, that leaves us with routing events. Fortunately that can be used for this.

Berdir’s picture

What about a combination of a unit test and remove the _no_cache option from the admin cron route once that is committed for test coverage of this?

Wim Leers’s picture

#3: my thoughts exactly :)

Wim Leers’s picture

Issue tags: +Novice, +php-novice
FileSize
1.59 KB

#2461087: Add 'no_cache' route option to mark a route's responses as uncacheable (was: Cron run response should not be cacheable) landed, this is now unblocked. Here's a reroll, that already does half of what #3 suggested.

Still needs the unit test.

dawehner’s picture

Status: Needs review » Needs work
Issue tags: +needs
+++ b/core/lib/Drupal/Core/EventSubscriber/SetNoCacheRouteSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/SetNoCacheRouteSubscriber.php
@@ -0,0 +1,29 @@

@@ -0,0 +1,29 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\EventSubscriber\SetNoCacheRouteSubscriber.
+ */
+
+namespace Drupal\Core\EventSubscriber;
+
+use Drupal\Core\Routing\RouteSubscriberBase;
+use Symfony\Component\Routing\RouteCollection;
+
+/**
+ * Sets the 'no_cache' option for routes inferred to be uncacheable.
+ */
+class SetNoCacheRouteSubscriber extends RouteSubscriberBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function alterRoutes(RouteCollection $collection) {
+    foreach ($collection->all() as $route) {
+      if ($route->hasRequirement('_csrf_token')) {
+        $route->setOption('no_cache', TRUE);
+      }
+    }
+  }
+

This needs an integration test ... its missing the entry in core.services.yml ... a unit test would have not triggered that.

Fabianx’s picture

Issue tags: -needs

I assume this needs Tests :-D

kim.pepper’s picture

Status: Needs work » Needs review
Issue tags:
FileSize
3.71 KB
2.3 KB

Adds a service to core.services.yml and a unit test.

Wim Leers’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: -Needs tests

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 8: 2463303-csrf-cache-5.patch, failed testing.

Wim Leers’s picture

Testbotfail.

Fabianx queued 8: 2463303-csrf-cache-5.patch for re-testing.

Fabianx’s picture

Status: Needs work » Reviewed & tested by the community
alexpott’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs issue summary update

This issue is a task so we need to outline how it fits within the allowable Drupal 8 beta criteria. Can someone add Drupal 8 beta phase evaluation template to the issue summary.

holingpoon’s picture

Issue summary: View changes

Added Drupal 8 beta phase evaluation template.

holingpoon’s picture

Issue summary: View changes
Fabianx’s picture

Issue summary: View changes
Status: Needs work » Reviewed & tested by the community
catch’s picture

Status: Reviewed & tested by the community » Needs work

So I understand that we wouldn't want a CSRF-protected route to end up in the page cache.

However:

- there's very limited use cases for rendering a CSRF-protected link to an anonymous user.
- if you do render a CSRF-protected link to an anonymous user, then that will start a session. Starting the session will disable page caching for the request rendering the link.
- when the user has the session, visiting the CSRF-protected route will also bypass page caching - since they have a session.

So as far as I can see, this already works. If for some reason it doesn't, I think we need a functional test that would fail without the patch, but this looks unnecessary to me.

Fabianx’s picture

Assigned: Unassigned » Wim Leers

We already had a test:

This was one of the blockers for the original "Enable page cache" issue, we since circumvented it by adding no_cache: true as route option, this was just to make this simpler / more automatic for CSRF routes ...

Assigning to Wim for feedback.

Wim Leers’s picture

Status: Needs work » Reviewed & tested by the community

I'm confused why #18 is talking about "CSRF-protected links" so much.

The key example we have in core, and the one that this patch removes the no_cache: TRUE option for, is the "run cron" route. That is by design something that an anonymous user hits.

Writing the above made me realize that you're right; you cannot possibly ever execute /admin/reports/status/run-cron?<CSRF TOKEN> as the anonymous user, because indeed CSRF tokens only ever work for authenticated users. So that's why the patch is able to remove that bit: it was never actually relevant in the first place.

But, it does become relevant in a world where we have #2429617: Make D8 2x as fast: Dynamic Page Cache: context-dependent page caching (for *all* users!), where no_cache-routes also mean that no SmartCache caching happens.

Which indeed makes it impossible to write a functional test for this right now, because this is more about signaling the right information to the Drupal ecosystem: "the output for this route should never be cached", without them having to know about the _csrf_token option, or other options that should prevent caching of the output of the route.

i.e. every route that should never be cached due to some (core or contrib) route options should always get the no_cache option set, just to convey the right information to things like SmartCache (or similar auth caching systems in Drupal 8 contrib). But, you're right that this is technically unnecessary, at least in Drupal core.

Back to RTBC for committer feedback. If you disagree catch, then feel free to mark this as "works as designed".

catch’s picture

Status: Reviewed & tested by the community » Needs review

I'm also confused.

There is system.run_cron /admin/reports/status/run-cron? this for manually running cron via the UI - this has a permission check and a CSRF check. Anonymous users can never get to this.

Then there is system.cron: /cron/{key} which is for running cron via wget/curl and requires the cron key. This does not use CSRF protection and has to explicitly opt out of caching. Can be visited by anonymous users.

In terms of SmartCache this makes more sense, but I'm having trouble understanding why we need to set this on the route, and not just set max-age=0.

Apart from the lack of hit rate for csrf-protected routes, the main thing is that something happens (the operation that is being CSRF-protected like flagging) that needs to happen every time the route is hit.

Also my understanding of smartcache was that it would operate pre-routing, so how does having the information on the route help - wouldn't we need to run routing, to get the route, to check this property, to decide whether cache get or not - would be quicker just to cache get all the time and never set it no?

Wim Leers’s picture

but I'm having trouble understanding why we need to set this on the route, and not just set max-age=0.

That'd be fine too. But in some ways, this is easier. Being able to reason about routes whose responses are always uncacheable means we don't have to execute the route's controller to know that. That's the key difference.

Also my understanding of smartcache was that it would operate pre-routing, so how does having the information on the route help - wouldn't we need to run routing, to get the route, to check this property, to decide whether cache get or not - would be quicker just to cache get all the time and never set it no?

SmartCache runs very much after routing. It relies on the route match to know what it needs to return. We could make SmartCache work at the URL level rather than the route level though.

SmartCache opts out of _admin_route routes, i.e. it never caches anything there. Similarly, it would be able to opt out of no_cache routes. That's where it would be useful.

Version: 8.0.x-dev » 8.1.x-dev

Drupal 8.0.6 was released on April 6 and is the final bugfix release for the Drupal 8.0.x series. Drupal 8.0.x will not receive any further development aside from security fixes. Drupal 8.1.0-rc1 is now available and sites should prepare to update to 8.1.0.

Bug reports should be targeted against the 8.1.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Mile23’s picture

Version: 8.1.x-dev » 8.2.x-dev
Status: Needs review » Needs work
Issue tags: -Novice
andypost’s picture

In related issue raised good points to exclude CSRF for anonymous users

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.0-beta1 was released on August 3, 2016, which means new developments and disruptive changes should now be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.0-alpha1 will be released the week of January 30, 2017, which means new developments and disruptive changes should now be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.0-alpha1 will be released the week of July 31, 2017, which means new developments and disruptive changes should now be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.