A Drupal site should not rebuild the same public page from scratch for every visitor. Caching lets it reuse work while keeping edited content up to date. The best starting point is a reliable core configuration, followed by a small set of repeatable checks.
This guide covers a basic setup for Drupal 10 and 11. Start on a staging copy, keep a database backup, and record your current settings. The example suits a public company website or blog; sites with anonymous shopping carts or other personalized experiences need a separate caching review.
1. Record a baseline before changing settings
Choose the homepage, one article, and one listing. Open them while logged out and record their loading behavior in your browser's Network panel. Also test an editor's normal workflow separately. Note the URL, device, connection, and whether you have already visited the page.
Look at time to first byte, which measures how long the browser waits for the response to begin, as well as image sizes and JavaScript downloads. A faster server response will not automatically fix an oversized hero image. Keep a short before-and-after record so you can tell which change helped.
2. Check Drupal's built-in cache modules
Open Extend at /admin/modules and check these core modules:
- Internal Page Cache: reuses complete pages for anonymous visitors when those pages are eligible for caching.
- Internal Dynamic Page Cache: reuses cacheable page output while handling personalized parts separately. It benefits both anonymous and authenticated browsing.
Internal Page Cache assumes anonymous visitors receive the same content. Do not enable it blindly for pages that vary by an anonymous visitor's session. Dynamic Page Cache depends on correct cache metadata in the components being rendered. See Drupal's Internal Page Cache documentation and Dynamic Page Cache overview.
If both modules are appropriate for your site, enable them through the interface or run this from a Composer project's root:
vendor/bin/drush en page_cache dynamic_page_cache -y
With DDEV, the equivalent is ddev drush en page_cache dynamic_page_cache -y. You do not need to install an extra caching module to begin.
3. Choose a browser and proxy cache lifetime
Go to Configuration → Development → Performance at /admin/config/development/performance. Set Browser and proxy cache maximum age according to how quickly visitors must see changes.
For example, choose 5 minutes for public service pages if the business accepts that delay. This setting controls HTTP cache headers; it is not a five-minute expiry for Drupal's Internal Page Cache. Setting it to zero does not disable that internal cache. Drupal normally refreshes its own dependent cache entries through invalidation.
A browser or CDN may still hold an older response. Before choosing a longer lifetime, decide how urgent corrections will reach visitors. Purging a CDN does not erase copies already stored in visitors' browsers.
The equivalent example setting is:
vendor/bin/drush cset system.performance cache.page.max_age 300 -y
4. Enable CSS and JavaScript aggregation
On the same Performance page, enable the CSS and JavaScript aggregation options and save. Aggregation combines eligible assets to reduce requests; it complements page caching. Drupal documents these controls in its core aggregation guide.
The command-line equivalents are:
vendor/bin/drush cset system.performance css.preprocess true -y
vendor/bin/drush cset system.performance js.preprocess true -y
Retest menus, forms, sliders, and tracking after this change. If one custom library fails, investigate that library's dependencies and execution order before disabling optimization across the whole site.
5. Review caching on expensive Views listings
Open Structure → Views, edit a listing, and select the relevant page or block display. Expand Advanced and inspect Caching. Check whether a change applies to this display or every display using the default.
For an ordinary content listing, start by reviewing Tag based caching. Drupal's core tag-based Views plugin keeps cached data until related tags are invalidated. Test creating, editing, and unpublishing a listed article, including the listing's pagination and exposed filters.
A listing driven by the clock, such as upcoming events, needs a planned refresh when time passes even if nobody edits a node. Time-based Views caching may be part of that design, but a longer page or CDN cache can still serve an old listing. Review every layer together. Custom queries and access rules also need a developer to confirm their dependencies.
6. Confirm that caching actually works
Use a logged-out browser or run the following GET request twice against the same public URL. Replace https://example.com/ with your site's final HTTPS address, avoiding a redirect, login session, or changing query string:
curl -sS -D - -o /dev/null https://example.com/
On an eligible page reaching Drupal, X-Drupal-Cache: HIT indicates an Internal Page Cache hit. A first request may show MISS. A simultaneous X-Drupal-Dynamic-Cache: MISS does not by itself mean caching failed: the outer page cache can reuse a response containing that earlier header.
Check Cache-Control too. At a CDN, inspect its cache-status header and Age; Drupal headers in a stored CDN response describe the original response, not necessarily new work by Drupal. If you receive a bot challenge, investigate access first.
Then edit a test article and confirm its page, listing card, and homepage teaser update. Unpublish it and check anonymous access. A fast cache that serves stale or private content has failed the test.
7. Check development overrides and custom components
If caching still behaves unexpectedly, inspect the settings and service files actually loaded on that environment. A development configuration may select cache.backend.null for render or page-cache bins. Review those overrides before deploying; do not remove unrelated database, proxy, or security settings.
For custom blocks, ask a developer to verify cache tags for content dependencies, cache contexts for variations, and an appropriate maximum age. Preserve metadata when rendering referenced entities. Our shared service card example explains how these pieces fit together. Repeatedly clearing every cache can hide a missing dependency.
8. Add Redis or a CDN only when the workload needs it
Redis: an optional cache backend
Redis can move supported Drupal cache storage out of the database. It does not make an uncacheable response cacheable. Before installing anything, confirm that your host provides a Redis service, a supported PHP client, and appropriate connection security.
On staging, select a compatible release of the Drupal Redis module, install it with Composer, and follow that release's README for service definitions and settings. Keep connection secrets in environment-specific settings. Verify which cache bins use Redis and rehearse rollback before changing production. Installing the module alone is not a complete Redis configuration, and shared hosting may not support the service.
CDN: start with public assets
A CDN can serve eligible images, CSS, and JavaScript closer to visitors. Cloudflare does not cache HTML by default, so enabling its DNS proxy is not the same as enabling full-page caching. See its default cache behavior.
For a basic setup, keep the provider's normal asset caching and verify the response headers. Before adding HTML rules, configure appropriate bypasses for authenticated sessions, administrative and account routes, private content, and form submissions. Establish how content edits purge affected pages. Avoid a blanket rule that stores every response, and retest with both anonymous and signed-in accounts.
9. Deploy and repeat the checks
For a project using configuration management, export the tested changes with vendor/bin/drush cex -y, review the diff, and deploy through the normal workflow. Keep environment-specific connection details outside exported configuration.
Use vendor/bin/drush cr when a configuration or code change requires a rebuild. If your deployment already rebuilds caches, do not immediately repeat it. Avoid running cache rebuilds on every cron execution or ordinary content edit. After a rebuild, visit a few important pages to warm their caches and repeat the same measurements.
- Public pages show the expected cache behavior on repeated requests.
- Edited and unpublished content updates across pages and listings.
- Private content remains private, and forms and logins still work.
- CSS, JavaScript, and images load correctly on mobile and desktop.
- Measured improvements hold across several comparable visits.
Start with these core settings and tests. If the site remains slow, review the specific page, query, integration, or hosting limit responsible. Druproid Support can help with a Drupal maintenance and performance review or a focused caching investigation.