Posted on March 1, 2017 · Posted in Misc

Configuring WordPress on Nginx: Full LEMP Guide

This guide is written for webmasters who want to solve the problem of poor performance of WordPress websites. It provides a step-by-step instructions to configure a limited resources server (1 core, 512 RAM) to be used in LEMP bundle (Linux + Nginx + MySQL + PHP). To use this guide comfortably, you should have a general understanding about websites and Linux servers.

The most popular solution for webservers is a LAMP (Linux + Apache +MySQL + PHP). This configuration is easy to setup, well documented and often used in small projects. However, being easy-to-use, it is resource-consuming and has high downtime risks at the load increase (more visitors, a lot of posts, heavy plugins).

As a result, webmasters have to either increase hosting payments and rise hardware performance or look for less resource-consuming solutions.

To reduce the load on Apache, a proxy server of Nginx is placed in front of LAMP which makes the static file return faster. This solution allows to significantly increase performance with little effort and support of Apache-specific features (like .htaccess). To improve the dynamics, PHP in CGI mode, byte-code and SQL queries cache are used. However, in such a system  there still remains a bottleneck — the Apache performance.

The next step in performance development is moving from Apache to FRM and full cache of all website pages. This guide tells in detail about WordPress configuration standing the load of several hundred concurrent requests per second under the conditions of very limited server resources. The guide describes all configuration steps, including initial server configuration able to protect from standard threats, LEMP setup and configuration, as well as WordPress configuration. If you follow all these steps, you’ll get an efficient server.

manual: configuring wordpress on nginx

Server Requirements

The guide provides the examples of single-core, 512 MB RAM, SSD VPS server configuration. Since the settings suggested below are intended for the intensive use of the objects cached on the disks, the most important parameter is the performance of disk subsystem.

Debian 7 x86 has been selected to be installed as the server OS, since in small installations it provides more efficient memory usage compared to x64 versions.

Note: Different hosting providers deliver different preset packages.

Server Environment Preparation

After your server OS has been installed, connect to it using ssh.

Update the server software.

apt-get update
apt-get upgrade

Install and configure sudo.

apt-get install sudo

Add a new user to get SSH access from a user account.

useradd -m -U -s /bin/bash webuser
passwd webuser

Configure sudo.

nano /etc/sudoers

# If you log in as root, you will have to enter root password instead of a user password.
Defaults rootpw
# Allow the webuser to get root rights.
webuser ALL=(ALL) ALL

Now log in as the new user and make sure that it’s ok.
If there haven’t been any problems, restrict root access via SSH.

sudo nano /etc/ssh/sshd_config

Find the string
PermitRootLogin Yes

and replace it with

PermitRootLogin no

After you made these changes, restart OpenSSH.

sudo service ssh restart

To restrict the opportunities of potential hackers to guess SSH passwords we will use fail2ban. Later we’ll configure fail2ban to block brute-force attacks  on your WordPress password.

sudo apt-get install fail2ban

Additional fail2ban settings are not necessary for our task.
Now if the ssh password is entered wrongly six times, the user will be blocked using iptables for 600 seconds.

Configure time synchronization.
#Install the necessary packages
sudo apt-get install ntp ntpdate
#Install synchronization server
sudo ntpdate -s
#Start ntp daemon
sudo service ntp start

Configure exim4 to send mail from your website.

sudo apt-get install exim4
sudo dpkg-reconfigure exim4-config

  • General type of mail configuration: select the upper item: internet site; mail is sent and received directly using SMTP.
  • System mail name: specify full server name, for example,
  • IP-addresses to listen on for incoming SMTP connections:
  • Other destinations for which mail is accepted: leave it empty.
  • Domains to relay mail for: leave it empty.
  • Machines to relay mail for: leave it empty.
  • Keep number of DNS-queries minimal (Dial-on-Demand)?No
  • Delivery method for local mail:  any value.
  • Split configuration into small files? Yes

Resume of the actions:

  1. A new user webuser has been created to access the server over ssh.
  2. To elevate the privileges to root, the user webuser has to enter root password.
  3. Protection ssh against brute force has been configured.
  4. Time synchronization has been configured.
  5. Mail service to send e-mails from the website has been configured.

LEMP Installation

We’ll use the installation from packages to make update management easier. To get the latest nginx (nginx 1.2.1 is installed by default), we’ll connect the original nginx repository.

Download and install the PGP key from the nginx server.

mkdir ~/installfiles
cd ~/installfiles
sudo apt-key add nginx_signing.key

Add nginx repository to sources list of apt manager.

sudo nano /etc/apt/sources.list
deb wheezy nginx
deb-src wheezy nginx

Update the data of the available packages.

sudo apt-get update

It’s time to install packages necessary for webserver operation.

sudo apt-get install nginx mysql-server php5-fpm php5-mysql php5-gd php-apc unzip

After a short installation, we have got an efficient nginx, php-fpm, mysql stack.

LEMP Configuration

Create a directory to store website files.

Specify your website name without http and www instead of %SITENAME%. For example,

sudo mkdir /srv/%SITENAME%
sudo chown -R www-data:www-data /srv/%SITENAME%

Create the website MySQL database.

Note: Change the name of the database, user name and password.

mysql -uroot -p
CREATE USER 'wp_user'@'localhost' IDENTIFIED BY 'Pa$$w0rd!';
GRANT ALL PRIVILEGES ON wp_db  . * TO 'wp_user'@'localhost';

Now let’s configure FPM.

Remove the default configuration.

sudo rm /etc/php5/fpm/pool.d/www.conf

Create a new configuration.

sudo nano /etc/php5/fpm/pool.d/blog.conf

Note: Replace %SITENAME% with your website name without www  in the config file.

# PHP-FPM conf
# This is an effective configuration for WordPress running on a 1CPU, 512 RAM server.
# If you would like to use more than one website on a server, copy this file and replace all names of the previous website to the name of the new one.
# Replace %SITENAME% with the name of your website without http and www.
# User and group name to launch php-fpm processes
user = www-data
group = www-data
#Path to the listening socket
listen = /var/run/$pool.sock
listen.owner = www-data = www-data
listen.mode = 0660
# Mode of worker process management
pm = dynamic
# Maximum number of worker processes. This setting defines the restriction to the number of simultaneous queries to be serviced.
pm.max_children = 8
# The number of worker processes at the start.
pm.start_servers = 3
# Minimum number of supported free worker processes.
pm.min_spare_servers = 2
# Maximum number of supported free worker processes.
pm.max_spare_servers = 4
# The number of child process queries, after which the process will be restarted.
# This is useful to prevent memory leakage when using third-party libraries.
pm.max_requests = 600
# Restricts the files available to PHP to the specified directory including the file itself
php_admin_value[open_basedir] = /srv/$pool:/tmp
# Configuration of logging slow scripts
request_slowlog_timeout = 5s
slowlog = /var/log/php-fpm-slowlog-$pool.log

After we have created a configuration file php-fpm, restart the service.

sudo service php5-fpm restart

If you need to create another website on this server, copy this configuration file and replace the name of the website in it.

Let’s configure nginx.

sudo nano /etc/nginx/nginx.conf

Change the username from nginx to www-data.

# user nginx;
user www-data;

Find and uncomment the following strings.

#tcp_nopush on;
tcp_nopush on;

Configure gzip.

gzip  on;
gzip_min_length  1000;
gzip_proxied     any;
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript text/x-js;
gzip_disable "MSIE [1-6]\.(?!.*SV1)";
gzip_comp_level  6;

Change keepalive timeout.

#keepalive_timeout 65;
keepalive_timeout 10;

Remove default configuration files:

sudo rm /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/example_ssl.conf

Create a new configuration file.

sudo nano /etc/nginx/conf.d/blog.conf

Note: replace %SITENAME% to the website name without www in the list.

# nginx site conf
# This is an effective configuration for WordPress running on a 1CPU, 512 RAM server. If you would like to use more than one website on a server, copy this file and replace all names of the previous website to the name of the new one.
# Replace %SITENAME% with the name of your website without http and www.
server {
listen *:80;
server_name %SITENAME% www.%SITENAME%;
access_log /var/log/nginx/%SITENAME%.access.log;
error_log /var/log/nginx/%SITENAME%.error.log;
root /srv/%SITENAME%;
index index.php;
# Don't report of favicon not found
location = /favicon.ico {
log_not_found off;
access_log off;
# Don't report of robots.txt not found
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
# Prevent access to the files if their names start with a dot. For example, .htaccess
location ~ /\. {
deny all;
# Prevent access to php files located in uploads, files directories.
location ~* /(?:uploads|files)/.*\.php$ {
deny all;
rewrite /wp-admin$ $scheme://$host$uri/ permanent;
# Disable logging for files, set the maximum cache lifetime.
location ~* ^.+\.(ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|rss|atom|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
access_log off; log_not_found off; expires 90d;
set $cache_uri $request_uri;
# Disable caching if POST requests are used.
if ($request_method = POST) {
set $cache_uri 'null cache';
if ($query_string != "") {
set $cache_uri 'null cache';
# Disable caching when accessing service scripts.
if ($request_uri ~* "(/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 $cache_uri 'null cache';
# Disable caching, if cookies are used.
if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_logged_in") {
set $cache_uri 'null cache';
# Return cache.
location / {
try_files /wp-content/cache/supercache/$http_host/$cache_uri/index.html $uri $uri/ /index.php?$args ;
# Return FASTCGI processing to the server in case of php files.
location ~ [^/]\.php(/|$) {
root /srv/%SITENAME%;
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
if (!-f $document_root$fastcgi_script_name) {
return 404;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_pass unix:/var/run/%SITENAME%.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
#fastcgi_param SCRIPT_FILENAME /srv/%SITENAME%/$fastcgi_script_name;

After you have saved the configuration file, make sure that the configuration is healthy.

sudo nginx –t

If there have been no errors, restart nginx.

sudo service nginx restart

Resume of the actions:

  1. MySQL database has been installed. A database for WordPres has been created, a user has been created and privileges have been set.
  2. Php-frm has been installed and configured.
  3. Nginx has been installed and configured.

WordPress  Installation

WordPress installation on the server is the same to Apache installation. Download the latest WordPress version to your server, unpack it and change the file owner to www-data.

cd ~/installfiles/
sudo mv wordpress/* /srv/%SITENAME%/
sudo chown -R www-data:www-data /srv/%SITENAME%

Then go to the browser and continue installation. I won’t describe the installation in detail, since it is intuitive.

Note: WP Super Cache plugin will be used to generate static files.

Right after that, install and activate 2 plugins necessary for correct LEMP operation: Nginx Helper and WP Super Cache.

Nginx Helper plugin doesn’t require any setting, but it’s important that it is installed and activated. In case WP Super Cache plugin is responsible for cache, Purge button above will not work.

WP Super Cache requires some configuration.

After the activation, select the necessary CNC type on http://%SITENAME%/wp-admin/options-permalink.php

Go to the Settings tab of the plugin settings.

Check the following settings (others have to be unchecked):

  • Cache hits to this website for quick access (Recommended)
  • Use mod_rewrite to serve cache file
  • Compress pages so they’re served more quickly to visitors. (Recommended)]304 Not Modified browser caching. Indicate when a page has not been modified since it was last requested. (Recommended)
  • Cache rebuild. Serve a supercache file to anonymous users while a new file is being generated. (Recommended)
  • Clear all cache files when a post or page is published or updated.
  • Extra homepage checks. (Very occasionally stops homepage caching) (Recommended)
  • Only refresh current page when comments made

Wordpress plugin WP Super cache settings for using with nginx

After making the changes, click Update Status.

When saving, you’ll see the following message above:

Rewrite rules must be updated
The rewrite rules required by this plugin have changed or are missing. Scroll down the Advanced Settings page and click the Update Mod_Rewrite Rules button

WP Super Cache doesn’t know that nginx is used and says that .htaccess has to be changed. To make it work, allow it. Scroll down and click Update Mod_Rewrite Rules button.

In the Debug tab, check:

  • Clear cache on error.

And also click Update Status button.

Now go to Easy tab and make sure that it works by clicking Test Cache button.

If you have seen the message “The timestamps on both pages match!”, you have done it right.

Resume of the actions:

  1. WordPress has been installed.
  2. WP Super Cache and Nginx helper plugins have been installed and configured.

Now the configuration is over.


  1. Don’t forget that the speed of your blog depends on the plugins you are using. Often they are developed by enthusiasts, are not optimized and have errors responsible for additional load.
  2. The guide doesn’t mention MySQL configuration and the backup procedure. It is a separate important topics.

Related Articles