Drupal Performance: Significant Improvements for Little Effort
Website performance is arguably the single most important area where you can invest your development time and resources. No matter how unique your marketing message is, or how empowering your product, you will not see conversions if your site is slow to load. Mobile users on fast connections are still subject to network intermittencies. Peak traffic congestion can degrade the speeds that visitors normally connect with. Even disregarding dialup users, plenty of your desirable segment of visitors may be browsing from airports, traveling internationally, or at a coffee shop. Performance is even a significant factor for SEO since Google led the trend in incorporating speed benchmarking and mobile experiences into their ranking algorithms. To further compound the issues, using CMS like Drupal or WordPress can hamper your site’s performance. Their out-of-box configurations are meant to be generically all-purpose, so they usually don’t incorporate much beyond the simplest of optimizations. Follow along as I walk you through the steps I took in dramatically improving the performance of my Drupal site in just a single afternoon.
To start, we need to establish the current state of the site. To ensure consistent measurements, I’m going to open up an Incognito window in Chrome, visit my homepage, and run the Lighthouse performance audit built-in to Chrome’s Developer Tools.
Updating Drupal Core and Modules
Considering I haven’t touched my site in almost 5 years (with the exception of applying security updates), I am quite surprised to see that the performance (83/100) and SEO (90/100) scores are quite good. I would have expected in that time that there would be more recommendations, but the basic premises of good semantic HTML, accessibility, and an eye towards limiting file size still hold true even in the face of dramatic changes in development trends. The Progressive Web App score (38/100) is understandably low, as this site was built before PWAs were even defined. Most of the recommendations there stem from not having a manifest file, which I anticipate will be easy to remedy. I’m sure there will be other heavier lifting to do around the more advanced PWA features, but we’ll get to those a bit later and tackle the more important stuff first.
Before tackling the items in the report, lets log into the Drupal administration and see whats going on there in the status report:
I want to avoid having to redo any work. Given that there are several important security updates for Drupal and my various plugins, it’s important that I address those before any performance improvements. As I review my current site status and follow my usual practice of making comprehensive backups, I notice some suspicious files on the webserver, leading me to take an hour or two to cleanup a compromised Drupal site, which I've covered in a separate article.
After cleaning up the site, I also addressed some out of date plugins that needed replacing, specifically replacing Mollom with Akismet for spam control. It doesn’t do anything for performance, but automating away drudgery is the whole reason why we have computers in the first place.
Addressing Degraded Performance
Now that the Drupal core and modules are all up to date with security patches, let’s go back and run that Lighthouse audit again:
Woah!! What happened? The performance metric tanked! Accessibility went up from 63/100 to 73/100, but performance dropped from 83/100 to 48/100 and Progressive Web App tanked from 38/100 to 12/100. Since the PWA audit takes performance into account, it makes sense that the numbers would be related, but I shouldn’t have seen such a significant falloff. If I look at the recommendations, it’s suggesting I lazy load offscreen images, and now that’s the number one recommendation? What’s changed? This wasn’t a significant issue before the updates. This is exactly why I ran an audit before making changes, so I can attribute and diagnose issues correctly.
If we look closer, the file sizes reported on the images are much larger than the previously were:
When I look closer at the page itself, I can see the blog post headline images are breaking out of the frame too:
This clues me in to what might be the problem. Drupal has a fantastic image resizing system that can be used to dynamically generate the right size images for any particular displays. The images here are much larger (both in pixels and file size) than what I had before the updates, so I’m betting that something in my image presets in Drupal got hosed somehow, and large versions of the images are being shown instead of the cropped and resized versions. I go to the Media module settings and check that the preset for blog headers still exists and is configured as intended. It looks good. Next, I try flushing site wide caches. Still seeing the wrong sized image loaded.
The other place where the image presets could be misconfigured is in the Views system. I find that I have it configured so that the image on each blog post is stored as an entity, and that entity shows up in “teaser” mode for the parent post. Further investigation reveals that these entities load from the portfolio node types. So I open up the display settings for Portfolio content types, find the Photo field in Teaser view, and see that the image field display style is set to original. That should have been preserved in the upgrade, but is an easy fix once found. I change that back to article_header (my defined dimensions):
A quick site wide cache flush, and teasers are showing correctly again:
So let’s run Lighthouse audit again and see what kind of improvement we’ve made:
OK, seems that fixed most of the performance issues reported. In fact, the original score of 83/100 is now up to 87/100 and the Accessibility score is up to 73 from the original 63, so that’s much better than what the site was scoring before updating Drupal. There must have been some under the hood updates in the core system. At this point, the site is all patched up, and in a stable state, so we can continue reviewing and improving on the performance recommendations.
Increasing Cache Time to Live
Let’s start with the quick wins that don’t require significant changes to Drupal or site functionality. The first one that stands out is the Caching Policy which controls how long a visitor’s browser will attempt to retain items in the local cache. This is otherwise known as Time To Live (TTL). Drupal’s default for everything is 2 weeks, but that’s far too low, and easily updated. Read on for more on how to improve the Cache Policy on Static Assets for a Drupal site.
Reducing Render-Blocking Assets
The next item that stands out as an easy win is render-blocking assets. These are requests that delay the page from showing. Usually they’re scripts and css files linked in the document head. You want as few as possible, because the browser cannot show any part of the page until these scripts are loading. Reducing or eliminating them entirely will improve the visitor’s perception of performance, as they’ll start seeing page contents earlier during load. It takes multiple steps, but I was able to reduce the number of render-blocking assets on my Drupal site in fairly short order.
Prefetch DNS Lookups
Let’s see, what else…. this aggregation module that I added to address the render-blocking assets is also automatically handling the addition of prefetch tags for some of the 3rd party CDN domains. Nice! Though there are some other ones in my templates that can benefit from this improvement, and are easy to manage manually:
One of these is the social-sharing plugin AddThis, and the other is Google Analytics. Both are being added directly from my theme, so I can also add dns-prefetch or preconnect link tags to my theme for these 3 domains, and clear up this suggestion, saving 0.37s of page load.I don’t want these to slow down my page load by prefetching them (analytics and sharing scripts can safely load late), but I do want the DNS query to happen, I’m going to use <link rel="dns-prefetch"> here instead of “preconnect”. In the head section of my template.php, before any scripts are output I add these lines to cover the 3 remote domains being used.
<link rel="dns-prefetch" href="https://www.google-analytics.com"> <link rel="dns-prefetch" href="http://s7.addthis.com"> <link rel="dns-prefetch" href="http://m.addthisedge.com">
Let’s see where we’re at by running the Lighthouse audit again:
After all the improvements, the performance score has made a significant improvement, up to 91/100. But I think I can do better.
Addressing Invisible Text While Webfonts are Loading
As I mentioned previously, Progressive Web Apps are tightly dependent on performance, and offer some features for furthering a site’s page load through caching and prefetch mechanisms. Thus addressing the Lighthouse Progressive Web App score will improve the page speed scores as well (not to mention help in Google’s assessment for SEO rankings). Since that’s a bit more detailed, I’ve split it into a separate article on implementing Progressive Web App capabilities on a Drupal site.
To summarize all these changes were completed in a single afternoon, we’ve achieved some great results for the effort:
The Lighthouse audit shows that Performance has increased from 83/100 to 98/100. Progressive Web App has gone from 38/100 to 100/100. Accessibility is up from 63/100 to 73/100. Best Practices are up a bit from 73/100 to 80/100, and SEO is holding steady at 90/100.
With a relatively small investment of time and effort, and a willingness to get into some code or do some research on plugins, it’s fairly simple to significantly improve the Lighthouse audit scores for a Drupal site.
The next day I did some quick browsing of my site, impressed at how snappy it feels, and discovered inline images in articles were broken. Fixing them in bulk was simple, but lesson learned, don't just rely purely on the audits.