13

What caching plugin configuration do you recommend and why under the following assumptions:

  • full control of the server configuration
  • running WordPress in multi-site/multi-domain mode
  • most domains are not using www. prefix (cookies)
  • (desire) to be able to disable caching for specific IPs or based on a cookie, when you do changes to the site you don't need caching.

Details: I'm using Firefox Google Page Speed plugin to try to optimize the speed of the website.

Also please do not guide with generic guidelines, like smaller images.

Let's be fair, using more than one caching plugin will bring you more problems than it will solve so please try to give a simple approach.

4 Answers 4

3

Basic answer to "what plugin" would probably be W3 Total Cache. It is one of the most functional and actively developed plugins at moment. However complete performance chain is much longer that WordPress plugin alone can handle.

  1. Web server (Apache or something else) configuration (response time, time to first byte, headers).
  2. Database (time spent processing queries).
  3. PHP/WordPress (page generation time, memory consumption).
  4. Front-end performance (amount of HTTP requests, bandwidth).

Good start would be static caching plugin (like W3) with opcode memory-based cache like APC.

But from there there are way more (and way more complex) things you could do, like content distribution networks, alternate web servers, etc.

1
20

My WordPress Performance and Caching Stack

This is the best WordPress performance stack for a low to mid range single server or VPS. I am classifying mid range as single core with around 1G of memory and fairly fast drives.

Server Stack

  • Linux - Either Debian Lenny or Ubuntu
  • Nginx - Configured as reverse proxy static file cache
  • Apache - Apache will handle the PHP offloaded by Nginx on an alternate port
  • MySql - Required by WP, make sure your running the latest stable version
  • PHP - Latest stable version of 5.2 or 5.3 branch

PHP Cache

  • APC - Configure it with mmap memory and shm size of at least 128M

WordPress Performance Plugin Stack

  • Nginx proxy cache integrator
  • W3 Total Cache - Set page cache to disk enhanced, minify to disk, and object and db to APC.
    • Self Hosted CDN - Create 4 cname aliases that point to domain on the server set up just to serve static file

With W3 Total Cache we are using disk for page cache and minify because Nginx will be serving our static files very fast.

How to configure Nginx to serve static files and pass PHP to Apache

The problem with using Apache alone is that it opens up a connection and hits php on every request even for static files. This wastes connections because Apache will keep them open and when you have lots of traffic your connections will be bogged down even if they are not being used.

By default Apache listens for requests on port 80 which is the default web port. First we are going to make changes to our Apache conf and virtual hosts files to listen on port 8080.

Apache Config

httpd.conf

set KeepAlive to off

ports.conf

NameVirtualHost *:8080
Listen 8080

Per Site Virtual Host

<VirtualHost 127.0.0.1:8080>
     ServerAdmin [email protected]
     ServerName yoursite.com
     ServerAlias www.yoursite.com
     DocumentRoot /srv/www/yoursite.com/public_html/
     ErrorLog /srv/www/yoursite.com/logs/error.log
     CustomLog /srv/www/yoursite.com/logs/access.log combined
</VirtualHost>

You should also install mod_rpaf so your logs will contain the real ip addresses of your visitors. If not your logs will have 127.0.0.1 as the originating ip address.

Nginx Config

On Debian you can use the repositories to install but they only contain version 0.6.33. To install a later version you have to add the lenny backports packages

$ nano /etc/apt/sources.list

Add this line to the file deb http://www.backports.org/debian lenny-backports main

$ nano /etc/apt/preferences

Add the following to the file:

Package: nginx
Pin: release a=lenny-backports 
Pin-Priority: 999

Issue the following commands to import the key from backports.org to verify packages and update your system's package database:

$ wget -O - http://backports.org/debian/archive.key | apt-key add -
$ apt-get update

Now install with apt-get

apt-get install nginx

This is much easier than compiling from source.

Nginx conf and server files config

nginx.conf

user www-data;
worker_processes  4;

error_log  /var/log/nginx/error.log;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    access_log  /var/log/nginx/access.log;
    client_body_temp_path /var/lib/nginx/body 1 2;
    gzip_buffers 32 8k;
    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;
    tcp_nodelay        on;

    gzip  on;

  gzip_comp_level   6;
  gzip_http_version 1.0;
  gzip_min_length   0;
  gzip_types        text/html text/css image/x-icon
        application/x-javascript application/javascript text/javascript application/atom+xml application/xml ;



    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

Now you will need to set up your Nginx virtual hosting. I like to use the sites-enabled method with each v host sym linked to a file in the sites-available directory.

$ mkdir /etc/nginx/sites-available  
$ mkdir /etc/nginx/sites-enabled
$ touch /etc/nginx/sites-available/yourservername.conf
$ touch /etc/nginx/sites-available/default.conf
$ ln -s  /etc/nginx/sites-available /etc/nginx/sites-enabled
$ nano /etc/nginx/sites-enabled/default.conf

default.conf

Note:

The static cache settings in the following files will only work if the Nginx proxy cache integrator plugin is enabled.

proxy_cache_path  /var/lib/nginx/cache  levels=1:2   keys_zone=staticfilecache:180m  max_size=500m;
proxy_temp_path /var/lib/nginx/proxy;
proxy_connect_timeout 30;
proxy_read_timeout 120;
proxy_send_timeout 120;

#IMPORTANT - this sets the basic cache key that's used in the static file cache.
proxy_cache_key "$scheme://$host$request_uri";

upstream wordpressapache {
        #The upstream apache server. You can have many of these and weight them accordingly,
        #allowing nginx to function as a caching load balancer 
        server 127.0.0.1:8080 weight=1 fail_timeout=120s;
}

Per WordPress site conf (For multi site you will only need one vhost)

server {
        #Only cache 200 responses, and for a default of 20 minutes.
        proxy_cache_valid 200 20m;

        #Listen to your public IP
        listen 80;

        #Probably not needed, as the proxy will pass back the host in "proxy_set_header"
        server_name www.yoursite.com yoursite.com;
        access_log /var/log/nginx/yoursite.proxied.log;  

        # "combined" matches apache's concept of "combined". Neat.
        access_log  /var/log/apache2/nginx-access.log combined;
        # Set the real IP.
        proxy_set_header X-Real-IP  $remote_addr;

        # Set the hostname
        proxy_set_header Host $host;

        #Set the forwarded-for header.
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        location / {
                        # If logged in, don't cache.
                        if ($http_cookie ~* "comment_author_|wordpress_(?!test_cookie)|wp-postpass_" ) {
                                set $do_not_cache 1;
                        }
                        proxy_cache_key "$scheme://$host$request_uri $do_not_cache";
                        proxy_cache staticfilecache;
                        proxy_pass http://wordpressapache;
        }

        location ~* wp\-.*\.php|wp\-admin {
                        # Don't static file cache admin-looking things.
                        proxy_pass http://wordpressapache;
        }

        location ~* \.(jpg|png|gif|jpeg|css|js|mp3|wav|swf|mov|doc|pdf|xls|ppt|docx|pptx|xlsx)$ {
                        # Cache static-looking files for 120 minutes, setting a 10 day expiry time in the HTTP header,
                        # whether logged in or not (may be too heavy-handed).
                        proxy_cache_valid 200 120m;
                        expires 864000;
                        proxy_pass http://wordpressapache;
                        proxy_cache staticfilecache;
        }

        location ~* \/[^\/]+\/(feed|\.xml)\/? {
 # Cache RSS looking feeds for 45 minutes unless logged in.
                        if ($http_cookie ~* "comment_author_|wordpress_(?!test_cookie)|wp-postpass_" ) {
                                set $do_not_cache 1;
                        }
                        proxy_cache_key "$scheme://$host$request_uri $do_not_cache";
                        proxy_cache_valid 200 45m;
                        proxy_cache staticfilecache;
                        proxy_pass http://wordpressapache;
        }

        location = /50x.html {
                root   /var/www/nginx-default;
        }

        # No access to .htaccess files.
        location ~ /\.ht {
                deny  all;
        }

        }

Self Hosted CDN conf

For your self hosted CDN conf you only need to set it up to serve static files without the proxy pass

server {

        proxy_cache_valid 200 20m;
        listen 80;
        server_name yourcdndomain.com;
        access_log   /srv/www/yourcdndomain.com/logs/access.log;
        root   /srv/www/yourcdndomain.com/public_html/;

 proxy_set_header X-Real-IP  $remote_addr;

      location ~* \.(jpg|png|gif|jpeg|css|js|mp3|wav|swf|mov|doc|pdf|xls|ppt|docx|pptx|xlsx)$ {
                                # Cache static-looking files for 120 minutes, setting a 10 day expiry time in the HTTP header,
                                # whether logged in or not (may be too heavy-handed).

                                proxy_cache_valid 200 120m;
                        expires 7776000;
                        proxy_cache staticfilecache;
                }

location = /50x.html {
                root   /var/www/nginx-default;
        }

 # No access to .htaccess files.
        location ~ /\.ht {
          deny  all;
        }

    }

Now start the servers

$ /etc/init.d/apache2 restart  
$/etc/init.d/nginx start

The Benchmark Results

On Apache Bench this setup can theoretically serve 1833.56 requests per second

$ ab -n 1000 -c 20 http://yoursite.com/

alt text

4
  • 1
    + Wow, this was impressive but for the moment I will prefer to skip the nginx setup because it would make the server setup too complex.
    – sorin
    Commented Oct 3, 2010 at 9:55
  • I want to follow your instructions on my VPS hosting.but I am afraid if any problem could occur ,if I do anything wrong.I have not done it earlier .Can a user like me able to do it?
    – user391
    Commented Dec 20, 2010 at 0:50
  • 1
    Thanks. Seriously useful. I am going to try this. I wish I could mod this up more than once :)
    – Nasir
    Commented Apr 22, 2012 at 12:47
  • Just a note, Apache could be removed from this stack, as nginx can serve PHP/WordPress.
    – Sam Rueby
    Commented Dec 10 at 20:53
4

Use a webspace with minimum 64MB Ram for Multisite and use APC and Memcached on the Apache, cache not static and you can use all WP-functions without problems. You scan via PageSpeed read also other options, there was coded in the theme. A cache can do a great work, but can not repair a bad theme or plugin. A another solution is to use subdomains without cookies as CDN in WordPress. Add this to the wp-config.php for the Cookies only on the domain, not the subdomain.

define( 'COOKIE_DOMAIN', 'example.com' );

Now set new functions in the functions.php of the theme or write a plugin to replace the path form static content to your subdomains, your custom CDN.

// replace for CDN on bloginfo
if ( !function_exists('fb_add_static_wpurl') ) {
    function fb_add_static_wpurl($info, $show) {

        if ( is_admin() )
            return $info;

        $keys = array(
            'url',
            'wpurl',
            'stylesheet_url',
            'stylesheet_directory',
            'template_url',
            'template_directory',
            );

        if ( in_array( $show, $keys ) ) {

            $wpurl = get_bloginfo('wpurl');

            $search = array(
                $wpurl . '/wp-content/images/',
                $wpurl . '/wp-content/download/',
                $wpurl . '/wp-content/themes/',
                $wpurl . '/wp-content/plugins/',
            );

            $replace = array(
                'http://cdn1.example.com/',
                'http://cdn2.example.com/',
                'http://cdn3.example.com/',
                'http://cdn4.example.com/',
            );

            return str_replace( $search, $replace, $info );

        } else {
            return $info;
        }
    }
    add_filter( 'bloginfo_url', 'fb_add_static_wpurl', 9999, 2 );
}

now the function for template and stylesheet-path

function fb_add_static_stylesheet_uri($uri) {

            if ( is_admin() )
                return $uri;

            $wpurl = get_bloginfo('wpurl');

            $search = array(
                $wpurl . '/wp-content/images/',
                $wpurl . '/wp-content/download/',
                $wpurl . '/wp-content/themes/',
                $wpurl . '/wp-content/plugins/',
            );

            $replace = array(
                'http://cdn1.example.com/',
                'http://cdn2.example.com/',
                'http://cdn3.example.com/',
                'http://cdn4.example.com/',
            );
            return str_replace( $search, $replace, $uri );

}
add_filter ( 'template_directory_uri', 'fb_add_static_stylesheet_uri' );
add_filter ( 'stylesheet_uri', 'fb_add_static_stylesheet_uri' );
add_filter ( 'stylesheet_directory_uri', 'fb_add_static_stylesheet_uri' );

Now read Page Speed on the frontend static CDN URLs without cookies.

Also add the follow source to the .htaccess for block dublicate content:

##
# Explicitly send a 404 header if a file on cdn[0-9].example.org is not
# found. This will prevent the start page (empty URL) from being loaded.
##
RewriteCond %{HTTP_HOST} ^cdn[0-9]\.example\.org [NC]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule .* - [R=404,L]

##
# Do not dispatch dynamic resources via cdn[0-9].example.org.
##
RewriteCond %{HTTP_HOST} ^cdn[0-9]\.example\.org [NC]
RewriteCond %{REQUEST_FILENAME} \.(php|html)$
RewriteRule .* - [R=404,L]

Please use the function, is also examples and you can write your solutions with my ideas.

0

Webserver stack

Essentially keep every operation in memory!

  • Modern CPU, high memory bandwidth as WordPress is mainly memory copying, sub-msec disk access time, try UpCloud!
  • Thin virtualization layer, try UpCloud! Keep away from popular, non-enterprise providers
  • Fast operating system: No systemd, Enough entropy, IRQ balance, Low memory usage
  • Block hammering attackers: Fail2ban, permanently block shadow nets
  • Anycast DNS
  • Quick webserver: Apache Event MPM
  • Parallel connection CDN with RAM-like speeds (Amazon CloudFront)
  • High speed SSL: ECDSA certificate, Entropy source, TLS1.2, Ciphersuites for AES-NI, SSL session cache, OCSP stapling, HTTP/2
  • Modern PHP with OPcache, connected through FastCGI
  • Lean WordPress installation: minimal and audited plugins only
  • Redis in-memory object cache
  • TokuDB (fractal tree) MariaDB engine
  • Resource optimization
  • Cut on JavaScripts
  • Continuous monitoring: Pingdom, HetrixTools

All my knowledge is open-source https://github.com/szepeviktor/debian-server-tools

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.