Nginx Wordpress Configuration


Discuss Wordpress and Centmin Mod Nginx on the forums here.


If you prefer using Memcached server for caching instead of fastcgi caching, check out new WP-FFPC Plugin guide.

Guide last revision: Friday August 19th, 2016 (previous update Sunday June 7th, 2015)

  1. Basic Nginx Wordpress configuration
  2. Advanced Nginx Wordpress configuration + fastcgi_cache + Nginx Helper plugin
  3. Advanced Nginx Wordpress configuration + fastcgi_cache + ngx_pagespeed


Setting up MySQL database

Enable InnoDB MySQL Engine and Table support by ensuring your MySQL config settings for /etc/my.cnf have the following InnoDB related settings set. If not set in /etc/my.cnf change them to the following and restart MySQL server:

innodb=ON
default-storage-engine = InnoDB

On a fresh Centmin Mod install, you may also want to enable server wide MySQL global UTF-8 support. Enabling this will apply to every MySQL database and tables you create on your MySQL server instance. To change defaults respectively for character set and collation to utf8 and utf8_general_ci, you need to add under [mysqld] group in /etc/my.cnf the following line character-set-server=utf8:

[mysqld]
 character-set-server=utf8

Note: Only MySQL databases and tables created after this change will by default be created as utf8 character set and collations. Any databases before this change, will still be latin1.

Restart MySQL server:

service mysql restart

Or restart with Centmin Mod command shortcut:

mysqlrestart

Basic Nginx Wordpress configuration: [top]

1. If you want to enable Wordpress Permalinks, you'll need to edit your site's Vhost configuration file and add the following and restart Nginx web server:

        location /wordpress {
        try_files $uri $uri/ /wordpress/index.php?q=$request_uri;
        }

Where /wordpress is where you installed Wordpress i.e. yourdomain.com/wordpress

2. Create a file named wpsecure.conf at /usr/local/nginx/conf/wpsecure.conf and add following lines:

# Deny access to any files with a .php extension in the uploads directory
# Works in sub-directory installs and also in multisite network
location ~* /(?:uploads|files)/.*\.php$ {
        deny all;
}

# Make sure files with the following extensions do not get loaded by nginx because nginx would display the source code, and these files can contain PASSWORDS!
location ~* \.(engine|inc|info|install|make|module|profile|test|po|sh|.*sql|theme|tpl(\.php)?|xtmpl)$|^(\..*|Entries.*|Repository|Root|Tag|Template)$|\.php_
{
        return 444;
}

#nocgi
location ~* \.(pl|cgi|py|sh|lua)\$ {
        return 444;
}

#disallow
    location ~* (roundcube|webdav|smtp|http\:|soap|w00tw00t) {
        return 444;
}

location ~ /(\.|wp-config\.php|readme\.html|license\.txt) { deny all; }

3. Create a file named wpnocache.conf at /usr/local/nginx/conf/wpnocache.conf and add following lines:

  # Add trailing slash to */wp-admin requests.
rewrite /wp-admin$ $scheme://$host$uri/ permanent;

4. Configure your Nginx vhost by adding these 2 lines and try_files for Wordpress permalinks.

include /usr/local/nginx/conf/wpsecure.conf;
include /usr/local/nginx/conf/wpnocache.conf;

So Vhost configuration file changes from:

server {
            listen   80;
            server_name localhost;
            root   html;

# limit_conn limit_per_ip 16;
# ssi  on;

            location / {

#       Enables directory listings when index file not found
#       autoindex  on;

#       Shows file listing times as local time
#       autoindex_localtime on;

#       Enable for vBulletin usage WITHOUT vbSEO installed
#       try_files       $uri $uri/ /index.php;
            
            }

include /usr/local/nginx/conf/staticfiles.conf;
include /usr/local/nginx/conf/php.conf;
include /usr/local/nginx/conf/drop.conf;
#include /usr/local/nginx/conf/errorpage.conf;

       }

to:

server {
            listen   80;
            server_name localhost;
            root   html;

# limit_conn limit_per_ip 16;
# ssi  on;

            location / {

#       Enables directory listings when index file not found
#       autoindex  on;

#       Shows file listing times as local time
#       autoindex_localtime on;

#       Enable for vBulletin usage WITHOUT vbSEO installed
#       try_files       $uri $uri/ /index.php;
            
            }

        location /wordpress {
include /usr/local/nginx/conf/wpsecure.conf;
include /usr/local/nginx/conf/wpnocache.conf;

        try_files $uri $uri/ /index.php?q=$request_uri;
        }

include /usr/local/nginx/conf/staticfiles.conf;
include /usr/local/nginx/conf/php.conf;
include /usr/local/nginx/conf/drop.conf;
#include /usr/local/nginx/conf/errorpage.conf;

       }

Advanced Nginx Wordpress configuration + fastcgi_cache + Nginx Helper plugin [top]

Notes:

  1. The following configuration was tested on Centmin Mod v1.2.3+ and higher.
  2. Some Centmin Mod users on our Google+ Community site have reported a minor issue when logged into Wordpress Admin Dashboard, that previewing new post links result in 404 not found errors. A workaround for now is open preview link in new tab/window for browser and on preview link (404) error page, clear cookies and refresh page. That sometimes allows admin to still be logged in but see preview link. If anyone finds the permanent solution, please share on our Google+ Community site. Or try alternative cache method with Wordpress + WP-FFPC plugin memcached caching + ngx_pagespeed

This advanced Nginx Wordpress configuration was the result of compiling various sources of reading and then translating all of it to be compatible with Centmin Mod Nginx's vhost configuration format. Sources of info include:

1. Edit /usr/local/nginx/conf/nginx.conf to add fastcgi_cache settings. For Centmin Mod users, you can quickly access /usr/local/nginx/conf/nginx.conf via command line shortcut 'nginxconf' which will invoke nano text editor to open /usr/local/nginx/conf/nginx.conf.

Find starting http container

  http {

and add below it the following line

  http {
include /usr/local/nginx/conf/wp_fastcgicache.conf;

Then create file named wp_fastcgicache.conf at include /usr/local/nginx/conf/wp_fastcgicache.conf and ensure /var/cache/nginx exists add following lines:

fastcgi_cache_path /var/cache/nginx levels=1:2 keys_zone=WPCACHE:16m max_size=32m inactive=50m;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
fastcgi_cache_use_stale error timeout invalid_header http_500;

log_format cache '$remote_addr - $remote_user [$time_local]  '
                  '"$request" $status $body_bytes_sent '
                  '"$http_referer" "$http_user_agent" nocache:$no_cache '
                  '$upstream_cache_status';

### Map Mobile
map $http_user_agent $is_mobile {
    default 0;
    ~*android|ip(hone|od)|windows\s+(?:ce|phone) 1;
    ~*symbian|sonyericsson|samsung|lg|blackberry 1;
    ~*mobile 1;
}

2. Create a file named wpsecure.conf at /usr/local/nginx/conf/wpsecure.conf and add following lines:

# Deny access to any files with a .php extension in the uploads directory
# Works in sub-directory installs and also in multisite network
location ~* /(?:uploads|files)/.*\.php$ {
        deny all;
}

# Make sure files with the following extensions do not get loaded by nginx because nginx would display the source code, and these files can contain PASSWORDS!
location ~* \.(engine|inc|info|install|make|module|profile|test|po|sh|.*sql|theme|tpl(\.php)?|xtmpl)$|^(\..*|Entries.*|Repository|Root|Tag|Template)$|\.php_
{
        return 444;
}

#nocgi
location ~* \.(pl|cgi|py|sh|lua)\$ {
        return 444;
}

#disallow
    location ~* (roundcube|webdav|smtp|http\:|soap|w00tw00t) {
        return 444;
}

location ~ /(\.|wp-config\.php|readme\.html|license\.txt) { deny all; }

3. Create a file named wpcache.conf at /usr/local/nginx/conf/wpcache.conf and add following lines:

If wordpress installed in subdirectory i.e. /wordpress, you meed to add /wordpress in front of /(memcache\.php

i.e.

if ($request_uri ~* "/wordpress/(memcache\.php

/usr/local/nginx/conf/wpcache.conf contents:

#fastcgi_cache start
set $no_cache 0;

# POST requests and urls with a query string should always go to PHP
if ($request_method = POST) {
        set $no_cache 1;
}
if ($query_string != "") {
        set $no_cache 1;
}

# Don't cache uris containing the following segments
# If wordpress installed in subdirectory i.e. /wordpress 
# meed tp add /wordpress in front of /(memcache\.php
# i.e. if ($request_uri ~* "/wordpress/(memcache\.php
if ($request_uri ~* "/\?add-to-cart=|cart/|my-account/|checkout/|shop/checkout/|store/checkout/|customer-dashboard/|addons/|(memcache\.php|apc\.php|wp-admin/.*|xmlrpc\.php|wp-(app|cron|login|register|mail)\.php|wp-.*\.php|feed/|index\.php|wp-comments-popup\.php|wp-links-opml\.php|wp-locations\.php|sitemap(_index)?\.xml|[a-z0-9_-]+-sitemap([0-9]+)?\.xml)") {
        set $no_cache 1;
}

# Don't use the cache for logged in users or recent commenters
if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in|edd_items_in_cart|woocommerce_items_in_cart|wordpress-sec") {
        set $no_cache 1;
}

# Add trailing slash to */wp-admin requests.
rewrite /wp-admin$ $scheme://$host$uri/ permanent;

# Centmin Mod Nginx has nginx_cache_purge module
# added http://centmin.com/nginx.html
    location ~ /purge(/.*) {
    fastcgi_cache_purge WPCACHE "$scheme$request_method$host$1";
    return 200;
    allow 127.0.0.1;
    deny all;
    } 

4. Create a file named wpnocache.conf at /usr/local/nginx/conf/wpnocache.conf and add following lines:

  # Add trailing slash to */wp-admin requests.
rewrite /wp-admin$ $scheme://$host$uri/ permanent;

5. Create a duplicate copy of /usr/local/nginx/conf/php.conf file and name it /usr/local/nginx/conf/phpwpcache.conf

  cp -a /usr/local/nginx/conf/php.conf /usr/local/nginx/conf/phpwpcache.conf

Add the following lines at bottom of /usr/local/nginx/conf/phpwpcache.conf just above the last ending curly brace }

Cache 200 and 302 status requests for 2 minutes, 301 redirects for 1 hour and any other requests for 2 minutes.

         fastcgi_cache_bypass $no_cache $is_mobile;
         fastcgi_no_cache $no_cache $is_mobile;
         fastcgi_cache WPCACHE;
         fastcgi_cache_valid  200 302 2m;
         fastcgi_cache_valid  301 1h;
         fastcgi_cache_valid  any 2m;
         fastcgi_cache_min_uses  1;
         add_header X-Cached $upstream_cache_status;

So the full file contents for /usr/local/nginx/conf/phpwpcache.conf looks like below:

  location ~ \.php$ {
    try_files $uri =404;
    fastcgi_split_path_info ^(.+\.php)(/.+)$;
    fastcgi_pass   127.0.0.1:9000;
    #fastcgi_pass   unix:/tmp/php5-fpm.sock;
    fastcgi_index  index.php;
    #fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
    fastcgi_param  SCRIPT_FILENAME    $request_filename;

fastcgi_connect_timeout 60;
fastcgi_send_timeout 180;
fastcgi_read_timeout 180;
fastcgi_buffer_size 256k;
fastcgi_buffers 4 256k;
fastcgi_busy_buffers_size 256k;
fastcgi_temp_file_write_size 256k;
fastcgi_intercept_errors on;

# comment out PATH_TRANSLATED line if /usr/local/lib/php.ini sets following:
# cgi.fix_pathinfo=0 
# as of centminmod v1.2.3-eva2000.01 default is set to cgi.fix_pathinfo=1

fastcgi_param  PATH_INFO          $fastcgi_path_info;
fastcgi_param  PATH_TRANSLATED    $document_root$fastcgi_path_info;

fastcgi_param  QUERY_STRING       $query_string;
fastcgi_param  REQUEST_METHOD     $request_method;
fastcgi_param  CONTENT_TYPE       $content_type;
fastcgi_param  CONTENT_LENGTH     $content_length;

fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
fastcgi_param  REQUEST_URI        $request_uri;
fastcgi_param  DOCUMENT_URI       $document_uri;
fastcgi_param  DOCUMENT_ROOT      $document_root;
fastcgi_param  SERVER_PROTOCOL    $server_protocol;

fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
fastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;

fastcgi_param  REMOTE_ADDR        $remote_addr;
fastcgi_param  REMOTE_PORT        $remote_port;
fastcgi_param  SERVER_ADDR        $server_addr;
fastcgi_param  SERVER_PORT        $server_port;
fastcgi_param  SERVER_NAME        $server_name;

# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param  REDIRECT_STATUS    200;

         fastcgi_cache_bypass $no_cache $is_mobile;
         fastcgi_no_cache $no_cache $is_mobile;
         fastcgi_cache WPCACHE;
         fastcgi_cache_valid  200 302 2m;
         fastcgi_cache_valid  301 1h;
         fastcgi_cache_valid  any 2m;
         fastcgi_cache_min_uses  1;
         add_header X-Cached $upstream_cache_status;

}

6. Now to put all the pieces together and edit your Centmin Mod Nginx vhost for your specific site. For this example Wordpress is installed at /wp.

The additional entries of note are:

          access_log /var/log/nginx/access.cache.log cache;

Place these 3 lines in /wp location if wordpress is installed at /wp. This will ensure only wordpress at /wp uses fastcgi_cache and not other scripts located outside of /wp.

include /usr/local/nginx/conf/wpsecure.conf;
include /usr/local/nginx/conf/wpcache.conf;
include /usr/local/nginx/conf/phpwpcache.conf;

Otherwise if wordpress installed at root location /, use these 2 lines within root location / container

include /usr/local/nginx/conf/wpsecure.conf;
include /usr/local/nginx/conf/wpcache.conf;

So it would like like the below example if wordpress installed at root / location.

            location / {
    include /usr/local/nginx/conf/wpsecure.conf;
    include /usr/local/nginx/conf/wpcache.conf;

    # block common exploits, sql injections etc
    #include /usr/local/nginx/conf/block.conf;

    #Enables directory listings when index file not found
            #autoindex  on;
            }

Change try_files line. If wordpress is installed at /wp change from:

   location /wp {
    try_files $uri $uri/ /wp/index.php?q=$request_uri;
    }

to:

   location /wp {
    try_files $uri $uri/ /wp/index.php?q=$args;
    }

If wordpress is installed at / change from:

   location / {
    try_files $uri $uri/ /index.php?q=$request_uri;
    }

to:

   location / {
    try_files $uri $uri/ /index.php?q=$args;
    }

If wordpress installed at root location /, you will need to comment out (with hash # in front the default php.conf and use phpwpcache.conf).

include /usr/local/nginx/conf/phpwpcache.conf;
#include /usr/local/nginx/conf/php.conf;

Otherwise if wordpress installed at location /wp, leave the default php.conf in place below root / location

include /usr/local/nginx/conf/staticfiles.conf;
include /usr/local/nginx/conf/php.conf;
#include /usr/local/nginx/conf/phpstatus.conf;
include /usr/local/nginx/conf/drop.conf;
#include /usr/local/nginx/conf/errorpage.conf;

Using the default main server Nginx vhost at /usr/local/nginx/conf/conf.d/virtual.conf as an example, edit it to the below format for wordpress installed at /wp.

  server {
#         listen   80;
            listen   80 default_server;
            server_name localhost;
            root   html;

        access_log              /var/log/nginx/localhost.access.log     main buffer=32k;
        error_log               /var/log/nginx/localhost.error.log      error;
        access_log /var/log/nginx/access.cache.log cache;

# ngx_pagespeed & ngx_pagespeed handler
include /usr/local/nginx/conf/pagespeed.conf;
include /usr/local/nginx/conf/pagespeedhandler.conf;

# limit_conn limit_per_ip 16;
# ssi  on;

            location / {
    # block common exploits, sql injections etc
    #include /usr/local/nginx/conf/block.conf;

    #Enables directory listings when index file not found
            #autoindex  on;
            }

    location /wp {
include /usr/local/nginx/conf/wpsecure.conf;
include /usr/local/nginx/conf/wpcache.conf;
include /usr/local/nginx/conf/phpwpcache.conf;

    try_files $uri $uri/ /wp/index.php?q=$request_uri;
    }

        # example nginx-http-concat
        # /csstest/??one.css,two.css
        #location /csstest {
        #concat on;
        #concat_max_files 20;
        #}

include /usr/local/nginx/conf/staticfiles.conf;
include /usr/local/nginx/conf/php.conf;
#include /usr/local/nginx/conf/phpstatus.conf;
include /usr/local/nginx/conf/drop.conf;
#include /usr/local/nginx/conf/errorpage.conf;

       }

Note, some common security settings are already contained in /usr/local/nginx/conf/drop.conf

        location = /robots.txt  { access_log off; log_not_found off; }
        location = /favicon.ico { access_log off; log_not_found off; expires 30d; }
        location ~ /\.          { access_log off; log_not_found off; deny all; }
        location ~ ~$           { access_log off; log_not_found off; deny all; }
        location ~ /\.git { access_log off; log_not_found off; deny all; }

7. Save your Nginx vhost conf file and then restart Nginx server

  service nginx restart

or via command line shortcut

  ngxrestart

8. Install Nginx Helper Wordpress Plugin

Install Nginx Helper Wordpress plugin. The plugin has a hard coded path to fastcgi_cache_path which is /var/run/nginx-cache which differs from configuration defined /var/cache/nginx outlined above. (see first FAQ question at https://github.com/rtCamp/nginx-helper).

You need to change the hard coded path by adding a line in wp-config.php like below:

  define( 'RT_WP_NGINX_HELPER_CACHE_PATH','/var/cache/nginx');

Advanced Nginx Wordpress configuration + fastcgi_cache + ngx_pagespeed [top]

Follow above Advanced Nginx Wordpress configuration guide and then enable Nginx ngx_pagespeed module as per instructions here.

Nginx ngx_pagespeed module pretty much makes any Wordpress cache plugin redundant (W3 Total Cache, WP Super Cache as ngx_pagespeed minifies all css and js and combines css and js and much more including caching. No more messing around with Wordpress Caching plugins !

To illustrate this, I installed the Responsive Wordpress theme and ran Firefox Firebug to show the differences. Notice number of requests are reduced as is page load times (~24.5% faster page load times and ~27.5% faster onload times).

First, with Nginx ngx_pagespeed module disabled.

ngx_pagespeed off

Chrome PageSpeed Score = 94

Chrome PageSpeed Score with ngx_pagespeed off

Second, with Nginx ngx_pagespeed module enabled with default Centmin Mod preset settings. Notice number of requests on page were reduced from 8 to 7 requests.

ngx_pagespeed on

Chrome PageSpeed Score = 95

Chrome PageSpeed Score with ngx_pagespeed on

Third, with Nginx ngx_pagespeed module enabled with default Centmin Mod preset settings + enabling combine_javascript filter. Notice number of requests on the page was further reduced from 7 to just 6 requests. The benefits increase as your serve more css and js files.

ngx_pagespeed on + combine_javascript filter
ngx_pagespeed on + combine_javascript filter

Chrome PageSpeed Score = 98

Chrome PageSpeed Score with ngx_pagespeed on + combine_javascript filter enabled