Tutorial

How To Configure Single and Multiple WordPress Site Settings with Nginx

How To Configure Single and Multiple WordPress Site Settings with Nginx

Introduction


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.

Requirements


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.

Basic NGINX Optimization


Adjust NGINX Worker Processes & Connections


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;

Enabling Gzip


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.

Creating NGINX .conf files


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:

  • common.conf: Configurations applicable to all sites.
  • wordpress.conf: Configurations applicable to all WordPress sites.
  • multisite.conf: Special configurations for WordPress multisite with sub-directories.

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

common.conf file


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.

wordpress.conf file


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.

multisite.conf file


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.

Small Note


Our current working directory is /etc/nginx/global, if you want to change it you can type:

cd /desired_directory

Creating Server Blocks


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:

Simple WordPress Installation


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:

  • server_name: Determine which server block is used for a given URL.
  • root: The path where your site is stored.
  • access log & error log: Set the paths for your logs

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;

Multisite with Subdirectories


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;
}

Multisite with Subdomains


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;
}

HTML & Other websites


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.

Enabling Server Block Files


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; 

Final Notes


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.

Learn more about us


About the authors

Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
10 Comments


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

include fastcgi_params;

with

include fastcgi.conf;

When I run cat /proc/cpuinfo | grep processor, I get ‘0’ processor. In this case, do I need to change to worker_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:

server {
    server_name www.domain.com;
    return 301 $scheme://domain.com$request_uri;
}

server {
        server_name *.domain.com;
        root /var/www/domain.com;
        access_log /var/log/nginx/domain.com.access.log;
        error_log /var/log/nginx/domain.com.error.log;
        include global/common.conf;
        include global/wordpress.conf;
}

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:

XXX.XXX.XXX.XXX domain.com
XXX.XXX.XXX.XXX www.domain.com
XXX.XXX.XXX.XXX sub1.domain.com
XXX.XXX.XXX.XXX sub2.domain.com
XXX.XXX.XXX.XXX sub3.domain.com

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 files wordress.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

server {
  server_name mysite.com;
  rewrite ^/(.*)$ http://www.mysite.com/$1 permanent;
}
server {
  server_name www.mysite.com;  
  root /var/www/mysite;
  access_log /var/log/nginx/www.mysite.com.access.log;
  error_log /var/log/nginx/www.mysite.com.error.log;
  include global/common.conf;
  include global/wordpress.conf;
}

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 with chmod -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?

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Get our biweekly newsletter

Sign up for Infrastructure as a Newsletter.

Hollie's Hub for Good

Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.

Become a contributor

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

Welcome to the developer cloud

DigitalOcean makes it simple to launch in the cloud and scale up as you grow — whether you're running one virtual machine or ten thousand.

Learn more
DigitalOcean Cloud Control Panel