A Drupal module that helps developers identify missing cache tags in their pages by tracking loaded entities and verifying their cache tags are properly bubbled up to the response.
- Tracks loaded entities and their cache tags, compares with tags bubbled to the HTTP response object
- Configurable operation modes (disabled, display errors, strict)
- Option to check only front-end pages or include admin pages
- Ability to skip specific URLs
- API for other modules to exclude certain entities from tracking
- Custom PHPCS sniff for Twig template best practices
Visit /admin/config/development/cmc
to configure the module. The following options are available:
- Disabled: No cache tag checking (default)
- Display errors: Shows missing cache tags at the top of affected pages
- Strict: Throws exceptions when cache tags are missing (recommended for development)
When enabled (default), the module only checks pages using the default theme. When disabled, it also checks admin pages using the admin theme.
Define specific paths that should be excluded from cache tag checking.
Modules can implement this hook to exclude certain entities from cache tag tracking:
/**
* Implements hook_cmc_skip_tracking().
*/
function mymodule_cmc_skip_tracking(EntityInterface $entity): bool {
// Skip tracking for specific entity types or conditions.
if ($entity->getEntityTypeId() === 'my_entity_type') {
return TRUE;
}
return FALSE;
}
- Enable "Strict" mode during development to catch cache tag issues early.
- Use "Display errors" mode on staging environments for visual feedback or to use tools such as VisualDiff tests.
- Never enable the module in production environments.
- Consider excluding admin pages to avoid unrelated issues when developing the front-end theme.
The module includes a custom PHPCS sniff that helps maintain Drupal best practices in Twig templates:
This sniff detects direct field access in Twig templates using patterns like node.field_*
, content.field_*
, etc.
This pattern is discouraged because it may introduce caching bugs by leaving out the proper cache tags in the top-level
render array.
-
Install development dependencies:
composer require --dev drupal/coder squizlabs/php_codesniffer
-
Register the custom standard:
vendor/bin/phpcs --config-set installed_paths /path/to/web/modules/custom/cmc/phpcs
Note: the example above will set your
installed_paths
to include only the path to this module's sniffers, which is likely not what you want. You can pass a comma-separated list of paths to the above command, or include the line below in yourphpcs.xml.dist
configuration file:<config name="installed_paths" value="../../drupal/coder/coder_sniffer,../../sirbrillig/phpcs-variable-analysis,../../slevomat/coding-standard,../../../web/modules/custom/cmc/phpcs" />
-
Add the standard to your
phpcs.xml.dist
:<rule ref="web/modules/custom/cmc/phpcs/ruleset.xml"/>
Also make sure you allow
twig
as one of the extensions to be sniffed, in case you have an<arg name="extensions">
config value. -
Run the sniffer:
vendor/bin/phpcs /path/to/your/templates `--standard=CMC`
If you included the standard in your
phpcs.xml.dist
configuration file, you can omit the--standard=CMC
flag.
The sniff will automatically skip the warning under these conditions:
-
If the template renders the full
content
variable:{{ content }}
-
If the template renders cache metadata explicitly:
{{ content['#cache'] }}
-
If you explicitly opt-out using the special comment:
{# cmc_direct_field_access_sniff_opt_out #}
❌ Not recommended:
{# Direct field access will trigger warning #}
{{ node.field_image }}
{{ content.field_tags }}
{{ media.field_media_image }}
✅ Recommended:
{# Render the full content variable #}
{{ content }}
{# Render cache metadata & assets explicitly #}
{{ node.field_image }}
{{ {'#cache': content['#cache'], '#attached': content['#attached']} }}
{# Or opt-out if you know this is being handled elsewhere #}
{# cmc_direct_field_access_sniff_opt_out #}
{{ node.field_special_case }}
Example warning:
FILE: /path/to/template.html.twig
--------------------------------------------------------------------------------
FOUND 1 WARNING AFFECTING 1 LINE
--------------------------------------------------------------------------------
15 | WARNING | Direct field access using "node.field_image" is not recommended.
| | Try to always render full render arrays that come from the backend.
--------------------------------------------------------------------------------