When identifying bottlenecks, select two or three pages of different types to start with. Ideally these will be the most popular types of page on the site—for example, article pages or user profiles, as well as a landing page such as the front page.
scripts property in .info files, or via
grep the code base for where it’s added. Start with the files loaded last, since the early files like jQuery and drupal.js may only be loaded due to dependencies.
While all pages on Drupal sites will include CSS, a very similar approach can be taken when trying to reduce the amount of CSS loaded overall.
The file has been added via the
styles .info property, despite not being needed on every request. Try to find which markup the file actually affects, then file a bug report for the module on Drupal.org to add it conditionally via
hook_node_view() as follows.
* Implements hook_node_view().
After reviewing the site with aggregation disabled, reenable it and view the pages again. This time it won’t be possible to see which individual files are being included, but instead you can look at the resulting aggregates of the pages and their comparative sizes.
A common problem with both the Drupal 7.x/8.x and Drupal 6.x aggregation strategies is that they’re fragile when files are added incorrectly—for example, if they’re added in different orders by different modules, or if the
Images embedded in content via the
For images loaded via CSS, there are more options. Go back to Firebug or Chrome DevTools to look for image requests; the paths will tell you whether they come from core, contributed, or custom modules, or themes.
Most Drupal 8 modules do not provide much default styling, with the exception of content forms, administrative features and user-facing menus which do have some icons.
There are several approaches for reducing image requests:
data-uri. This means they are served as part of the CSS file itself rather than downloaded separately, saving an HTTP request for each image that’s inlined. Remember that the larger your CSS file is, the longer it takes before the browser can download and parse it and move on to other things (like downloading images served via
imgtags), so this is a trade-off that needs to be made carefully if at all. This is supported by a contributed module for Drupal called CSS Embedded Images that automatically inlines images when the CSS is preprocessed.
data-urias well, saving a further HTTP request.
data-uri, or embedded into HTML using either
data-urior the SVG format itself, which provides a great deal of flexibility in terms of how they’re served.
Both icon fonts and SVG have significant advantages over the older techniques of sprites and base64 encoding of binary images; however, some older browsers don’t support them, so you may need to include a polyfill library if your site requires them.
If you have automated code deployment, minification could be added as a step in building releases (this is also something that could be considered for Drupal.org project packages). This is really a site-specific version of using/contributing to the Speedy module and is only mentioned here for completeness.
Serving files with gzip compression and respecting
variable_set() in Drupal 7, or via the configuration API in Drupal 8. You will also need to edit your .htaccess to comment out the rules for rewriting filenames, since the Apache module will be handling serving the correct file instead. Note that there’s no UI provided for this in the administration screens. To see the configuration options in Drupal 8, either review the aggregation code itself, or look at system.performance.yml:
max_age value of the
Cache-Control header can be set via admin/config/development/performance or the configuration API; assets are set to have an
Expires header of two weeks via .htaccess if
mod_expires is enabled in Apache. For sites that aren’t undergoing frequent releases, you may want to tweak this upward. If you’re not using Apache, you’ll need to ensure that you handle cacheable headers for static assets in the web server you’re using.
See Chapter 19 for more information on CDNs.
jQuery Update also provides an option to serve the minified jQuery file via Google’s CDN rather than from the module folder. If you’re not already using a CDN, this allows quite a large file to be served via a CDN “for free.” There’s also the potential that site visitors will have visited other sites that serve the same jQuery version prior to visiting yours and already have it cached, although how likely this is depends on the traffic patterns of your site’s visitors and overall adoption of the Google CDN. If you have a family of sites all running Drupal with lots of traffic between them, the chances of this happening might be increased.
However, this does mean an extra DNS request, a dependency on Google’s infrastructure, and an extra HTTP request, since jQuery will no longer be included in aggregates, so be aware that there are trade-offs in both directions.
Regardless of the quality and performance of Drupal core, contributed modules, and your own custom module or themes, all of that optimization and thought can go to waste—or at least be cancelled out—as soon as you add analytics, social widgets, advertising, and similar external services to a site.
All external services are different, but there are several rules of thumb that apply to most. We’ll look at some of them in the next section.
If example.com is unable to serve the request in a timely manner, browsers will wait until either it eventually serves the request, or the request times out before rendering the full page. This can result in large blank sections below where the script is included or even entirely blank pages, depending on the browser and the location of the
script tag, not to mention potential delays of 30 seconds or more. Most of the optimizations in this chapter have focused on changes that are likely to save milliseconds, hundreds of milliseconds, or perhaps a couple of seconds at most; yet a single external script can render a site unusable—potentially as unusable as an outage on your own infrastructure, in terms of the end user experience.
The SPOF-O-Matic browser plug-in by Patrick Meenan both flags likely single points of failure and can simulate complete failure for any external script it finds on your pages. This allows SPOFs to be found easily and provides an easy way to demo just how bad they are to anyone who might question the importance of handling external scripts carefully!
Even scripts loaded asynchronously can block the browser
onload event. Note that techniques in this area change frequently; some services still support (and advertise in their documentation) snippets they provided several years ago and that might be found on sites in the wild, and some services have ignored these techniques and exclusively provide snippets that will cause a SPOF.
To avoid this, ensure you audit sites for SPOFs; SPOF-O-Matic is great for this. When adding scripts, avoid any temptation to embed markup or
script tags directly into a page.tpl.php, head.tpl.php, or any other template or custom block, and use Drupal APIs such as
drupal_add_html_head() instead. Better still, if a contributed module supports the service, consider enabling the widget or analytics via that module instead of custom code, as the contributed project has a better chance of keeping up with newer versions of the snippet than you do.