Contact Menu

WordPress with W3 Total Cache on Nginx with APC (Virtualmin)

Virtualmin now includes support for Nginx web server. I deploy many WordPress sites with W3 Total Cache and APC Alternative PHP Cache, so I was very interested to see how Nginx performance compares to Apache for WordPress, and whether APC and W3TC would play nice with Nginx.

I prefer to install WordPress in public_html/wordpress for ease of development, compatibility with other scripts, etc. Accordingly, the suggested configuration is for WordPress installed in a subfolder. This info also assumes you have successfully installed APC Cache on your server, and set up a clean install of Virtualmin with Nginx as the Alternative Web Server. Switching from Apache to Nginx later is difficult, as is transferring apache virtual server backups. Best to start fresh and build from scratch.

Testing the Virtualmin Nginx Modules

Virtualmin does not support Nginx and Apache together, and there is no easy migration from Apache to Nginx, so it is suggested to begin with a fresh install of Virtualmin with no existing accounts.

If you would like to experiment with Nginx, and you already have a Virtualmin Pro license, you are permitted to set up Virtualmin on a second server for testing and migration. I run the extra Virtualmin Pro system on a VPS that I use for backups, slave DNS, and testing.

The Nginx modules are also available for the free Virtualmin GPL version. To install the modules, go to Virtualmin Package Updates (wbm-virtualmin-nginx and wbm-virtualmin-nginx-ssl).

WordPress Plugins for this Setup

  • W3 Total Cache — adds opcode cache, minification, browser cache and page cache capabilities to WordPress… and it works with Nginx. Works best with Alternative PHP Cache to provide opcode and database cache. Setting APC for all caches works easiest with Nginx… setting to file caching introduces URL rewrite errors… See W3TC Settings information below for instructions on how to implement File caching.
  • nginx Compatibility — makes WordPress more compatible with Nginx, allows use of permalinks without /index.php/

Configuring Nginx for WordPress pretty URL Permalinks

The nginx Compatibility plugin has support for pretty URLs, but you must configure Nginx to use them. You should add or edit the following URL path locations to nginx.conf for your virtual domain… either by direct edit, or via URL Path Locations in Virtualmin > Services > Configure Nginx Website (see attached image…). This config is for WP installed in /public_html/wordpress, so you will need to omit or change /wordpress if WP is installed in a different directory.

location ^~ /files/ {
rewrite /files/(.+) /wordpress/wp-includes/ms-files.php?file=$1 last;
location @wordpress {
fastcgi_pass localhost:9000;
fastcgi_param SCRIPT_FILENAME $document_root/index.php;
include /etc/nginx/fastcgi_params;
fastcgi_param SCRIPT_NAME /index.php;
location ~ \.php$ {
try_files $uri @wordpress;
fastcgi_index index.php;
fastcgi_pass localhost:9000;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include /etc/nginx/fastcgi_params;
location ^~ /blogs.dir/ {
root /home/username/public_html/wordpress/wp-content;

Adding APC Support for a Virtual Domain

Because Virtualmin’s implementation of Nginx uses php-fastcgi, each virtual domain will load its own php.ini file, so you can add the APC directives to that file (/home/domainname/etc/php5/php.ini)

Add the following to php.ini (adjust to your requirements)

extension =
apc.enabled = 1
apc.shm_segments = 1
apc.shm_size = 12M
apc.optimization = 0
apc.num_files_hint = 256
apc.user_entries_hint = 1024
apc.ttl = 0
apc.user_ttl = 0
apc.gc_ttl = 600
apc.cache_by_default = 0
apc.filters = "-/home/username/public_html/scripts-not-to-cache/.*,-/home/username/public_html/apc/.*"
apc.slam_defense = 0
apc.use_request_time = 1
apc.mmap_file_mask = /tmp/apc.XXXXXX
apc.file_update_protection = 2
apc.enable_cli = 0
apc.max_file_size = 2M
apc.stat = 1
apc.write_lock = 1
apc.report_autofilter = 0
apc.include_once_override = 0
apc.rfc1867 = 0
apc.rfc1867_prefix = "upload_"
apc.rfc1867_name = "APC_UPLOAD_PROGRESS"
apc.rfc1867_freq = 0
apc.localcache = 1
apc.localcache.size = 256
apc.coredump_unmap = 0
apc.stat_ctime = 0

Reloading PHP-fastcgi processes after changes to php.ini

You will need to reload the php-fastcgi processes if you change php.ini. Luckily, Virtualmin creates service scripts for each virtual domain, which you can use to reload PHP.

The service scripts are located in /etc/rc.d/init.d.

Script allows stop, start, restart
service php-fcgi-domain2-com restart

The service may also be restarted via Webmin > Services > Bootup and Shutdown

Memory Considerations

PHP FCGId will launch the number of sub-processes you specify in Virtualmin > Server Configuration > Website Options, so beware of the memory overhead if you specify a large number, because each will consume the amount of memory specified in  apc.shm_size. You may need to experiment with this value, depending on your server’s memory and potential traffic.

Virtualmin explains: “When PHP scripts for this domain as run via FCGId, the number of PHP processes set in this field will be kept running at all times to serve requests. You can increase this from the default of 4 to improve PHP script latency, or decrease it to reduce memory use. Setting it to None will cause PHP processes to be launched only as needed on demand, and to be cleaned up after some period of inactivity.”

Nginx support on Virtualmin is working well, and it is reported that the lead developer, Jamie Cameron, is working on a php-fpm implementation, which should increase performance and lessen memory requirements. Let’s hope this rumor is true!

W3 Total Cache Settings

Provided you use the nginx.conf settings described above, using APC cache for all W3TC categories of cache/minify/etc. will work with no additional changes to the conf file. However, if you wish to choose Disk cache for page and/or minify cache, URL rewriting will not work properly on Virtualmin. You must include the location {…} directives in the nginx.conf created in your public_html root by W3TC, and add them after the location {…} directives described above.

You can do this either by copying the directives and pasting into /etc/nginx/nginx.conf, or (better) by using an include statement:

location ^~ /blogs.dir/ {
root /home/username/public_html/wordpress/wp-content;
include /home/username/public_html/nginx.conf;

However, remember you will need to restart NginX web server after making changes to the W3TC configuration, because these will over-write the local nginx.conf file.


Sorry, no benchmarks. See my example/test site here.

Nginx support on Virtualmin is working well, though I don’t see much improvement over Apache in terms of initial index page load, or time to first byte on the WordPress test site I created.  Cached and subsequent pages load very quickly, so it looks like there is an overall improvement. All WordPress functions seem to work well: comments, image uploads, etc. are no problem. I am interested to see if there is improvement with other scripts, especially phpbb3.

, , , ,

20 Responses to WordPress with W3 Total Cache on Nginx with APC (Virtualmin)

  1. Jason April 17, 2012 at 1:03 pm #

    Great tutorial, Chris.Yours is the only one like it.

    Is there anything outside of these instructions that might prohibit pretty permalinks from working correctly? I think I’ve covered everything, but I’m still getting 404s for some reason.

    • Chris Gilligan April 17, 2012 at 1:17 pm #

      Thanks Jason,

      My instructions are for installation in public_html/wordpress and SiteURL as domain (not domain.tld/wordpress).

      Is your problem perhaps with W3TC/APC? For php.ini/APC changes, you have to reload the php-fcgi process for the virtual server.

      For W3TC Minify, UNcheck Minify > General > Rewrite URL , or your css and js files will probably not load.

      Did you install the Nginx Compatibility plugin? It adds a function to make pretty permalinks work without /index.php (by fooling WP to think mod_rewrite is loaded). The default instructions for that plugin are not applicable to a Virtualmin server.

      Also be sure you have the correct port for the virtual server in nginx.conf, as they will all be increments of 9001,9002,9003, etc in the order they were created. If you have overwritten the correct port, check in Webmin > Services > Bootup and Shutdown for the correct port, modify nginx.conf…

      After you update nginx.conf, you will have to restart the nginx server, and also the process for the virtual machine. Be patient, it is slower to restart than apache.

  2. Jason April 17, 2012 at 1:26 pm #

    Okay. I *think* I’ve done all of that, save for double-checking the ports. Though I started with a vanilla server/Virtualmin setup and changed nothing out of the ordinary. I do have Nginx Compatibility installed and activated.

    It should work with that plugin alone, correct? Even with WTC deactivated, I still have 404s. I’ll try restarting the php-fcgi process again.

  3. Jason April 17, 2012 at 1:54 pm #

    Just to be clear, the ports you list in your nginx.conf amendment are supposed to both be fastcgi_pass localhost:9000; right?

    • Chris Gilligan April 17, 2012 at 2:09 pm #

      port should match the port assigned to the virtual server, check Webmin > System > Bootup & Shutdown > for the correct port. The first virtual server will be assigned 9000, next is assigned 9001, etc.

      Also be sure if you are using W3TC, to include w3tc’s additional nginx.conf (/home/username/public_html/nginx.conf), see W3 Total Cache Settings, above.

  4. Jason April 17, 2012 at 3:19 pm #

    Okay. That all checked out restarted the php-fcgi service and restarted nginx. Does this config look correct to you?

    W3TC refuses to deploy, and is telling me: “It appears Minify URL rewriting is not working. If using apache, verify that the server configuration allows .htaccess or if using nginx verify all configuration files are included in the configuration.”

    We know that—but W3TC refuses to deploy even with Minify URl disabled. Neither has an effect on URL rewriting, and everything looks fine when I run the compatibility check.

    When I appended the APC settings to /home/domainname/etc/php5/php.ini, was it okay that I placed them toward the bottom of the file?

  5. Jason April 17, 2012 at 4:07 pm #

    For anyone following along, I sort of fixed this problem by amending my /home/domain/public_html/nginx.conf with

    location / {
    if (!-e $request_filename) {
    rewrite ^(.+)$ /index.php?q=$1 last;

    Permalinks are now pretty, but W3TC has not stopped complaining.

    • Chris Gilligan April 17, 2012 at 4:25 pm #

      Change all of the W3TC caches to APC, don’t use disk.

      Make sure the active NginX Compatibility plugin is PHP5 version if you are on PHP5. Default for that plugin is PHP4.

      OR, if everything is working fine, you can disable Minify error notifications in Minify > General

      Thanks for the additional info, and I’m glad you got it working.

  6. Jason April 17, 2012 at 4:35 pm #

    Thanks, Chris. Switched to APC, per the tutorial. I must have skipped that information the second time around. Did enable the PHP5 version of NginX compatibility.

    Still unsure about the Minify error. Error reporting has been disabled all along, and I don’t have any problems loading my JS or CSS, so I’m not really sure what it’s harping about. I was able to disable the notification by unchecking General > Settings > Miscellaneous > Verify Rewrite Rules. Kind of scary, but everything seems fine.

  7. Jason April 20, 2012 at 11:35 am #

    Chris, were you able to successfully autoupdate with nginx/Wordpress? I read that it may require users to run fastcgi as nginx. Not sure if that’ll have any effect on Virtualmin.

  8. Chris Gilligan April 20, 2012 at 4:59 pm #

    Yes, autoupdate works. Virtualmin does run nginx as FCGId, which is run as virtual server owner, which is what makes WordPress updates “just work”.

    Virtualmin’s implementation may not be the best or fastest way to run a WordPress site on Nginx, but it is certainly convenient and easy. It’s also very easy to add additional PHP FCGId sub-processes to decrease latency, or to switch to “None”, and have the processes spawn on demand (higher latency, but more RAM frugal, and auto-scaling).

    Also, make sure you have the PHP FTP module loaded.

  9. Max May 30, 2012 at 11:43 pm #

    If you want to make the opcode cache a little more effective, but only on a production server, you should set apc.stat = 0. When it is set to 1 APC checks on every request an object in the cache to see if the file has recently been modified on disk, if it has, it recompiles the php code and stores the object again.

    You can see how the extra stat() on each object access would slow the object cache down in a production environment – particularly if the site is very busy, however, this is very useful if you are developing on a site with APC caching enabled.

    • Chris Gilligan May 31, 2012 at 9:26 am #

      True Max! Remember to flush the APC cache after software updates such as WordPress core or plugin updates.

  10. Mike June 12, 2012 at 10:00 am #

    Excellent information – would this run with multisite installations where sites run on thei own domain name?

    Thanks a lot!

    • Chris Gilligan June 14, 2012 at 4:19 pm #

      Mike, I do not know if this will work with multisite, as I do not use it. I suppose I could test it at some point in the future, but I am not motivated to do so at the moment.

  11. Santiago June 22, 2012 at 6:08 pm #

    Excellent information, Thanks.

  12. Mike October 9, 2012 at 10:46 am #

    Hey, another question, i did set up a new website according to this description which works fine but i allways need to call it with http://domain.tld/wordpress – grrr
    How can i order nginx to always run it from the wordpress sub without having it into the URL?


    • Chris Gilligan October 9, 2012 at 1:08 pm #

      Settings > General > Site Address (URL)

      Then copy index.php from /wordpress and put it in your public root ( /public_html or whatever folder is above /wordpress )

      In this new index.php, change require('./wp-blog-header.php'); to require('./wordpress/wp-blog-header.php');

      If you have any internal links or hard-coded menus already set up, you may have to change them manually or via SQL.

  13. Nabeel December 11, 2015 at 7:01 am #

    Why are we adding @wordpress block? and what does it refer to? any files in “wordpress” directory? or any directory name having wordpress installed in it?

    Is it still valid with current version of nginx and wordpress and w3tc?

    • Chris Gilligan December 11, 2015 at 10:54 am #

      Nabeel, this is a very old post from 2012; I no longer suggest to use this configuration, it was just a test case. @wordpress is to handle WordPress pretty URLs/permalinks.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.