Drupal Performance Optimization: Caching and Speed Tips
By Pawan Singh · Jun 2026
Drupal's caching system is deep and can carry a site through very high traffic when used correctly — the usual performance problems come from not using it correctly, not from Drupal being inherently slow.
Understand Cache Tags Before Tuning Anything Else
Drupal's render cache doesn't just cache pages for a fixed time and hope nothing changed — every cached render array is tagged with the entities it depends on (node:42, user:7, config:my_module.settings). When one of those entities is saved, Drupal invalidates exactly the cached items tagged with it, nothing more. This is why Drupal can cache content aggressively and still show fresh data immediately after an edit — it's precise invalidation, not a short TTL guessing game. Custom code that renders dynamic content should propagate the right cache tags/contexts itself; skipping this is the most common reason "the page looks stale after I edited it" bugs happen in custom modules and views.
Enable Internal Page Cache and Dynamic Page Cache
Both ship in core and should be on for any production site:
vendor/bin/drush en page_cache dynamic_page_cache -y
Internal Page Cache caches entire responses for anonymous visitors — the fastest possible path, since Drupal's bootstrap barely runs at all for a cache hit. Dynamic Page Cache caches the parts of a page that are the same for every visitor (the main content) while leaving genuinely personalized parts (a "logged in as X" block) dynamic — this is what makes authenticated-user pages still benefit from caching instead of bypassing it entirely.
Configure a Reverse Proxy / CDN
For anonymous traffic specifically, a reverse proxy in front of Drupal (Varnish, or a CDN like Cloudflare) serving Drupal's cached HTTP responses directly — without hitting PHP at all — is the single biggest lever for anonymous page load performance and server load reduction. Set $settings['reverse_proxy'] = TRUE; and the trusted proxy IPs in settings.php so Drupal correctly reads the real visitor IP from forwarded headers rather than the proxy's own IP.
OPcache
PHP's OPcache caches compiled bytecode so PHP doesn't re-parse and re-compile every file on every request — this is a PHP-level setting, not Drupal-specific, but it matters enormously for any PHP application, Drupal included:
opcache.enable=1
opcache.memory_consumption=256
opcache.max_accelerated_files=20000
opcache.validate_timestamps=0
validate_timestamps=0 gives the biggest performance win but means OPcache won't notice a changed file on disk — only use it in production, and clear OPcache explicitly (or restart PHP-FPM) as part of every deploy.
Use a Persistent Cache Backend, Not the Database
By default, Drupal stores its internal caches in database tables — functional, but slower than a dedicated cache store under real load, and it adds write pressure to the database that a separate cache backend avoids entirely. Install Redis or Memcache and configure it as the cache backend in settings.php via the redis or memcache contrib module — this is a standard, well-supported production configuration, not an exotic setup.
Optimize Views
Views (Drupal's query-builder UI) is powerful but easy to build inefficient queries with, especially once relationships and multiple filters stack up. Enable Views' own query caching (per-view, under the view's Advanced settings), and check the generated SQL (via Views UI's "Query build time" / devel module) for anything doing more joins or subqueries than the actual output needs — a view rendering a simple list sometimes accumulates unnecessary relationships added during earlier iteration.
Aggregate and Minify CSS/JS
Under Configuration → Development → Performance, enable CSS and JavaScript aggregation for production — this combines and minifies asset files, cutting the number of HTTP requests and total payload size per page. Leave both off in local development, where they make debugging harder for no benefit.
Image Styles Instead of Full-Size Images
Never let templates output the original uploaded image directly — define appropriately-sized image styles (Configuration → Media → Image styles) for each context (thumbnail, hero, card) so visitors download an image sized for where it's actually displayed, not a multi-megabyte original scaled down by CSS.
Profile Before Optimizing Blindly
Use the core Devel module (with the Webprofiler sub-module) or Blackfire/Xdebug's profiling mode to find where time is actually going — a slow page is just as often one badly-designed custom query or an uncached third-party API call in a block as it is anything cache-configuration related. Optimizing cache settings won't help if the actual bottleneck is a single N+1-style query pattern in custom code.
Common Issues
- Page cache "not working" for logged-in users — expected; Internal Page Cache only applies to anonymous traffic by design. Dynamic Page Cache is what benefits authenticated visitors.
- Content changes not appearing despite aggressive caching — almost always a missing cache tag on custom code, not a Drupal core caching bug; audit what cache metadata your custom render arrays declare.
- Site slow only for large content listings — check for missing pagination or an inefficient Views query rather than assuming it's a general caching problem.
- OPcache changes not taking effect after a deploy —
validate_timestamps=0means OPcache genuinely won't notice; restart PHP-FPM as part of the deploy process.
Dealing with a slow Drupal site, or planning one that needs to handle real traffic? Get in touch — I work as a freelance Drupal developer.