WordPress is the most popular CMS (content management system) used on the internet today. WordPress sites can be served using a HTTP server such as Apache or NGINX, while Apache is a great option to serve websites, many sites have moved to NGINX because of it’s scalable event-driven architecture, low resources and better delivery of statics files. In this tutorial you will learn how to configure NGINX for various types of WordPress installations, including multisite configurations, rewrite rules and the use of .conf files to apply repeated configurations.
In this guide, you will need sudo to install and edit files. I assume that you have gone through the initial server setup.
You will need to install MySQL, PHP & NGINX. You can follow these guides to install LEMP on Ubuntu or Debian.
Note that our server blocks will be different & that in this tutorial we are making PHP-FPM use a UNIX Socket.
It is often recommended to set the number of NGINX workers equal the number of processors, you can determine the number of processors using:
cat /proc/cpuinfo | grep processor
Open up the main NGINX configuration file:
sudo nano /etc/nginx/nginx.conf
Increase or decrease the number of workers depending on your system’s specs:
worker_processes 1;
NGINX limits the number of connections that a worker can maintain at one time, if your websites have many visitors you might want to increase the limit of connections. In theory the maximum number of connections = workers * limit.
worker_connections 768;
Files can be compressed using Gzip to accelerate WordPress, the smaller the data size requested by the user, the faster the response. Think about CSS files & HTML files, they have many similar strings, repeated text and white spaces. Gzip uses an algorithm called DEFLATE that removes duplicate strings by linking to the previous location of that identical string and creates a much smaller file. Find the Gzip section and enable it:
gzip on;
gzip_types text/css text/x-component application/x-javascript application/javascript text/javascript text/x-js text/richtext image/svg+xml text/plain text/xsd text/xsl text/xml image/x-icon;
Save & exit.
Since you might be hosting more than one WordPress website, we are going to create a few .conf files that can be loaded from the server blocks instead of writing the same configuration many times on each server block.
In the next steps we will create 3 files that will hold our configurations:
We are going to create all the files in a directory called “global” but first we will need to create the mentioned directory:
sudo mkdir /etc/nginx/global
I am going to set /etc/nginx/global as the current directory just to make things easier.
cd /etc/nginx/global
Let’s create our first .conf file applicable to any kind of websites.
sudo nano common.conf
This will open an empty file, copy the following configurations:
# Global configuration file.
# ESSENTIAL : Configure Nginx Listening Port
listen 80;
# ESSENTIAL : Default file to serve. If the first file isn't found,
index index.php index.html index.htm;
# ESSENTIAL : no favicon logs
location = /favicon.ico {
log_not_found off;
access_log off;
}
# ESSENTIAL : robots.txt
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
# ESSENTIAL : Configure 404 Pages
error_page 404 /404.html;
# ESSENTIAL : Configure 50x Pages
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/www;
}
# SECURITY : Deny all attempts to access hidden files .abcde
location ~ /\. {
deny all;
}
# PERFORMANCE : Set expires headers for static files and turn off logging.
location ~* ^.+\.(js|css|swf|xml|txt|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 30d;
}
listen 80;
specifies the listening port of the server.
index index.php...
specifies the default file to serve (WordPress index.php). If the first file isn’t found, the second will be used and so on. You might have HTML sites that’s why we are including index.html & index.htm;.
location = /robots.txt {allow all;}
allows the access to robots.txt, if you want to specify another directory for the robots.txt you can add an alias:
location /robots.txt {
alias /var/www/example.com/public/sample_robots.txt;
}
location ~ /\. {deny all;}
in the Linux operating system a hidden file begins with a “.”, access to some hidden files, such as .htaccess, should be blocked for security reasons.
location ~* ^.+\.(js|css|swf...
expires headers tell the browser whether they should request a specific file from the server or whether they should grab it from the browser’s cache. With expires 30d we are telling the browser to store static files such as pictures for 30 days.
Save and exit.
Let’s create a .conf file applicable to all WordPress sites:
sudo nano wordpress.conf
This will open an empty file, copy the following configurations:
# WORDPRESS : Rewrite rules, sends everything through index.php and keeps the appended query string intact
location / {
try_files $uri $uri/ /index.php?q=$uri&$args;
}
# SECURITY : Deny all attempts to access PHP Files in the uploads directory
location ~* /(?:uploads|files)/.*\.php$ {
deny all;
}
# REQUIREMENTS : Enable PHP Support
location ~ \.php$ {
# SECURITY : Zero day Exploit Protection
try_files $uri =404;
# ENABLE : Enable PHP, listen fpm sock
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
}
# PLUGINS : Enable Rewrite Rules for Yoast SEO SiteMap
rewrite ^/sitemap_index\.xml$ /index.php?sitemap=1 last;
rewrite ^/([^/]+?)-sitemap([0-9]+)?\.xml$ /index.php?sitemap=$1&sitemap_n=$2 last;
#Yeah! you did it.
try_files $uri $uri/ /index.php?q=$uri&$args
rewrite rule required to allow you to choose your custom permalink structure on WordPress.
location ~* /(?:uploads|files)/.*\.php$ {deny all;}
this will prevent malicious code from being uploaded and executed from the WordPress media directory.
location ~ \.php$ {...}
since WordPress is a php site, we need to tell NGINX how to a pass our php scripts to PHP5.
try_files $uri =404;
this is a security rule, you only want to either serve a determined php file or go to a 404 error.
More Rules: You might want to add more NGINX rules, for example, if you use the same WP Plugins that require custom rules on all your installations as I do, you can add more rules in this .conf file, e.g. I use Yoast SEO on all my websites therefore I am adding the rewrite rules required here, in this way I do not have to copy the same rewrite rules for each server block.
Unlike single site WordPress, which can work with “ugly” permalinks and thus does not need any URL rewrite, a MultiSite installation requires custom rewrite rules to format URLs for your subsites. Let’s create a .conf file applicable to multisite WordPress installations:
sudo nano multisite.conf
This will open an empty file, copy the required rewrite rules:
# Rewrite rules for WordPress Multi-site.
if (!-e $request_filename) {
rewrite /wp-admin$ $scheme://$host$uri/ permanent;
rewrite ^/[_0-9a-zA-Z-]+(/wp-.*) $1 last;
rewrite ^/[_0-9a-zA-Z-]+(/.*\.php)$ $1 last;
}
Save & exit.
Our current working directory is /etc/nginx/global, if you want to change it you can type:
cd /desired_directory
It is time to create our first server block. Since we already have everything configured in our .conf files there is no need to duplicate the default server block file. Let’s disable the default server block:
sudo rm /etc/nginx/sites-enabled/default
And create a server block file:
sudo nano /etc/nginx/sites-available/demo
This will open an empty file, copy the following configurations depending on what you want to achieve:
Imagine that you want to configure a WordPress site with this domain www.demo.com. First we will have to create a server block server {...}
where we will put our rules. We have to specify which server block is used for a given URL, include common.conf & wordpress.conf and finally we will tell NGINX the location of the WordPress installation in our server.
server {
# URL: Correct way to redirect URL's
server_name demo.com;
rewrite ^/(.*)$ http://www.demo.com/$1 permanent;
}
server {
server_name www.demo.com;
root /home/demouser/sitedir;
access_log /var/log/nginx/www.demo.com.access.log;
error_log /var/log/nginx/www.demo.com.error.log;
include global/common.conf;
include global/wordpress.conf;
}
Remember to change the following data to fit your needs:
You can see that there are two server blocks, that’s because www.demo.com & demo.com are different URLs. You probably want to make sure that Google, Bing, users…etc pick the URL that you want, in this case I want my website to be www.demo.com so I have configured a permanent redirect from demo.com to www.demo.com. It is also possible to specify multiple domains:
server {
# URL: Correct way to redirect URL's
server_name demo.com sub.demo.com example.com;
If you want a multisite installation with subdirectories you will need to include the rewrite rules stored in multisite.conf:
# URL: add a permanent redirect if required.
server {
server_name www.demo1.com;
root /home/demouser/sitedir1;
access_log /var/log/nginx/www.demo1.com.access.log;
error_log /var/log/nginx/www.demo1.com.error.log;
include global/common.conf;
include global/wordpress.conf;
include global/multisite.conf;
}
If you want a multisite installation with subdomains you will need to configure this server block to listen to a domain with a wildcard:
server {
server_name *.demo2.com;
root /home/demouser/sitedir2;
access_log /var/log/nginx/demo2.com.access.log;
error_log /var/log/nginx/demo2.com.error.log;
include global/common.conf;
include global/wordpress.conf;
}
If you want to host simple html websites or other webapps you might need to specify custom rules or create more .conf files and include them in the server block:
# URL: add a permanent redirect if required.
server {
server_name www.demo3.com;
root /home/demouser/sitedir3;
access_log /var/log/nginx/demo3.com.access.log;
error_log /var/log/nginx/demo3.com.error.log;
# custom rules
}
Remember to save & exit.
The last step is to activate the host by creating a symbolic link between the sites-available directory and the sites-enabled directory:
sudo ln -s /etc/nginx/sites-available/demo /etc/nginx/sites-enabled/demo
We’ve made a lot of the changes to the configuration. Reload NGINX and make the changes visible.
sudo service nginx reload;
To create additional virtual hosts, you can just repeat the process above, being careful to set up a new document root with the appropriate new domain name each time. It is also possible to combine multiple server blocks in just one file:
server {
server_name demo.com;
rewrite ^/(.*)$ http://www.demo.com/$1 permanent;
}
server {
server_name www.demo.com;
root /home/demouser/sitedir;
access_log /var/log/nginx/www.demo.com.access.log;
error_log /var/log/nginx/www.demo.com.error.log;
include global/common.conf;
include global/wordpress.conf;
}
server {
server_name www.demo1.com;
root /home/demouser/sitedir1;
access_log /var/log/nginx/www.demo1.com.access.log;
error_log /var/log/nginx/www.demo1.com.error.log;
include global/common.conf;
include global/wordpress.conf;
include global/multisite.conf;
}
# More server blocks....
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!
Nice write up. I suggest though instead of:
rewrite ^/(.*)$ http://www.demo.com/$1 permanent;
you use:
return 301 $scheme://www.demo.com$request_uri;
A return is quicker than a rewrite, and $scheme is protocol-independent.
Thank you for your review.
Yes, you are right, I did not consider a return (which seems to be regarded as being faster)
Does this assume that Wordpress is already installed? I’m having trouble understanding the order in which to follow the tutorials. I think I should follow:
Correct?
Remember to configure your DNS to fit your subdomains or with a wildcard (*)
Thanks! I got it running!
Thanks for the excellent write-up! I did everything as you described and it went smoothly but now im wondering if i need an unique ip for each simple wordpress installation. I want to have 5 separated wordpress blogs, each with its own database and wordpress installation files but i dont know if for each server_name i should use a different ip and then use domain mapping. Thats the only thing im not fully understanding. Any help appreciated!
@stk.walshy: server_name should be the blog’s domain name. You don’t need one ip per blog :]
@stk.walshy
You can have multiple domains pointing to an unique IP (your server’s IP) NGINX can serve different “folders (root)” depending on the “domain (server_name)” :
For example,
let’s say that we have domain1.com domain2.com domain3.com domain4.com & domain5.com
Suppose that you want to host them in one of your digitalocean servers
You will need to create 5 Databases
Extract WordPress (Five times since you have 5 blogs) & add the required permissions: a.) /home/demouser/sitedir1;
b.) /home/demouser/sitedir2; c.) /home/demouser/sitedir3; d.) /home/demouser/sitedir4; e.) /home/demouser/sitedir5;
Remember to edit wp-config.php
Configure NGINX so that it knows which folders to serve depending on the domain. You will need to create 5 server blocks and edit server_name & root: server { … server_name domain1.com; root /home/demouser/sitedir1; … } server { … server_name domain2.com; root /home/demouser/sitedir2; … }
Remember to configure your DNS If you have any questions, please feel free to ask.
I must have transposed something and am getting a ‘this page is not redirecting properly’. Any clue where I can start troubleshooting?
I managed to find an error message but cannot see in any of the modified files where I’ve snafu’d: 2014/01/14 12:55:19 [warn] 3456#0: conflicting server name “162.243.236.35” on 0.0.0.0:80, ignored
Any help is appreciated.