85

Besides installing W3 Total Cache or another caching plugin what steps can I take to make sure that my theme and site run as fast as possible.

1
  • if you run your site on vps , you should try redis cache.
    – ahmetlutfu
    Apr 5, 2014 at 22:58

15 Answers 15

34

You could install WordPress on Nginx. There are a number of resources to help:

Some performance information from that last link (which appears to be a bit different setup than the others):

So I decided to put a proxy in front of wordpress to static cache as much as possible. ALL non-authenticated traffic is served directly from the nginx file cache, taking some requests (such as RSS feed generation) from 6 pages/second to 7000+ pages/second. Oof. Nginx also handles logging and gzipping, leaving the heavier backend apaches to do what they do best: serve dynamic wordpress pages only when needed.

...

On nginx – it’s so efficient it’s scary. I’ve never seen it use more than 10 to 15 meg of RAM and a blip of CPU, even under our heaviest load. Our ganglia graphs don’t lie: we halved our memory requirements, doubled our outgoing network throughput and completely leveled out our load. We have had basically no problems since we set this up.

5
  • Anyone have any stats on the speed savings of using Nginx?
    – Mike Lee
    Aug 18, 2010 at 15:07
  • Mike, I added another link, and some information from that post. Aug 18, 2010 at 15:14
  • I moved my main blog from a 1G server running Apache to a 512M server runing Nginx. Runs more smoothly, despite the decrease in RAM. Admittedly, I have other services running on the 1G server, though (email, imap, mailman, several other low-traffic web sites). Jun 7, 2012 at 20:26
  • NB running WordPress on nginx is different from using nginx as a proxy cache in front of Wordpress.
    – sam
    May 2, 2013 at 21:08
  • Nginx is still the best, but there are several other parts of the "stack" you need to optimize as well: wordpress.stackexchange.com/a/16473/152624 Apr 7, 2021 at 18:33
26

Set client-side expiries for things like css, images, JavaScript etc which don't need to be redownloaded for each page view. This, by far, made the biggest difference to my site loading times. The fastest download is the download that never happened ...

# BEGIN Expire headers
<IfModule mod_expires.c>
  ExpiresActive On
  ExpiresDefault "access plus 7200 seconds"
  ExpiresByType image/x-icon "access plus 2592000 seconds"
  ExpiresByType image/jpeg "access plus 2592000 seconds"
  ExpiresByType image/png "access plus 2592000 seconds"
  ExpiresByType image/gif "access plus 2592000 seconds"
  ExpiresByType application/x-shockwave-flash "access plus 2592000 seconds"
  ExpiresByType text/css "access plus 2592000 seconds"
  ExpiresByType text/javascript "access plus 2592000 seconds"
  ExpiresByType application/x-javascript "access plus 2592000 seconds"
  ExpiresByType text/html "access plus 7200 seconds"
  ExpiresByType application/xhtml+xml "access plus 7200 seconds"
</IfModule>
# END Expire headers

# BEGIN Cache-Control Headers
<IfModule mod_headers.c>
  <FilesMatch "\\.(ico|jpe?g|png|gif|swf|gz)$">
    Header set Cache-Control "max-age=2592000, public"
  </FilesMatch>
  <FilesMatch "\\.(css)$">
    Header set Cache-Control "max-age=2592000, public"
  </FilesMatch>
  <FilesMatch "\\.(js)$">
    Header set Cache-Control "max-age=2592000, private"
  </FilesMatch>
<filesMatch "\\.(html|htm)$">
Header set Cache-Control "max-age=7200, public"
</filesMatch>
# Disable caching for scripts and other dynamic files
<FilesMatch "\.(pl|php|cgi|spl|scgi|fcgi)$">
Header unset Cache-Control
</FilesMatch>
</IfModule>
# END Cache-Control Headers

You may pre-gzip everything you reasonably can (7-zip is a good tool for this) & upload it in the same place as the file you just gzipped. Change .htaccess to serve the pre-gzipped files, as below. The caveat here is you need to remember to re-gzip them if/when you update things. This cuts out the CPU overhead, apart from parsing .htaccess.

RewriteEngine on
#Check to see if browser can accept gzip files. If so and we have it - serve it!
ReWriteCond %{HTTP:accept-encoding} gzip
RewriteCond %{HTTP_USER_AGENT} !Safari
#make sure there's no trailing .gz on the url
ReWriteCond %{REQUEST_FILENAME} !^.+\.gz$
#check to see if a .gz version of the file exists.
RewriteCond %{REQUEST_FILENAME}.gz -f
#All conditions met so add .gz to URL filename (invisibly)
RewriteRule ^(.+) $1.gz [QSA,L]

This is just a raw answer. There are a lot of variations on this theme. I blogged about this and added quite a few references to more in-depth articles at http://icanhazdot.net/2010/03/23/some-wordpress-stuff/. Read that and, more importantly, the references I point to - they are good resources.

Be aware that if you tinker often then users will need to refresh their cache.

A plugin I found very useful too is wp-minify. The thing to watch with this one is that you should exclude page-specific items (contact form, front page slider etc) so you're not re-downloading the whole set of css, JS etc for each page. It is a good way to minify, combine & compress your baseline CSS, JS etc. It cuts down on http requests a lot. Wp-minify plays well with supercache and also with expiry headers that I detailed above.

Use Yslow in Firebug (Firefox) or similar to monitor your http requests and what is and isn't compressed. Have a look at expiry headers in there too. You will soon see what you can improve.

7
  • 2
    In case someone plans to copy/paste your Rewrites, there's an instance of "ReWrite" that should be corrected.
    – Jeremy L
    Dec 12, 2010 at 23:13
  • 2
    which instance?
    – CAD bloke
    Dec 19, 2010 at 6:52
  • @Nerdling Could you please point out which instance needs fixing? I'd like to use the above code. Dec 28, 2012 at 15:38
  • It may be related to mod gzip being deprecated in later versions of Apache. I had to change mine to mod deflate recently.
    – CAD bloke
    Dec 28, 2012 at 21:36
  • 2
    For a good set of defaults for .htaccess the HTML5 Boilerplate project provides a template. github.com/h5bp/html5-boilerplate/blob/master/dist/.htaccess Nov 18, 2014 at 17:48
22

Minimize the number of plugins you run to only what you really need. Especially be aware of plugins that add javascript and CSS code on every page load, even when that code isn't being used on the page.

If you are creating your own theme from scratch, break your CSS down so that features that are only need for particular page templates or view types (single post, archives, category, etc) are only loaded when needed.

Configure W3TC to use a CDN (like Amazon CloudFront, or any of the others supported by W3TC).

See if the Minify options work for you (some plugins generate js/css that won't minify nicely, so be sure to test your site after activating the minify feature).

If you have full control of your MySQL server, make sure that you have the query_cache turned on. Use a MySQL tuning script to find other ways to optimize your database config.

If using a CDN is problematic for some reason, configure mod_expires in your apache setup. Set expiration times as long as reasonable for static types like images, css, javascript, video, audio, etc.

15

Run memcached and use an object cache to reduce the number of database queries. This caches data from the database, rather than pages. Not sure if w3-total-cache already does this.

Make sure you are running an opcode cache like APC. (There are several more available.)

2
  • 2
    APC really makes wordpress a lot more responsive, especially the admin pages. BUT, there are some potential configuration conflicts between WP-SuperCache and APC. These don't seem to affect W3 Cache.
    – WhIteSidE
    Aug 12, 2010 at 15:50
  • There's an excellent post from Mark Jaquith on this:APC Object Cache Backend for WordPress. You can use batcache happily with APC.
    – icc97
    Oct 21, 2014 at 14:26
8

In addition to using a disk caching plugin like wp-cache, put your blog on a host volume that has the "noatime" property set on it. Otherwise, SSH into your host (if your webhost provides that) and routinely run this command on your files every few days:

chattr -R +A ~/*

The ~/* means "my files under my home directory". You can change that path as you see fit. You can also set this up on a cron job in cpanel if your webhost provides that.

For more info about atime property, see this. It speeds up Linux disk read performance greatly.

Sometimes your site is being hammered by spiders. You can use a tool like SpyderSpanker or Chennai Central to filter out spiders who don't help bring more page rank to your site and merely slow it down, and then throttle good spiders (like Google, Bing, etc.) by sending them random HTTP 304 Not Modified messages.

Another thing I see is just poorly written plugins. If you learn how to make plugins, you begin to see how some plugins are inefficiently coded, or even find timebombs, such as a database table that fills and fills and never gets cleaned out, storing things such as incoming connection data.

Beyond all the other solutions here, you can also create a WordPress web farm of your blog by hosting it on several web node PCs that all connect back to one single database and one single disk volume for the files (such as a volume mounted over NFS). Check out Ultra Monkey for how to get that all going.

7

A few answers off the top of my head:

1) Minimize the number of HTTP requests the browser has to make to your host by concatenating JavaScript and CSS where possible/practical.

2) Offload as much of your image/media serving to 3rd party CDNs as possible, particularly if you're using shared hosting.

3) Try reducing the number of posts you're displaying on the front page in order to cut down on total render time.

3a) Try using a theme that presents a few featured posts in full on the front page and all other, older posts as excerpts.

1
  • 2
    +1 for reducing number of posts, this gives a tremendous boost with no cost. People don't really need to see ten old posts, I just set my conf to eight.
    – ripper234
    Apr 23, 2011 at 14:25
7

Caching the WordPress Menu also gives you a performance boost. Especially if you have a lot of Pages or a giant Menu Structure, this should be considered.

Do it in 2 easy steps. At first, create a function that gets or creates the menu, instead of calling wp_nav_menu directly.

function get_cached_menu( $menuargs ) {

    if ( !isset( $menuargs['menu'] ) ) {

        $theme_locations = get_nav_menu_locations();
        $nav_menu_selected_id = $theme_locations[$menuargs['theme_location']];
        $termslug = get_term_by( 'id', $nav_menu_selected_id, 'nav_menu' );
        $transient = 'menu_' . $termslug->slug . '_transient';

    } else {

        $transient = 'menu_' . $menuargs['menu'] . '_transient';

    }


    if ( !get_transient( $transient ) ) { // check if the menu is already cached

        $menuargs['echo'] = '0'; // set the output to return
        $this_menu = wp_nav_menu( $menuargs ); // build the menu with the given $menuargs
        echo $this_menu; // output the menu for this run
        set_transient( $transient, $this_menu ); // set the transient, where the build HTML is saved

    } else {

        echo get_transient( $transient ); // just output the cached version

    }

}

In your theme, replace the wp_nav_menus with get_cached_menu. Now, everytime the menu is called, you have one Databasequery instead of the whole Menubuilding.

Menus don't change often - but you also have to hook into the wp_update_nav_menu action to delete the old transients.

Do it like this:

add_action('wp_update_nav_menu', 'my_delete_menu_transients');

function my_delete_menu_transients($nav_menu_selected_id) {

    $termslug = get_term_by( 'id', $nav_menu_selected_id, 'nav_menu' );

    $transient = 'menu_' . $termslug->slug . '_transient';

    delete_transient( $transient ); 

}

The Menu will be generated the next time the page is called - and use the cached version until someone updates the menu again.

Updated Version

Thanks @helgatheviking for pointing out a mistake between slugs and IDs. I updated the functions so it works both with theme_position and menu (for a direct call of the menu).

The menus are always saved with the name of the Menu, not the position in the Theme.

1
  • This seems like a really cool idea. I'm having a problem with the code however. When we're clearing out the transient, the $nav_menu_selected_id is a number, while when calling the get_cached_menu() the menu_id is a string variable, because that parameter becomes the CSS ID for the <ul> element. Dec 28, 2012 at 15:40
5

Use a database class that is trimmed for optimization. We made good experiences with own code to reduce memory usage and database access speed. Next to that, you can optimize the database structure itself by some small changes that do a lot as well.

Part of the database class code can be found in the wordpress trac, it did not made it into core (Ticket #11799 and related).

1
4

For a highly trafficked site, you should tune all MySQL buffers for the content that is in place now. Regardless of the version of WordPress, the MySQL layer can have its configuration computed.

In fact, if you have InnoDB data without enabling innodb_file_per_table, you need to cleanup InnoDB by segmenting each table into its own physical tablespace. It is possible to do decent MySQL tuning even if you have a limited hardware. There are many scenarios for doing such InnoDB optimizations.

IMHO, you cannot plan good settings for my.cnf without knowing the amount of data to configure for. You would have to periodically load a current dataset from production into a staging environment, perform optimizations and come away with the numbers to configure in the my.cnf of the production server.

3

you could enable global output compression. this will gzip everything going out automatically if the browser supports it. This drastically reduces the size of files transferred, but does increase your CPU load.

2
  • This will tend to make your site "feel" much slower. The Yahoo! technical documents suggest flushing you code right after the end of head and before the beginning of body so that scripts and styles can start loading. By buffering the entire page, you prevent this from happening, and so the page "feels" slow because the user has to wait for WordPress to render the entire page before the user sees anything.
    – WhIteSidE
    Aug 12, 2010 at 15:53
  • Scott was not speaking about buffering the whole page but using apache output compression. That's something different, only if you use the PHP output compression via the output buffer this would have the deficiencies you describe vaguely. But not per-se anyway because in the end, buffering output can make things faster. This has something to do with I/O on your server.
    – hakre
    Aug 18, 2010 at 8:51
3

I recently spoke about this subject at WordCamp Houston. All of the above recommendations are great and the important thing is to make sure all the front end stuff is fully optimized then you can start working on the caching and server performance issues.

Progressive rendering will make your pages feel faster because the user will see the page content before it is fully loaded. To do this make sure any blocking js is at the very bottom of the page and css is at the top.

Also if you use a lot of social media buttons you can customize the scripts to make them load in an iframe after the page is fully loaded. I wrote a tutorial on how to do it with the TweetMeMe re tweet button (now obsolete since Twitter released their own retweet button) but can still be applied to other share buttons.

For server performance look into Nginx as a front end proxy for static content with Apache handling the heavy PHP and MySQL lifting.

2

Since nobody mentioned it yet, one of the most important steps to enhance server performance in conjunction with any LAMP setup would be to switch to apache worker thread and mod_fcgid.

This freed up 500MB of memory on my virtual private server.

3
  • I've tried this before, but I've never been able to get a stable apache worker + fcgi environment running. If anybody knows of some good setup instructions for this under Ubuntu, please post them. I'd especially be grateful for instructions that detail some of the Apache config directives that affect the FCGI behavior, and explain how tweaking them might affect memory usage, performance, etc. Currently, I'm using a forked apache with an nginx front-in proxy cache server. Feb 28, 2011 at 14:51
  • Define stable. My installation is running very stable, but you would need 2GB of RAM in my config. You just have to read and tweak. apache's documentation on fcgi is fairly extensive. Mar 9, 2011 at 10:50
  • 3
    try to check virtualmin.com its very stable and free Sep 11, 2011 at 5:41
1

Guide for checking plugin slow down

There's a beautifully simple plugin called Page Load Time, which adds timer to your page footer. Its actually only four lines of code:

<?php
function ur_pageload_footer() {
    printf(__('Page in %s seconds', 'pageload'), timer_stop());
}
add_action('wp_footer', 'ur_pageload_footer')

Then:

  1. Create a spreadsheet
  2. List out all your active plugins and put them in there
  3. Refresh the page three times noting the page load time each turn
  4. Go through your plugins one by one deactivating them
  5. Repeat step 3
  6. Note the order that you deactivated the plugins

Your spreadsheet should look something like

+-------+-------+-------+-------+--------+
| Run 1 | Run 2 | Run 3 | Order | Plugin |

So if after deactivating a plugin the page response time increases significanly then you can see if you can avoid that plugin.

I found two plugins that caused 'significant' slow down mqtranslate and (the rather old but good) Multi-level Navigation Plugin.

1
  • It would be really cool to automate this process is phantomjs and selenium(or something similar) so it runs automatically and spits out a little report at the end. Nov 18, 2014 at 17:44
0

Stick with the W3 Total Cache plugin for caching functionality in WordPress. Enable page caching and database caching from the plugin's settings page. Make sure you choose 'Alternative PHP Cache (APC / APCu)' as the caching mechanism. Do NOT enable any minification in W3 Total Cache as there are many chances for you to break your site's appearance and/or functionality. We'll leave it to Cloudflare.

Once you're done with configuring the rest of the plugin functionalities, set up Cloudflare for your website. Make sure you enable Cloudflare in the W3 Total Cache settings too under 'Extensions'.

Cloudflare is a Content Delivery Network which caches all the static contents (image files,CSS,JS,documents,etc.,) from your site and serves it to your visitors from their global servers. This can help speed up page load times and reduce the load on your server. For a list of file types that are cached by Cloudlfare checkout this list. Moreover, Cloudflare has a free plan.

In Cloudflare, set caching level to standard and set browser cache expiration to something at least greater than 20 hours. Enable Always Online™ so that even if your server goes down, Cloudflare will serve your website's static pages from their cache. Also enable their auto minify feature (remember why I asked you to not enable minification is W3 Total Cache? Because Cloudflare does it better!) Then set Rocket Loader™ to automatic.

Here's an excerpt of what Rocket Loader does :

  • Decreasing the number of network requests by bundling JavaScript files, even third party resources, to avoid slowing down page rendering.

  • Asynchronously loading scripts, including third party scripts, so
    that they do not block the content of your page from loading
    immediately.

  • Caching scripts locally (using LocalStorage, available on most
    browsers and smart phones) so they aren't re-fetched unless
    necessary.

More information can be found here.

If possible switch to the Genesis framework for WordPress because they are clean without any bloat. Genesis was built with speed and SEO in mind. I myself have tested it and my PageSpeed scores were good. Also if you're using Genesis, then don't forget to enable fragment cache in W3 Total Cache settings.

Since now you're using Cloudlfare as a CDN, you can make use of a plugin like 'Imagify' or 'Compress JPEG & PNG images' by TingPNG to compress your images. Both are free plugins available in the WordPress.org plugin repository. Also, Imagify supports the powerful lossy compression algorithm.

Finally, install 'Remove Query Strings From Static Resources' plugin from the WordPress repository so that it removes the query strings from static resources like CSS & JS files. This is because resources with a “?” or “&” in the URL are not cached by some proxy caching servers (remember, Cloudflare is also a proxy caching server).

Then install 'Use Google Libraries' plugin. This plugin allows your WordPress site to use Google's AJAX Library API CDN rather than serving these files from your WordPress install directly.

Some of the benefits are:

  • Increases the chance that a user already has these files cached.
  • Takes the extra load off your server.
  • Uses compressed versions of the libraries (when available).
  • Google's servers are set up to negotiate HTTP compression with the requesting browser.

Last but not the least, use the 'WP-Optimize' plugin by Ruhani Rabin to clean and optimize your database.

Hope this answers your question with regard to optimizing WordPress to reduce server load.

0

In regard to the entire server "stack" and beyond I'd refer you to this question (and my answer there) which discusses several items including DNS, SSL, datacenters, OS, and LEMP stacks. The essence of my answer there was using KVM cloud servers with Nginx, PHP-FPM, and Redis object caching.

But the nature of this thread is focused more on WordPress itself, along with related best practices, so I will mention a few things in a more narrow sense:

Firstly, way too much emphasis is put on cache "plugins" in the WordPress community. The reason for this is because shared Apache hosting still dominates the market, and for a variety of usability reasons most lowend web hosts avoid activating server-level caching by default (if at all).

In the years since this question, several "premium" web hosts have grown popular, some of which have server-level caching built-in to their hosting (these caches are usually built on Nginx, Redis, or Varnish, although a few cPanel-based hosts are now using Litespeed cache). Nginx FastCGI cache is arguably the most robust option when it comes to server-level caching, and also the least prone to conflicts. Certain cache plugins for WordPress do integrate with Nginx, but this is rather silly and just adds more bloat.

Secondly, a CDN is all but expected these days. Many "premium" hosts offer CDNs by default for serving your static assets from edge nodes around the world, or you can use a third-party service instead. The most popular option is CloudFlare, because it is a free CDN (and SSL proxy). Keep in mind that CDNs do not serve HTML content, which is why server-level caching is important.

Thirdly, and the most common cause of slow performance in my experience (besides hosting) is the problem of third-party scripts and assets -- in other words, items that your website is loading from other servers, such as tracking and advertising scripts, widgets and forms, videos and media, and many others. Even if these things load async/deferred (in the background) it can still cause major issues due the sheer size and amount of assets that are often being loaded. Sometimes, a plugin or theme is causing this, too: for example, I have seen WP themes loading 20+ Google font variations!

Fourthly, is the problem of general theme and plugin bloat. Not only do some of them embed third-party assets from remote servers, but some of them are horribly coded too and cause database thrashing, etc. Even if the code quality is decent, too many webmasters think they can use a very bloated theme along with a page builder plugin, and add-ons for the page builder, and a ton of other plugins, and then simply install a cache/speed plugin to minify or lazy-load everything... NOPE, that is not how it works. Having hundreds of DOM elements and dozens of CSS/JS files will always cause performance issues, no matter what. The fastest loading option will always be a custom-coded WordPress theme with hardcoded HTML, a single style.css file (loaded inline), and a minimum amount of plugins that are lightweight and well-coded... period.

Lastly, (and yes, common) is simply avoiding "stupid" mistakes. I can't count how many times I have seen a client website taking 30+ seconds to load only to discover their theme is trying to load several missing files that are causing 404 not found errors, or their homepage background "animation" is 50MB in size!

Remember, there is no "quick fix" cache plugin you can install that can take the place of common sense solutions and being reasonable in your design decisions.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.