Introduction
Google dominates the search engine market for a large part thanks to its spartan, no-bells-nor-whistles interfaces. But also thanks to its incredible speed (which is partially thanks to that spartan interface, of course).
Since youāre reading this article, youāre probably a Drupal developer.
Itās pretty likely that youāve had some visitors of your Drupal-powered web
site complain about slow page load times. It doesnāt matter whether your
server(s) are shared, VPSes or even dedicated servers. Visitors that live
abroad ā i.e. far from where your servers are located ā will face the same
performance issues, but at even worse scales.
This article is about
tackling these issues.
Front-end performance
Faster servers with more memory stop improving your web siteās performance at some point. Yet, even before your web site gets big, there are other places to look at to improve performance, where greater effects can be achieved, even at lower costs ā significantly lower costs actually. Typically, less than 20% of the total response time is used to retrieve the HTMl document. That means the other 80+% is used to process whatās in the HTML file: CSS, JS, images, videos. And in many cases, that number is even higher.
Depending on your website, your server(s), et cetera, these optimizations will probably shave off between 25 and >100 percent (estimated) of your page loading time. Initial (empty cache) and consecutive page loads (primed cache) will both be significantly faster, unless youāve already done your own round of optimizations.
Much thanks go to Yahoo!ās research that resulted in fourteen rules and the accompanying YSlow tool (weāll get to that in a second) that allows you to check how your web site performs according to those rules. If you can apply all fourteen successfully, your web site should fly. (Assuming that your page generation times arenāt super slow, of course.) As always, more optimizations are still possible. Iāll discuss some very effective ones briefly at the end.
YSlow
First things first: make sure youāve installed Firefox, Firebug and YSlow for Firebug (version 0.9 or better).
Firebug is simply a must-have for any web developer, it doesnāt matter whether youāre a professional or an amateur. YSlow is a Firefox add-on developed by Yahoo!, that analyzes your web page and tells you why exactly (remember those fourteen rules?) your site is slow (hence āy-slowā, which is pronounced as āwhy slowā). But at the same time, it tells you how you can fix those pain-points. The lower the rule number, the greater the effect.
What follows is a comprehensive, yet pretty complete review of how Drupal 5 and 6 score on each rule, by listing the required features, settings or guidelines.
If you want to skip the information and want to see results, just skip to the part where I explain how you can apply the optimizations to your site.
Rule 1: Make fewer HTTP requests
Requirement | Drupal 5 | Drupal 6 |
---|---|---|
CSS aggregation | yes | yes |
JS aggregation | no | yes |
Generate CSS sprites automatically | no | no |
Drupal even has the ability to compress CSS files (through stripping comments and whitespace). JS aggregation has been added in Drupal 6. To my knowledge, not a single CMS/CMF ships with the ability to generate CSS sprites. Nor does a single one have a module or extension that allows them to do so. This could be a Drupal key performance feature, if it were supported.
Solution
The easiest way to reduce this significantly is to enable Drupalās CSS and
JS aggregation. You can find these settings at admin/settings/performance in your Drupal site.
If
youāre using Drupal 5, thereās a backport of Drupal 6ās JS aggregation
feature, you can find it in this
issue ā I sponsored this patch.
There is not yet an automatic CSS sprite generator module for Drupal. If
your site is styled pretty heavily, this would benefit you even more than CSS
and JS aggregation. I hope somebody ā or some Drupal company ā will take the
initiative.
In the mean time, thereās a free CSS Sprite Generator out
there, if you donāt mind doing it manually.
Rule 2: Use a CDN
Requirement | Drupal 5 | Drupal 6 |
---|---|---|
Alter URLs of served files dynamically | no | no |
Drupalās File API needs work: it should be trivial to alter file URLs dynamically, e.g. based on the file size or type of a file.
Solution
I chose to tackle this particular problem myself, because using a CDN greatly enhances the usability of your web site for visitors that live far away from your servers. And one of the projects Iām working on, is one with a very international audience.
The first part of whatās needed, is obviously to update Drupal core to
support file URL altering. I chose to create a new function,
file_url()
, through which all URLs for files should be
generated, including the URLs for additional CSS files in the
page.tpl.php
file (e.g. for a print.css
file). This
patch also provides a new hook: hook_file_server()
, through which
modules can provide new file servers. To configure the preferred file server,
a new āFile serversā setting has been added to the File system settings form.
If one server canāt serve a file, Drupal will try the second server, and so
on. It will always fall back to the web server Drupal is being served from if
all servers provided by modules failed.
Currently, Iāve only got a Drupal
5 patch (itās included
in the CDN integration module and attached at the
bottom of this article), because I want to get more feedback before I start
maintaining patches for 2 different versions of Drupal. As soon as the patch
ends up in its final form, I will provide a Drupal 6 patch, and of course push
for Drupal 7 inclusion. An issue at
Drupal.org has been created.
The second part ā integration with a CDN ā obviously requires an
implementation of hook_file_server()
. So the CDN integration module was
born. Itās written with flexibility in mind: it supports synchronization
plugins (currently ships with one: FTP), can create unique filenames or
directories (necessary if you donāt want to break relative paths), provides
the tools to check whether your filters are working well (per-page and
site-wide statistics) and the filters can be configured using parameters
similar to Drupalās file_scan_directory()
function.
An article that includes benchmarks of the effects of the CDN integration module is being worked on. The same article will include a complete installation tutorial as well.
Rule 3: Add an Expires header
Requirement | Drupal 5 | Drupal 6 |
---|---|---|
Donāt set the Expires header for web pages | yes | yes |
Set the Expires header for all other files | yes | yes |
Allow far future Expires headers: ability to alter URLs of served files dynamically | no | no |
By setting the Expires header for files, you tell the browser that itās ok
to cache them.
Drupal sets the āExpiresā header for all other files than
web pages to 2 weeks. This is sufficient for most uses. For maximum
performance, this should be set to a date in the far future (e.g. 10
years from access), but this requires unique filenames: each time the file is
updated, the filename should change as well this is why file URL altering is a
requirement. If not, your users could still be using the old files, since they
may be in their cache.
Solution
Changing the future date for the Expires headers is easy enough: simply
edit your .htaccess
file. Your Apache server must also have
mod_expires installed, this is available by default on most servers. However,
making filenames unique is an entirely other matter. The altering of file URLs
is already solved in the solution for rule 2.
So all you have to do now, is implementing a file server that supports this.
The aforementioned CDN integration module provides this feature, but if you
want to use it, you of course have to use a CDN.
Rule 4: GZIP components
Requirement | Drupal 5 | Drupal 6 |
---|---|---|
GZIP web pages | yes | yes |
GZIP CSS and JS files | no | no |
When Drupalās page caching is enabled, pages are written to the cache in GZIPped form! To learn more about how Drupal handles GZIPping, run this command from your Drupal root directory:
egrep ārn "gzip" .
Donāt forget the dot at the end!
However, Drupal does not yet allow
you to gzip CSS and JS files.
Solution
A Drupal core patch for this is being
worked on, but has unfortunately been inactive for quite some time.
If
you are using my CDN integration module, you donāt need to worry about this,
since CDNs GZIP files by default, if the client supports it.
Alternative solution
As an alternative, you could configure your Apache server to automatically
compress files.
An example for Apache 2.x: add the following lines to
your .htaccess
or httpd.conf
file:
AddOutputFilterByType DEFLATE text/css application/x-javascript
Rule 5: Put CSS at the top
Requirement | Drupal 5 | Drupal 6 |
---|---|---|
Abstraction to add CSS files to the web page | yes | yes |
Default location in the XHTML document is the tag | yes | yes |
Drupal has this abstraction: drupal_add_css()
.
Putting
stylesheets to the document HEAD makes pages load faster: it allows the page
to be rendered progressively.
Rule 6: Put JS at the bottom
Requirement | Drupal 5 | Drupal 6 |
---|---|---|
Abstraction to add JS files to the document | yes | yes |
Default location in the XHTML document is just before
| no | no |
Drupal has this abstraction as well: drupal_add_js()
.
JS
should be at the bottom, because browsers wait until everything in the
tag has loaded. As you probably know, JS files tend to be
pretty large these days, so loading them might take a while, thus postponing
the rendering of the page. If youād put the JS files at the bottom, then your
page can be rendered while the JS files are still loading! It also achieves a
greater download parallelization, thus cutting down your overall page loading
time.
This is also being discussed at groups.drupal.org.
Solution
Unfortunately, the default value for the $scope
parameter of
drupal_add_js()
is bad: 'header'
. If we simply make
'footer'
the default, weāre good. The number of contributed
modules that sets this to 'header'
explicitly, is very low, so it
shouldnāt be too much work to convert these. And Iāve yet to encounter the
first module that has issues with being at the bottom instead of the top.
A more complex part of the solution are Drupalās default JS files:
misc/jquery.js
and misc/drupal.js
. These can be put
in the footer without any issues whatsoever. But what if a contributed module
chooses to put its files in the header? Then they may not yet be loaded! For
maximum compatibility, we should add the default JS files to the header if at
least one module chooses to add its JS file to the header.
Iāve attached patches for both Drupal 5 and 6, but neither implement the more complex part I just explained. In my opinion, Drupal should enforce a strict policy: all JS files should be āfooter-compatibleā. Until somebody can point me to some JS that must be in the header to work properly, Iām unlikely to change my opinion about this proposed policy.
Alternative solution
The second method to fix this, doesnāt involve hacking Drupal core, but is
also more hassle since you have to repeat it for every theme youāre using.
Suppose youāre using the default Drupal core theme, Garland. Then open the
themes/garland/page.tpl.php
file in your favorite editor. Find
this line at the top of the file:
Cut it away from there, and put it just before this line at the bottom:
So your end result should look like this:
As you can see, it now comes just before the closing
tag. (Well, also before the
$closure
output, which is the output
generated by all hook_footer()
implementations.)
Rule 7: Avoid CSS expressions
Requirement | Drupal 5 | Drupal 6 |
---|---|---|
No default theme should implement it | yes | yes |
CSS expressions are not recommended, because theyāre evaluated many times:
when the page is rendered or resized, but also when the page is scrolled. Even
when the user moves the mouse over the page!
None of the Drupal core
themes uses CSS expressions. Just make sure you donāt use it in your own themes.
Rule 8: Make JS and CSS external
Requirement | Drupal 5 | Drupal 6 |
---|---|---|
Inline CSS and JS should be avoided or used sparingly | yes | yes |
If your web site is a common homepage for many users, you may want to use a custom strategy and read this. Otherwise, you can ignore this rule without worrying.
Rule 9: Reduce DNS lookups
Requirement | Drupal 5 | Drupal 6 |
---|---|---|
Use 2-4 hostnames by default: ability to alter URLs of served files dynamically | no | no |
To my knowledge, not a single hosting provider offers a static file server by default. So it makes a lot of sense that Drupal doesnāt do it by default. However, Drupal should support it out-of-the-box.
If you use a lot of so called widgets (those small blocks of content provided by Flickr, del.icio.us, MyBlogLog, and so on) on your site, youāll have some extra work to do.
Solution
The altering of file URLs is already solved in the solution for rule 2. So, once again, all you
have to do now, is implementing a file server that supports this.
If you
use my CDN integration module, then youāll be using 2 hostnames or more, but
this of course requires you have access to a CDN.
Alternatively, you can
use a static file server. Robert
Douglassā article on using Drupal with a static file server is a very
complete reference: from the pros and cons to the entire server setup.
See Yahooās
documentation for more details.
Widgets solution
If youāre using a lot of widgets and you want to continue using them, you
can. The solution is simple: cache as much as possible on your own site (or to
your CDN).
For example, if you use Google Analytics, make sure youāve
installed the Google
Analytics module, which has an option to cache the .js file locally (and
update it once each day, to make sure youāre serving the latest version).
Rule 10: Minify JavaScript
Requirement | Drupal 5 | Drupal 6 |
---|---|---|
JavaScript minification | no | no |
This was originally included in Drupal 6 core. However, it has been removed because it was problematic; it would result in boatloads of JS errors, and thus the JS would simply stop working. The technique used by Drupal 6 was Dean Edwardsā packer, also nicknamed packer.
Solution
Packer, isnāt just a minifier, itās also an obfuscator. A minifier only strips whitespace and comments, but an obfuscator also munges the code; it renames variable and functions as short as possible. For this, the packer uses a reduction algorithm (hence its name). However, this has serious consequences for the page loading time as well: it can easily take 200 ms to unpack the JS! Additionally, the effectiveness of GZIPping packed JS files is much lower.
More reliable alternatives are JSMIN (minifier/uglifier), Dojo Shrinksafe
(minifier/obfuscator) and YUI Compressor
(minifier). The last two are built on top of Rhino, Mozillaās Javascript engine,
written in Java. Therefor neither qualify for Drupal core inclusion. A JSMIN PHP implementation
exists, so I think this is the best choice.
Thereās an issue to add this to Drupal 7.
Rule 11: Avoid Redirects
Requirement | Drupal 5 | Drupal 6 |
---|---|---|
Avoid redirects by default | yes | yes |
Drupal could redirect users accessing the non-aliased URL
/node/11
to the aliased version /about
, but it does
not ā at least not by default.
The Global Redirect
module implements this feature in a sensible way. See the project page for
a complete explanation.
Rule 12: Remove Duplicate Scripts
Requirement | Drupal 5 | Drupal 6 |
---|---|---|
Abstraction to add JS files to the document | yes | yes |
Drupal has this abstraction, as mentioned in rule 6:
drupal_add_js()
. You then just use a static variable to prevent
adding the same file multiple times. For an example, see the jCarousel
module.
Rule 13: Configure ETags
This is the only rule that depends completely on the server setup. An ETag uniquely identifies a file, and is used to verify if the file in the browserās cache matches the file on the server.
The problem is that they are generated using attributes specific to the server (inodes) theyāre being served from. This implies that when, for example, youāre using multiple servers behind a load balancer, you may one time be accessing the files from server 1, another time the files from server 2. And since the ETags donāt match, youāll be downloading the file again!
Solution
If youāre using multiple servers, disable ETags. For Apache users: add this
line to your httpd.conf
:
FileETag none
IIS users have to follow a more complex procedure.
Alternative solution
Use a single server to serve files, or a CDN. See the solutions for rules two and nine for detailed information.
Rule 14: Make Ajax Cacheable
Requirement | Drupal 5 | Drupal 6 |
---|---|---|
Pluggable render formats | no | no |
Ability to set GZIP-ability per format (i.e. rule 4) | no | no |
These feature was being worked on for Drupal 6, but unfortunately wasnāt
ready in time.
The ability to set the GZIP-ability per output format
(i.e. rule 4) should be handled at in the same patch, or in a follow-up, since
it affects the performance of AJAX callbacks so much.
Other - but mostly
less important - rules that are implemented automatically: rules 9 and 13
(AJAX responses will be served from the same server as Drupal), rule 11
(redirects are extremely unlikely to be used for AJAX callbacks in Drupal).
Rule 10 is almost completely irrelevant, because GZIPping JSON data has a much
greater effect.
Solution
The node rendering refactoring issue is listed for the Drupalcon Boston 2008 code sprint, so weāll probably see this in its final form in a couple of months.
Applying this to your site
This is of course a boatload of information. The easiest way to apply all of it to your site, is by installing my CDN integration module, and using the included Drupal core patch that also adds JS aggregation and puts JS files at the bottom (by changing the default scopes).
Live sites
Before you start applying this to your site, you of course want some proof that all these optimizations do indeed have an effect. No problem. Youāre looking at it. This page should have been loaded in less than a second. Subsequent page loads should complete in less than half a second. With Drupalās page caching disabled (!), eAccelerator installed and a MySQL query cache in place.
Another live site is DrupalBin. That site is running on a shared server (DreamHost), without eAccelerator and without a MySQL query cache ā which explains the often slow page generation times.
Additional optimizations
In order of effectiveness:
- The Boost module enables Drupal to perform static page caching. This means that rendered pages are written to files, and through some mod_rewrite magic, it will serve the statically cached page from the file if itās available, thus without even a single DB query!
- This article at 2bits is chock full with Drupal performance tips.
- The core patches module Advanced cache, again by Robert Douglass, provides caching for blocks, comments, the forum structure, built nodes, path lookups, popular search queries and taxonomy trees.
Drupal issues
- #101227: GZIP aggregated CSS and JS, by Owen Barton (Grugnog2) and Ted Serbinski (m3avrck).Has some patches, but is out of sync.
- #210447: Introduce Javascript compression, by Nedjo Rogers (nedjo). Doesnāt have patches yet.
- #134478: Refactor node rendering, by Jeff Eaton (eaton) et al.
- #214934: file_url() and hook_file_server(), by Wim Leers.
More information
- High
Performance Web Sites (video of presentation), by Steve
Souders.
Explains all YSlow rules in detail. - CSS Sprites: Image
Slicingās Kiss of Death, by Dave Shea, A List Apart.
Explains the need for CSS sprites, how they work and the pittfalls. - CSS
Sprites: How Yahoo.com and AOL.com Improve Web Performance,
websiteoptimization.com.
Case-study that may be interesting if youāre learning about CSS sprites. - CSS
Sprite Generator Tool, by Stuart Colville, Muffin Research Labs.
Announcement of the aforementioned CSS sprite generator. - Announcing
SilkSprite: A CSS Sprite Plugin for Blueprint, by Don Albrecht, Ajax
Bestiary.com.
Automatic CSS sprite generator for the Blueprint CSS framework. - Performance
Research, Part 2: Browser Cache Usage - Exposed!, by Tenni Theurerās,
Yahoo! User Interface Blog.
Aptly describes why itās important to not forget about page views with an empty cache. - Serving
JavaScript Fast, Cal Henderson, Vitamin.
A pretty complete reference on how to serve GZIPped CSS and JS files. - Gzip Your
Minified JavaScript Files, Julien Lecomte.
A comparison of how well GZIP-able minified JS files are, after using a certain minifier. Also explains why the packer should not be used due to its reduction algorithm. - Static
Page Caching for Drupal 4.7, Arto Bendiken.
Announcement of the Boost module. - Page Load
Time Worksheet, Tag 1 Consulting, Inc.
This worksheet can help you to isolate and fix bottlenecks affecting your page load times.
Thanks to ā¦
- Yahoo! for their work on YSlow.
- Greg Knaddison (greggles) for proofreading this article and making several excellent suggestions to make this article more complete.
good job
This is certainly worth a space in my bookmarks.
Great article! Thank you
Great article! Thank you
Great article
Great article, and careful analysis. Iāve written about YSlow before (http://buytaert.net/yslow) ā and I have been recommending it to a lot of people. It should be part of the āDrupal development toolchainā that every developer uses.
The Javascript position issue is interesting ā we should enforce good behavior through our APIs and make the required changes. Why would we allow Javascript to be placed at the top of the page?
In some cases this is needed
AFAIK, loading JS at the bottom should be fine in most cases - jQuery usually works unobtrusively by interacting with the DOM once the document is ready. As long as the jquery.js file is loaded before any jQuery code, there should be no problem.
There are however some situations where youād want jQuery to do its magic before the document is completely loaded, such as dynamically adding classes to elements to change their appearance. If this has to wait for the bottom of the document to be reached, one would experience an annoying glitch as the code is run. An exampe of this is the plugin to add rounded corners to boxes.
So loading
jquery.js
anddrupal.js
in the footer by default, and in the header if a module has added some JS there, sounds reasonable. Themes may add JS directly intotpl.php
files, but this is bad practice anyway, one should usedrupal_add_js()
instead. JoakimNot quite correct - this has been tested
Please see: http://groups.drupal.org/node/8399#comment-25894
I was under the same impression - but decided to run tests. If you want to manipulate the DOM there is virtually no difference whether you place $(document).ready(function(){//DOM manipulation}) at the top or the bottom of the page. See the link for complete details.
If anyone finds a flaw in the testing method or has some additional insight please share in the thread on g.d.o http://groups.drupal.org/node/8399
andre
Correction
Yea, I corrected myself in that same thread. I was speaking from my own experience and what Iāve learned from others, but the problems I had may have been caused by something else.
Anyway, loading the required JS files from <head> if needed would allow the few scripts who depends on this to run as well. One would only have to specify āheaderā as the scope instead of the (as suggested) default āfooterā.
Joakim
you may want to call a
you may want to call a function such as swfobject inline, having the code at the bottom breaks this. I am aware that you can just call the function below the script declaration however this is not really possible if you will be posting into the content of a node.
Excellent question
Thatās an excellent question, but Iām afraid I canāt answer this yet either.
Iāve searched a lot, but failed to find any information regarding this rather critical topic. Not even a mention on A List Apart.
Iāve contacted John Resig about this, asking him to comment here.
Why would we allow
Why would we allow Javascript to be placed at the top of the page?
document.write()
.Wow
What can I say - this is the most complete article I have seen on this topic by far.
Oh if only this (and YSlow!) had existed when I was trying to convince everyone that we needed to aggregate CSS!
Next stop - gzipping for D7 :)
Very very useful
Thanks, very very useful specially the CDN module. good work man
Thank you
Thank you for that great work very informative , and helpfull
Good stuff-- one more resource
Very helpful stuff, Wim!
Hereās one more major resource I didnāt see listed:
Drupal Performance Agencyās checklists and worksheets, see āDocumentsā on that page: http://tag1consulting.com/drupal/performance
The docs cover some server and MySQL tuning as well as PHP and Agaric in a very systematic way. It was helpful at the right time for Agaric to keep World Social Forum 2008 online.
benjamin, Agaric Design CollectiveThanks!
Iāve never heard of that Drupal company, but they seem to have a great team and do great things.
Thanks for the link, Iāve added it to my more information section.
Great article!
I donāt think much more can be said on the topic, and the links are excellent. Great resource for all of us, both n00bs and those who know abit about āfrontendā performance :)
Thanks!
Joakim
rule 3 addendum for Drupal 6
Well, your observation in rule 3 for Drupal 6 is not entirely up to date. The CSS and JS file serving was changed in the RC phase to include a query string which changes on every update.php run and when all caches are flushed. So basically Drupal has a built-in way to ārenameā the URLs for the static CSS and JS files with short query string additions. (BTW this grown out of a bug fix for Garland to show up the new styles and behaviors with Drupal 5 ā 6 upgrades, even though the file names are the same).
Not quite what's necessary
Although it covers the basics of what is necessary, itās very ineffective, in that it forces a refresh of all files!
Please correct me if Iām wrong :)
(Relevant d.o issue: #199946.)
you are right
Yes, you are right. Since Drupal cannot afford to check for the dates of all files each time they are served, we donāt know about that, so all we can do is to refresh files on upgrades / updates. There is lot of stuff to do here in Drupal 7 and in contribs for Drupal 6.
Current approach & thoughts
My current approach is to keep an array with source file paths (i.e. the ones that are actually Drupalās web server) as keys and destination file paths (i.e. the ones synced to the CDN) as keys in the
variable
table. The destination file paths already contain the unique filenames (or unique directory, to not break relative paths for e.g. CSS files).So, currently, it doesnāt scale: if youāre working with thousands of files, the size of this array will grow into the megabytes, which is unacceptable. This will have to be reworked to work with DB queries. But then the same problem as for URL aliases arises: lots of queries per page, but files are typically only in the 5-20 range (with CSS/JS aggregation enabled of course), whereas URL aliases are very often in the >100 range. We can reduce the number of queries by still putting the most commonly used files in an array.
If thereās some info on best practices in this area available, let me know ;)
Content Synchronization
This is a fantastic article and I can see a ton of potential uses for the CDN module even above and beyond using it for CDNs. One of the parts of the module that I find most interesting is the content synchronization piece. I work in a situation where we have a dev/qa/production deployment process, and multiple load balanced production servers, so content synchronization is always rearing its head in various forms. Would you consider pulling this functionality out into its own module which is basically a black box for pushing files around? I think there are a lot of uses the community could find for such a module.
Partially done already by design
This is partially done already: the module supports synchronization plugins. Currently thereās only one plugin: FTP.
The part that scans for files that need to be synchronized (based on the filters configured by the user, so highly customizable) are all contained in the pretty small cdn_cron.inc file (320 lines and it contains loads of cron-specific stuff that could be ripped out).
Iāll gladly accept patches, but I wonāt start a separate module for this just yet ā plenty of work already with the modules Iām currently (co-)maintaining. If youād like to sponsor it though, you can contact me.
Pingback
[ā¦] Improving Drupalās page loading performance (tags: drupal web2.0) [ā¦]
Pingback
[ā¦] Improving Drupalās page loading performance (tags: drupal web2.0) [ā¦]
Outstanding
Thanks for the great article - this is the by far the most comprehensive guide for tackling all the issues that yslow has been pointing out on all our Drupal-powered sites.
Youāve done a real service to the Drupal community here.
-mike
Packer decompression delay
I updated the article with an explanation about the decompression delay due to the way Packer works.
Can't always move $scripts to end
Iāve got a situation where I canāt move $scripts to the end of the document. Iāve have a site frontpage that uses several jcarousels to scroll through images. Thereās also a few ad blocks on the page. The adblocks contain a script tag that calls the ad js. That js uses document.write to print code for either an image or flash. Unfortunately this isnāt exactly an instantaneous process and in the time it takes for this snippet of html to be generated and sent, the page has already begun to be rendered. If I were to use document.ready for the jcarousels, they wouldnāt be formated until the page is completed loading and would cause a flash of styled content (itās fairly significant if you have a lot of styling of this nature). So the solution is to put the jcarousel code imediately after the ul that it styles, and donāt use document.ready, just use the jquery function directly. So for this to work the jquery library needs to be loaded before this point, i.e. in the head.
I can see this becoming an increasingly common situation as we see ever more amounts of javascript in everyday webpages.
Agreed
I agree. Ideally, the changes performed by JS should not affect the display: when JS is enabled, the same āinitial viewā should be present, through CSS only. This is of course not realistic.
However, your method to prevent flashing (putting the .js files in the HTML head and calling the
jcarousel()
method immediately after the ul it styles) seems awkward: we donāt want inline JS. Isnāt putting the JS code in the head of the document itself enough to guarantee a flawless load?For readers interested in reproducing this issue: see my Mini panels demo, which has a jCarousel.
didn't work for me
Moving the jcarousel code to the header doesnāt work for me. This makes sense in my mind since the ul in question doesnāt yet exist at this point.
Please try this ā¦
Please try this:
$('ul#some-id').ready(function() { // jCarousel initalization });
(i.e. use a selector instead of
document
)ETag on load-balanced Apaches
An alternative to disabling FileETag when running multiple load-balanced Apaches is to let Apache consider only the file size and last modification time when generating the ETag. This is done using the following config line: FileETag MTime Size
This setting requires that files are copied to the parallel servers in a way that preserves the last modification time (and that files arenāt modified several times per second without changing size, but this probably isnāt a problem).
Thanks
Thanks for your addition!
I knew there were ways to preserve ETags, but I deliberately chose to not enter that topic, because that would lead me too far off the focus.
Excellent post
What an excellent post. I was just starting to post something similar but decided against it when reading yours as it covered the topic perfectly. Very useful and well laid out - thank you.
I think itās worth mentioning that Live Search (MSN, whatever) have confirmed that they do use ETags and there are ways of syncāing ETags in both Apache and IIS when used in a cluster.
Thereās no evidence (that I can find) that Google use ETags - they play it safe by using If-Modified-Since.
Oh, YSlow can (ironically) slow down your browser (at least it did on mine) so Iād disable it when not using it specifically.
dan
Indeed
Indeed there are ways to do that. Actually the comment that was posted just before your own describes such a method :)
And indeed, YSlow ā like just about any other Firefox add-on ā slows Firefox down. I choose to have YSlow and the Web Developer Toolbar enabled at all times, since I use it for web development only. I uninstall all other add-ons.
great article
What an excellent post. I was just starting to post something similar but decided against it when reading yours as it covered the topic perfectly. Very useful and well laid out - thank you.
Pingback
[ā¦] Improving Drupalās page loading performance | Wim Leers (tags: drupal performance optimization articles css howto yslow) [ā¦]
Thanks for this excellent
Thanks for this excellent post and analysis. Specially for āSolutionsā.
Yahoo! posted 20 additional optimization rules
Yahoo! posted 20 additional optimization rules on. Theyāll provide an in-depth explanation later, but itās interesting already. See http://developer.yahoo.net/blog/archives/2008/03/yahoos_latest_p.html.
I've been wondering how to
Iāve been wondering how to improve my YSlow score for awhile, since most of the things YSlow tells me arenāt modify-able for anyone who isnāt really capable of tampering with PHP and .htaccess files. This article should help. Thanks!
thank you ! great article
thank you ! great article ! really helpfull
nice
It really works great as well. Drupal is getting better and better. thanks
improvement for css and js aggregation
Great and useful article. The definitive guide!! :-)
for your info, recently a simple and smart module to compress css and js files has been published at drupal.org. The name is Smartcache and authorās nick is Gremlinc5.
I have tested it and works fine, and you donĀ“t have to tweak any core files, just add a .htaccess rule and a couple of files. Worths a look as maybe it has a place in your article.
Is Digg-France slow to load ?
Hi,
Please can you confirm that my website is very slow to load ? http://www.digg-france.com/
Itās a Drigg module based websites and it seems quite slow.
Thanks.
ps: maybe you should add the comment_subscribe module for you site ;-)
I want to cache images but default sets header to 1978
Iāve gone through your recommendations (canāt afford CDN) and my nagging problem is that drupal never seems to cache any of my images saved above root (private files structure). I have it set to cache images in my .htaccess file and I have mod_deflate, mod_expires and mod_headers enabled in apache.
YSlow shows says images on my front page do not have a far future Expires header: (11/19/1978) http://dev.domain.org/system/files/admin/pconnectAd.gif
I know the default for headers in bootstrap is 1978 for content but how do I change for my private image files?
Hereās my htaccess:
Enable expirations.# Requires mod_expires to be enabled. <IfModule mod_expires.c>
ExpiresActive On ExpiresDefault āaccess plus 2 yearsā
Do not cache dynamically generated pages.ExpiresByType text/html A1
Set up 10 year caching on commonly updated files<FilesMatch ā.(xml|js|css)$ā> ExpiresDefault āaccess plus 10 yearsā </FilesMatch>
Set up 10 year caching on commonly updated files<FilesMatch ā.(gif|jpg|jpeg|png)$ā> ExpiresDefault āaccess plus 10 yearsā </FilesMatch>
Force no caching for dynamic files<FilesMatch ā.(php)$ā> ExpiresActive Off </FilesMatch>
More information: http://drupal.org/node/272082
Any and all suggestions will be appreciated : )
thank you
Thank you for your tutorial. I had use YSlow and tweak common.inc into JS at bottom and CSS at top. While drupal run module CSS at top, theme CSS at bottom, I change it viceversa, since I dont use any important CSS with module.
About the run module/theme CSS, I edit the value on
if (!$preprocess && $type == 'theme')
, like these:// If a CSS file is not to be preprocessed and it's a module CSS file, it needs to always appear at the top, // regardless of whether preprocessing is on or off. if (!$preprocess && $type == 'theme') { $no_module_preprocess .= ''."\n"; } // If a CSS file is not to be preprocessed and it's a theme CSS file, it needs to always appear at the bottom, // regardless of whether preprocessing is on or off. else if (!$preprocess && $type == 'module') { $no_theme_preprocess .= ''."\n"; }
Now, what I should do is to minimize the usage of images of my CSS.. since it use like useless 11 css images.
Pingback
[ā¦] Improving Drupalās page loading performance [ā¦]
CSS Sprites modules
I never try it, but thereās a CSS sprites generator module for Drupal 5.x:
http://drupal.org/project/sprites
Both FCKEditor and Google
Both FCKEditor and Google Analytics may choke if the JS is placed at the bottom, in their current state. What about placing the $scripts tag just inside the body tag?? What potential issues might this cause? Would it be an improvement?
On-line site performance testing tool
I would also recommend this online free tool: http://site-perf.com.
It measure loading speed of page and itās requisites (images/js/css) like browsers do and shows nice detailed chart - so you can easily spot bottlenecks.
Also very useful thing is that this tool is able to verify network quality of your server (packet loss level and ping delays).
Improvements in 3 times
Thanks for your arcticle. It helped me improve my site performance in more then 3 times. The only problem for me now is improving internal Drupal performance such as page loading for authenticated users.
Pingback
[ā¦] Improving Drupalās Page Loading PerformanceLooking to speed up a Drupal website? Try following these steps. [ā¦]
will pay attention
great article, and we are going to start paying attention on this, especially the usage of YSlow. BTW, http://wimleers.com is not using CDN? It was listed as an example in CDN module?
You have to add
You have to add wimleers.cachefly.net as the CDN used on the web site, then itāll increase the score. The CDN module is not yet ready for production use though. Iām also having a server issue because of which new files havenāt synced yet at the moment of speaking, causing a couple of files not to be served from the CDN.
Very good article! Thanks
Very good article! Thanks
Pingback
[ā¦] Improving Drupalās Page Loading Performance Bįŗ”n Äang tƬm cĆ”ch tÄng tį»c cho cĆ”c trang web cį»§a mƬnh? ÄĆ¢y lĆ cĆ”ch giįŗ£i quyįŗæt dĆ nh cho bįŗ”n. [ā¦]
Smart Optimizer
Iāve found that a PHP library called Smart Optimizer works well with Drupal.
http://farhadi.ir/works/smartoptimizer
It automatically minifies, concatenates, and gzips CSS and JS and caches the resulting files to serve future requests. Super easy to setup as well - just copy the directory to your server and paste a snippet in you htaccess file. Makes a huge difference in Yslow.
Re putting .js files in footer
Great resource BTW!
I used your alternative suggestion and amended my theme to put the .js files at the bottom of the page. However it appears that this breaks FCKEditor, the editor simply does not show up! When I reverted to putting .js files in the header it worked again!
Iām using Drupal 6
Excellent Article
Man that was awesome. Going to bookmark it. Will refer to it often. Thanks!
mod_deflate
Mod_deflate in apache2 is pretty much the same as mod_gzip in apache1.3, and mod_deflate is included with the apache2 source package. Both modules allow compressing of the apache server on the fly
http://railsgeek.com/2008/12/16/apache2-httpd-improving-performance-modā¦
Pingback
[ā¦] # [ā¦]
Very good tutorial
An excellent tutorial!
I learned a lot from this. Thanks so much.
Proof in the pudding?
Great advice! And I like how you challenge us to try loading the page to see how your tips work on your own site. Unfortunately, when I run Yslow on this page, it get this:
Performance Grade: C (71) Expand All Collapse All D 1. Make fewer HTTP requests F 2. Use a CDN B 3. Add an Expires header D 4. Gzip components B 5. Put CSS at the top A 6. Put JS at the bottom A 7. Avoid CSS expressions n/a 8. Make JS and CSS external A 9. Reduce DNS lookups C 10. Minify JS A 11. Avoid redirects A 12. Remove duplicate scripts F 13. Configure ETags
Time to revisit your own advice? :-(
Temporary lack of pudding
I stopped using my CDN provider because it turned out they had very few PoPs and for that, I was no longer willing to pay. So itās simply because Iām no longer using a CDN. This is temporary. Iām in my exam period currently, so I donāt have the time to test with another CDN.
Iāll fix it ASAP :)
Pingback
[ā¦] # [ā¦]
Pingback
[ā¦] Improving Drupalās Page Loading Performance Bįŗ”n Äang tƬm cĆ”ch tÄng tį»c cho cĆ”c trang web cį»§a mƬnh? ÄĆ¢y lĆ cĆ”ch giįŗ£i quyįŗæt dĆ nh cho bįŗ”n. [ā¦]
JavaScript Aggregation
A small note that the JavaScript Aggregator module for Drupal 5 will aggregate the JavaScript for you. In Drupal 6, the JavaScript Aggregator module will use JSMin to compress/minify the aggregated JavaScript.
Yep, that's new since I
Yep, thatās new since I wrote this article :)
I will probably post an updated version of this article in a couple of weeks.
JS in the Footer (unable to apply database updates)
I Just wanted to update my drupal from D6.9 to D6.10. Befor I startet the required database update, I reapplied the patch for JS in the footer (manually, because common.inc have been changed).
If āfunction drupal_add_jsā had been changed to insert the JS in the footer, the database update canāt be applied, because the script, that shows the progressbar, wonāt run.
Solution: First update the database and than patch common.inc. Whenever you need to update the database (e.g. after module update), you must change common.inc to include JS in the header, and after that, you can reapply the patch.
Pingback
[ā¦] Improving Drupalās Page Loading Performance Looking to speed up a Drupal website? Try following these steps. [ā¦]
Rule 4: GZIP components
Been working with Rob Loach and his Javascript Aggregator now gzipās javascript. Based off of our work, I released CSS Gzip. No hacking of core required!
Great job!
I already noticed. Great work :) Iām sure itāll be appreciated by many Drupal users!
Pingback
[ā¦] Š½ŠøŠ¶Šµ ŃŠ°ŃŠæŠ¾Š»Š¾Š¶ŠµŠ½ ŠæŠµŃŠµŠ²Š¾Š“ Š·Š°Š¼ŠµŃŠŗŠø āImproving Drupalās page loading performanceā, Š² ŠŗŠ¾ŃŠ¾ŃŠ¾Š¹ ŃŠ°ŃŃŠ¼Š°ŃŃŠøŠ²Š°ŃŃŃŃ ŠæŃŠøŠŗŠ»Š°Š“Š½ŃŠµ Š¼ŠµŃŠ¾Š“Ń [ā¦]
Pingback
[ā¦] hį» thį»ng lĆ mį»t khĆa cįŗ”nh rįŗ„t quan trį»ng. HĆ£y tƬm hiį»u tį»« bĆ i viįŗæt nĆ y. Improving Drupalās Page Loading Performance Bįŗ”n Äang tƬm cĆ”ch tÄng tį»c cho cĆ”c trang web cį»§a mƬnh? ÄĆ¢y lĆ cĆ”ch giįŗ£i quyįŗæt [ā¦]
Help to improve loading performance ?
Hi,
We are still trying to improve our Drigg based website http://www.digg-france.com .
Does anybody here could help? Please tell us what could be done to our digg-like and how much work it would need and cost ?
Thanks a lot. http://www.digg-france.com
I need some improvements
Itās so slow for me, so hopefully this helps.
Sprite generator offset options?
for the css sprite generator, what do the offset options do, besides make the image bigger? Is there any reason the offset shouldnāt be as little as possible? (The sprite generator doesnāt accept an offset of zero, though I donāt understand what the problem would be.)
Rule 6: Javascript at bottom breaks site
I installed the Javascript Aggregator module and it works great. Page load times are significantly improved. But when I tried to implement Rule 6, I had a problem.
The site makes heavy use of the Gmap module, with most of the pages featuring a Google map. When I tried to move the
<?php print $scripts ?>
line to the bottom of the page, all of the maps disappeared, exposing the āJavascript is required ā¦ ā message. Returning the scripts to the <head> corrected the problem. (Drupal version 5.17)I just wanted to say that
I just wanted to say that this script has really helped improve the general performance (and YSlow score!) of my companyās Joomla site. I also wanted to say thanks. My Drupal 6.2 siteās YSlow score improved from 69 to 81. Keep up the good work. Cheers.
Pingback
[ā¦] Improving Drupalās Page Loading Performance Looking to speed up a Drupal website? Try following these steps. [ā¦]
Huge article, thanks !
Huge article, thanks !
Pingback
[ā¦] Improving Drupalās Page Loading Performance Tagged with: ęē« ē“¢å¼ęØ”ęæ [ā¦]
Info on the webpage
Thank You for the article. I am still reviewing it.
I have a minor issue request. Could you tell me how you were able to add the āTAGSā option at the end of the article block where there are ā apache Drupal Page loading performance performance ā tag links.
Simple: through theming, in
Simple: through theming, in
node.tpl.php
:)Great article
Really useful and definitely worth bookmarking. Now to get my head round how to implement some patches!
Thanks!
I applied some of the information written here to optimize my drupal site http://www.thecapulenos.com . The only thing I canāt apply is the CDN module integration, but it improves much website performance so Iām happy with it.
Pingback
[ā¦] Improving DrupalĆ¢ā¬ā¢s Page Loading Performance Looking to speed up a Drupal website? Try following these steps. [ā¦]
Issues with drupal_add_js at bottom
Hi Wim, After using it for several weeks, I have just got it! :-(
Here an example of what is impacted when one use footer as default scope from drupal_add_js: there is no more marker on Google Maps (through the gMap module).
I have noticed some other small cases like non-expandable fieldgroup on the update.php pageā¦
Do you know more about this point in D7? Did Angela ān friends have taken it into account?
Thanks, Alexandre
Nothing new in Drupal 7
Google Maps markers are indeed one of the many examples.
There are no new features regarding JS placement in the HTML document in Drupal 7.
Pingback
[ā¦] The CDN module is maintained by Wim Leers which also wrote a great but a bit older post on Drupal page loading performance. Also check out his slideshow and video presentation from DrupalCon Paris on this topic if you want [ā¦]
related post
Wrote a related blog post: http://blog.merge.nl/2009/11/08/speeding-up-drupal-performance
Instead of jsmin you can now also use the new google closure compiler. I wonder which one is better.
Pingback
[ā¦] Dit kan aardig oplopen in laadtijd besparing. Een zeer goed artikel hierover mbt Drupal staat op: Improving Drupalās page loading performance | Wim Leers Daarnaast heeft de Yahoo! Yslow tool zeer veel uitleg om je pagina nog sneller te laden! Zo [ā¦]
Pingback
[ā¦] Improving Drupalās page loading performance [ā¦]
Pingback
[ā¦] Š½ŠøŠ¶Šµ ŃŠ°ŃŠæŠ¾Š»Š¾Š¶ŠµŠ½ ŠæŠµŃŠµŠ²Š¾Š“ Š·Š°Š¼ŠµŃŠŗŠø āImproving Drupalās page loading performanceā, Š² ŃŠµŃŠµŠ½ŠøŠµ ŠŗŠ¾ŃŠ¾ŃŠ¾Š¹ ŃŠ°ŃŃŠ¼Š°ŃŃŠøŠ²Š°ŃŃŃŃ ŠæŃŠøŠŗŠ»Š°Š“Š½ŃŠµ Š¼ŠµŃŠ¾Š“Ń [ā¦]
Page load performance/CSS stylesheet help please
Dear Wim leers,
I hope you donāt mind me posting as you are a freelancer I was hoping you could help me out. I know this post is about loading performance (you have some spam on here as well) but I was wondering if there is a way to remove a stylesheet on one page? I have developed a forum by using phpbb2drupal and now my forum is totally messed up as it is calling style.css rather than the phpbb3 embedded style. I have tried creating a page-forum.tpl.php page and removed print styles, but it still does not work.
Also tried this solution on the drupal site:
<?php print str_replace(ā@import āmisc/drupal.cssā;ā, ā, $head); ?>So, question is how to remove CSS styles (default) to one page in drupal and keep the headers. Please please please help me :-)
Where's the spam then?
Whereās the spam then? :)
And here you can see how to remove a stylesheet in Drupal 6: http://snipplr.com/view/12182/remove-unwanted-stylesheets-from-drupal-6. Itās really not that hard to find ā itās the first hit on Google: http://www.google.com/search?q=drupal+remove+stylesheet.
Quick Thank you!
I Just wanted to thank you for posting the link to remove the style sheet. I think I was having a blond moment that day! Thanks again WinLeers :-)
No problem :)
No problem :)
Pingback
[ā¦] Drupal 6 les pages ne sont pas parfaitement au point(voir Improving Drupalās page loading performance) mais drupal 7 va ĆŖtre plus performant. var addthis_language = āfrā; Share| CatĆ©gorie(s) : [ā¦]
XHTML compression?
I read the entire article and it is definitely an important one in my bookmarks! Thanks Wim!
Although I quickly browsed through all the comments, maybe I missed another question about thisā¦
Why not also use an equivalent of Tidy on the generated XHTML? Removing all the whitespace has made my non-Drupal static sites loading faster when I have Tidy in my CMS-engine (I use an older, but great Dutch product for creating static sites). And with not removing line-breaks the source is still quite easy to check (or just do occasional checks in an XML validator with a pretty print option).
Which also brings me to: what would be the impact of whitespace-stripping PHP? Iām clueless about this, but I know from long ago that parsers are very often progress byte-by-byte on their low level processing. Would an optimization here do any good?
I also would love to hear a comment on the impact of using 2 dedicated servers, one for Drupal, one for MySQL (Gb connected). This is of course a very expensive approach, but would it make page loading also a lot snappier? Or is it just a final approach for really big trafic sites?
Thanks for any comments! Cheers, Peter
Yes, tidied HTML would
Yes, tidied HTML would indeed load faster. The impact is just significantly less than the loading of other resources, it can save at most a few KB of an HTTP request that would happen anyway. But it can help.
Whitespace-stripping PHP would also work but is of course definitely not recommended. Instead, use a PHP opcode cache such as APC.
The impact of using 2 dedicated servers would depend on your web site. Itās also completely not related to page loading performance: thatās all about page rendering performance, i.e. server-side performance. If you have slow SQL queries and by extent slow page loads, it may make sense to do that. But it really depends.
Gzip
Hi,
I was wondering why gzip was not working on my site. As stated here, cache must be enabled for gzip to work. thanks for the tip.
Aktuell???
Guten Tag, leider kann ich nicht so gut Englisch, daher meine Frage auf deutsch:
Ist der Artikel noch Aktuell?? oder gibt es da schon Ćnderungen/Verbesserungen wegen dem Gzip in Drupal5 / 6 ???
Good day, unfortunately I can not so good English, so my question to German:
If the item is still news? or there are already changes / improvements for the Gzip in drupal5 / 6??
The article is still
The article is still relevant for Drupal 5 and 6. I will try to do a big update though, because there are many more things to analyze by now :)
Javascript problem (not minifed)
Awsome articel. I encountered the following problem with javascripts in drual 6. My theme uses jQuery 1.2.6 and i am using the minified version. Okay, i also use the boost modul in drupal and gzip and so it is okay. The problem is now, that the final .js just includes the minified version of jquery but the non minifed versions of the other .js. But what i want is just one static minifed version of the whole javascript. (js is not dynamic data so it is not changing). it looks stil like that: I want a minifed version because at the moment it takes like 140kb. Here is the website: Javascript problem
var Drupal = Drupal || { āsettingsā: {}, ābehaviorsā: {}, āthemesā: {}, ālocaleā: {} };
/**
/**
...
Good!
Thx for the article, so far Iām getting grade B (84pts) with drupal6 +boost + a lot of sweat :)
Thanks
Danke fĆ¼r den guten Artikel.
Pingback
[ā¦] Here are some ideas for Drupal and Joomla:Drupal Speed Tips: Quick tips at Drupal.org WimLeers Detailed TutorialJoomla Speed Tips: Joomspot JoomlaPerformance ā great [ā¦]
It is better to use web
It is better to use web server compression instead of Drupal built-in, because it will offload PHP for another tasks.
Pingback
[ā¦] These subjects are extensive on their own, and will be only briefly covered in this article. For further and in-depth information visit: http://wimleers.com/article/improving-drupals-page-loading-performance. [ā¦]
Awesome
Our webmaster told us about all the changes to the Google algorithm and how page load time is a critical issue now and this post has been indispensable in helping to speed up our Drupal site.
Thanks.
Paul
Google and Drupal
In Webmasterworld there was recently a discussion about a new factor, which Google has incorporated in its ranking algorithm in recent months - speed of page loading. The reason why Google wants faster loading of pages is simple - faster the loading of page, the less people will wait for it and thus the more people will read and search for new pages. And this of course means more ads served by google. In this context yours article is really great. Regards, S.
Pingback
[ā¦] vermittelten Besucher habe. Die anderen Probleme bin ich dann jedoch anhand des englischen Artikels Improving Drupalās page loading performance [ā¦]
Very good article
Will refer to it often. Thanks!
Pingback
[ā¦] Issues aufgetaucht sind, welchen ich mich in kommender Zeit widmen muss. Dazu kommt das Post von Wim Leers gerade [ā¦]
Pingback
[ā¦] Here are some ideas for Drupal and Joomla: Drupal Speed Tips: Quick tips at Drupal.org WimLeers Detailed Tutorial Joomla Speed Tips: Joomspot JoomlaPerformance ā great [ā¦]
Excellent article and discussion thread!
Iāve really learned a lot from this article and comments, even if itās a bit old. Thank you thank you thank you..
Pingback
[ā¦] Improving Drupalās page loading performance ā Although this is for Drupal 5 & 6, the principles still apply to 7. (Maybe Wim will update his article?) [ā¦]
Pingback
[ā¦] Improving Drupalās page loading performance [ā¦]
Thanks
Thanks, very very useful specially the CDN module. good work man
Pingback
[ā¦] (PDF presentation of a website case handling 70 million pageviews per month)http://wimleers.com/article/imprā¦ (tips ofr improving performance)http://buytaert.net/tag/drupal-sā¦ (examples of Drupal [ā¦]