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.
I’ve changed the wordpress.conf file because a blank page in wordpress.
replacing
with
When I run
cat /proc/cpuinfo | grep processor
, I get ‘0’ processor. In this case, do I need to change toworker_processes 0;
in ```/etc/nginx/nginx.conf````?Really nice guide, less files and more power :D
Thank you for this Tutorial @SantiagoTi .
I am a beginner and have a question. Under the “Creating Server Blocks” area:
Wouldn’t ‘sudo nano /etc/nginx/sites-available/demo’ be trying to write server block code into a directory?
I am using CentOS7, so maybe the installation root is different. Is it normal to write configuration to a directory in Ubuntu?
Thank you for your time.
Hi folks,
Great tutorial, it has worked fine for me until I’ve tried to set up Multisite.
Here’s my situation:
I’m deploying a sub-domain Multisite installation that I have been working on locally using XAMPP (note Apache). My URL structure is as follows:
I have deployed my code and amended wp-config.php (that was originally set-up to be compatible with Apache) and replaced domain.dev with domain.com here:
define('DOMAIN_CURRENT_SITE', 'domain.com');
I have amended all
wp_*_options
,wp_options
,wp_site
and any other tables I deemed important with the amended domain.com domain.I have configured nginx as follows:
Note that I am wanting to redirect any www requests to domain.com
Finally, I have configured my local hosts file to read as follows:
When I attempt any domain I am redirected to another WordPress (not multisite) site on my server, namely the first site in the list in
sites-available
As far as I can tell I have followed everything laid out in this tutorial but unfortunately not getting anywhere!
Oh and my nginx error logs are barren!
Any ideas, anyone?
Much appreciated :)
excellent, everything works … my wordpress ta perfectly in the air. but I have 4 more domains in the air aimed at the servers names of digital ocean, all of them when I type in the browser, always carries the same site, always my only installation … which install more web and how do I each loading up different facilities wordpress?
I followed your guide and all has appeared to be fine, however I have a strange issue whereby the URL’s for internal notification links do not include /network/. Therefore when clicking on them to say install a required plugin, visit a configuration page, activate a plugin etc. I will get a message similar to “You do not have sufficient permissions to access this page.”
Now I know this is not a plugin conflict (everything disabled, uninstalled including themes and different plugin/themes attempted, same issue occurs). I cannot see any viable way it could be a wordpress configuration issue and it certainly is not a privileges problem (as it clearly is the “/network/” part of the URL path that is missing). So I’m guessing it is an nginx routing issue, but I truly cannot find the source, or am I looking in the wrong place?
I am not able to get this to work with just one wordpress site. Eventually I want to have 2 Wordpress sites on 2 different directories on the same server. I included all 3
.conf
fileswordress.conf
common.conf
&multisite.conf
just like it says in the tutorial.My file in
/etc/nginx/sites-enabled/mysite
that is symlinked from/etc/nginx/sites-available/mysite
When I visit my site URL I just get a
web page not available
I configured the
wp-config.php
file as well. Created my MySQL user and made sure the user has access to the database. Also changed the permissions of/var/www/mysite
withchmod -R 755 .
And I have nothing on my logs: No
/var/log/nginx/www.mysite.com.access.log
nor/var/log/nginx/www.mysite.com.error.log
files have been created. Thanks for the help.how to make a few sites with multiple Wordpress?