I have a love/hate relationship with Joomla!. It’s an awesome tool, it just sucks.
My biggest beef with it, is that it’s slow. It renders content on the fly, for every page load. This is cool, if we used some of the more interactive features, but we don’t, so it’s just wasted overhead.
Joomla! has built in caching which would help… in theory. It appears that we use modules **cough** widgetkit **cough** that are not compatible with the Joomla caching. I keep turning caching on in the name of speed, and it keeps getting turned off because the site breaks. !@#$%^&* This sucks even more because Joomla! turns off browser caching for some dumb reason when you turn off server caching. Server side and client side caching are coupled? #EPICFAIL
So, this means I need to look elsewhere to get more performance out of Joomla!…
It should be noted that I have Apache & MySQL configured and tuned fairly well as well as have APC opcode caching turned on with 512mb of memory which is about used up (I’d just bumped it to 740mb while writing this post).
Browser Caching
Heh, silly me. One of my Joomla! homepages is 70 files long. No joke. Some of it is Google analytics, some social bookmarking widgets, etc. Joomla! thinks that the world changes by the moment, so it doesn’t provide browser level caching for ANY of these files.
I manged to get the subsequent page loads down to 10 files for the homepage by turning on some browser caching by .htaccess.
AddType font/ttf .ttf
AddType font/otf .otf
AddType application/x-woff .woff
AddType image/svg+xml .svg
AddType application/vnd.ms-fontobject .eotAddType font/ttf .ttf
AddType font/otf .otf
AddType application/x-woff .woff
AddType image/svg+xml .svg
AddType application/vnd.ms-fontobject .eot
<IfModule mod_expires.c>
ExpiresActive On
ExpiresDefault "access plus 1 year"
ExpiresByType text/html "access plus 600 seconds"
ExpiresByType image/gif "access plus 2 months"
ExpiresByType image/jpeg "access plus 2 months"
ExpiresByType image/png "access plus 2 months"
ExpiresByType text/css "access plus 2 months"
ExpiresByType text/javascript "access plus 2 months"
ExpiresByType application/x-javascript "access plus 2 months"
ExpiresByType image/ico "access plus 2 months"
ExpiresByType font/ttf "access plus 1 years"
ExpiresByType font/otf "access plus 1 years"
ExpiresByType application/vnd.ms-fontobject "access plus 1 years"
ExpiresByType application/x-woff "access plus 1 years"q
ExpiresByType image/svg+xml "access plus 1 years"
IfModule>
<IfModule mod_headers.c>
<FilesMatch "\.(ico|pdf|flv|jpg|jpeg|png|gif|js|css|swf|eot|woff|otf|ttf|svg)$">
Header set Cache-Control "max-age=290304000, public"
</FilesMatch>
IfModule>
AddOutputFilterByType DEFLATE application/vnd.ms-fontobject
AddOutputFilterByType DEFLATE font/ttf
AddOutputFilterByType DEFLATE font/otf
AddOutputFilterByType DEFLATE image/svg+xml
I’d based a lot of these .htaccess suggestions from here.
Apache mod_cache Caching
Another big thing I did was to have the Apache webserver cache my uncacheable-by-Joomla! pages.
Apache has the ability to cache content at the apache level – before PHP is even fired up. This feature really tickles my fancy.
So, I put some configs in my vhost.conf file, told PLESK to reload my configs, did a graceful restart of apache, and started testing…
Then I created the cache directory, chown-ed it over to apache, and chmod-ed it to 700 before the caching worked. I think. Maybe? The apache mod_cache module is quite quiet about what it’s doing.
There are a few juicy bits:
CacheIgnoreNoLastMod On – This tells the apache level caching to ignore the fact that Joomla! doesn’t want anything to cache it’s pages. We now have a server side cache!!!
CacheIgnoreHeaders Set-Cookie – This is very important. Without it, you risk session hijacking at your Apache level. **scary**
CacheIgnoreQueryString On – This works well if we have SEO URL pages, it ignores everything after the ? in the URL.
<IfModule mod_cache.c>
<IfModule mod_disk_cache.c>
CacheDefaultExpire 3600
CacheEnable disk /
CacheDisable /administrator
CacheRoot "/var/cache/httpd/"
CacheDirLevels 2
CacheDirLength 1
CacheMaxFileSize 1000000
CacheMinFileSize 1
CacheIgnoreCacheControl Off # Allows browser refresh of the cache
CacheIgnoreNoLastMod On # Ignores Joomlas caching instructions
CacheIgnoreQueryString Off
CacheLastModifiedFactor 0.1
CacheMaxExpire 86400
CacheStoreNoStore On
CacheStorePrivate Off
CacheIgnoreHeaders Set-Cookie
</IfModule>
</IfModule>
[Update 12/6] But Alas, deep URLs don’t seem to work. You only seem to get the content from the first loaded page of a submenu. The Cache is returning the wrong page! I looked high and low, and couldn’t find the issue in the context of Joomla! but it appears that Wordpess can have the same issue, so I just used that fix. It appears that mod_rewrite operates before mod_cache so a rewritten URL is what is getting pulled out of the cache. If the re_write rule just clobbers anything unknown to index.php, the cache will return the same results for multiple pages. A quick tweak of the .htaccess file cleared up the issue for me.
Change:
# internally rewrite the request to the index.php script
RewriteRule .* index.php/ [L]
to:
# internally rewrite the request to the index.php script
RewriteRule ^(.*)$ index.php/$1 [L]
Some references I used to create the above config can be found here, here, and here.
By turning the local browser cache off in Firefox using the webdeveloper tools and going to a deep content page that isn’t visited often – ensuring that I am the one who is generating the page for the first time, I can verify that I can slice over a second off of the load time for the base HTML file. I was going from an average of 1.6 seconds to generate a page in Joomla! to often under .4 seconds to serve that cached page.
[Update 12/6] I think it should also be pointed out that the mod_cache cache will grow forever unless htcacheclean is run periodically to clean it up.