Disabling production optimization
Starting from version 8, Drupal is optimized for production out of the box. This means that all cache layers are enabled, and a change in the code is probably not visible until the cache is cleared. Obviously, this can slow down development a lot. In the earlier phases of development, all those optimizations can be turned off, but it is mandatory to turn them all back on before production because running a Drupal site without the cache enabled can hide some very difficult-to-catch bugs. How many times have you heard of data breaches where a legit logged-in user had access to data about some other user? This is probably a cache issue, where a page has been built for a user and then cached and served as is to every other user. If that page contains some personal data, that is leaked to everyone.
Note
Drupal is a complex system; to build a single page, it needs to do a lot of things, and most of them involve some sort of interaction with an external (and usually slow) system (the database for the data, the filesystem for the code, and others). If the markup that Drupal is building can be used as is for multiple requests, it’s a waste of resources to build it over and over again. Cache systems exist precisely to store the intermediate steps of a complex calculation to avoid doing the same thing twice. This text is not part of the note. It must be moved outside, just before the next sentence (At the end...)
At the end of the web/sites/default/settings.php
file, we’ve added these lines:
if ( file_exists($app_root . '/' . $site_path . '/settings.local.php') && getenv('IS_DDEV_PROJECT') == 'true' ) { include $app_root . '/' . $site_path . '/settings.local.php'; }
This basically means that if some conditions are true
, the system should include a configuration file with local settings. The conditions are set out here:
- Check whether the
$app_root . '/' . $site_path . '/settings.local.php'
file exists.app_root
resolves to the/var/www/html/web
folder, andsite_path
resolves to thesites/default
folder - Check whether the value of the
IS_DDEV_PROJECT
environment variable istrue
Both conditions are true on our local stack; the file it’s located at /var/www/html/web/sites/default/settings.local.php
and its content has been copied from /var/www/html/web/sites/example.settings.local.php
, with a couple of lines uncommented. Let’s see the most important ones:
$settings['container_yamls'][] = DRUPAL_ROOT . '/sites/development.services.yml';
Drupal organizes all the business logic into services, discrete PHP classes that perform a specific task (such as sending an email, rendering a template, writing to logs, and so on). All those services are managed by a service container (https://symfony.com/doc/current/service_container.html
). When some code needs a service to perform a task, it asks the service container to provide one, along with all its dependencies. The service container also manages parameters, which are scalar values that can be used to configure the application.
One of the services defined in the service container manages the integration with a cache backend.
A cache backend is used to store data that is slow to be computed. Instead of executing the same slow code on every request, Drupal can store the results in a cache. This speeds up the building and delivery of pages.
By default, the cache backend is the same database that Drupal uses for content (managed by a service called cache.backend.database
). Every time Drupal needs to save some intermediate computation, it passes the data to the cache system, which in turn uses the cache backend to persist it somewhere.
We can use the development.services.yml
file to add or change services and parameters in the service container. Let’s break down the file to see what it does:
parameters: http.response.debug_cacheability_headers: true
This parameter forces Drupal to add a couple of HTTP headers to the response. This will be useful later when we talk about cache tags.
twig.config: debug: true cache: false
Those two lines configure Twig (Twig is the render engine that turns templates and data into HTML), by enabling debug
and disabling the cache.
services: cache.backend.null: class: Drupal\Core\Cache\NullBackendFactory
Lastly, here we define a new cache backend service, cache.backend.null
, which uses the NullBackendFactory
class to manage the cache. NullBackendFactory
doesn’t persist the cache anywhere, basically forcing Drupal to rebuild its data structures on every request.
Continuing with the settings.local.php
file, we find the following:
$config['system.performance']['css']['preprocess'] = FALSE; $config['system.performance']['js']['preprocess'] = FALSE;
Those two lines disable the CSS and JavaScript preprocessing and minification system of Drupal. When preprocessing is TRUE
, if you look at the source of any Drupal page, you will see something like this:
<link rel="stylesheet" media="all" href="/sites/default/files/css/css_4X4pNrCAbSu81rOnCPB9UMoipKt5NIfi5kpoa86bdG8.css" /> <link rel="stylesheet" media="all" href="/sites/default/files/css/css__4bA7B8aKHQdv-woFVLAGpXcUrTGif-jwiZJPj5yZ1s.css" /> <script src="/sites/default/files/js/js_Y151moVVESM9BeoCt8M3cSJ2_x_o14H43dXy6nGL6JM.js"></script>
This is difficult to understand because isn’t clear where a CSS rule or a JavaScript function comes from (also because the CSS in those files are minified). When preprocessing is FALSE
, the output is way more readable:
<link rel="stylesheet" media="all" href="/core/themes/stable9/css/system/components/ajax-progress.module.css?rmstqh" /> <link rel="stylesheet" media="all" href="/core/themes/stable9/css/system/components/align.module.css?rmstqh" /> <link rel="stylesheet" media="all" href="/core/themes/stable9/css/system/components/autocomplete-loading.module.css?rmstqh" />
In the preprocessed version, the filename changes every time the cache is cleared, and this forces the browser to download the file again. When preprocessing is turned off, Drupal emits the original name of the file. To force the browser to download the file again, a token is appended at the end.
Three more lines deserve attention in settings.local.php
:
$settings['cache']['bins']['render'] = 'cache.backend.null'; $settings['cache']['bins']['page'] = 'cache.backend.null'; $settings['cache']['bins']['dynamic_page_cache'] = 'cache.backend.null';
Those lines are commented in the original example.settings.local.php
file. By removing the comments, we force the cache bins that deal with HTML generation to use the cache.backend.null
service that we added in the default.services.yml
file.
Note
Starting from Drupal 10.1 there is an easier way to manage development settings. A new Configuration | Development settings page has been added that allows you to turn on Twig development mode and to turn off render cache, dynamic page cache, and page cache. You can find more information on the related change record here: https://www.drupal.org/node/3359728.
Enabling Twig debug and disabling caches slow down the website but will allow us to see a change in template as soon as we reload a page, without the need to clear the cache. As we mentioned before, the functionalities of the website must be tested with those caches enabled, as they will be in production, to avoid possible data leaks.