How To Setup A Complete Nginx Web Server

This tutorial covers everything you need to setup a complete linux based Nginx web server stack – Nginx / PHP / MariaDB / Redis. Whether you’re looking to run a fully custom site or something based on WordPress or another CMS, this guide will help you get your server up and running in no time.

While the bulk of the information regarding configuring Nginx will apply to any Nginx install, the scripts / instructions specific to installing the software will only work on Debian / Ubuntu.

Before We Begin

Important information you need before you proceed.


There are a few things you’ll need to have in advance to complete this tutorial.

  • You need a domain / subdomain. If you haven’t already registered a domain and need a registrar, I highly recommend Namecheap (affiliate) as an affordable registrar. Every domain includes the option for free private registration – nobody wants to get harassed because their domain info is public and you shouldn’t have to pay extra for the privilege.
  • You need to know how to configure DNS for your domains / subdomains.
  • You need somewhere to host your web server – either in your own network or in the cloud. If you’re in need of a cloud server, I can personally recommend both Linode (affiliate) and DigitalOcean (affiliate) as quality service providers.

If you’re running your server from your home network, consider putting a reverse proxy in the cloud. Instead of DNS pointing to your home IP address it would instead point to the cloud proxy. I do exactly this for the services I host in my own homelab.

Let me know in the comments, below, if you’re interested in knowing how I do this without having to open up firewall ports on my home network.

Git Repository

This tutorial makes use of a bash script and configuration files I’ve collected into a git repository specifically for the use when setting up a Nginx server.

Git repository screenshot

I hate duplicating effort. You could also say that I have a preference for automating tasks – especially tasks that I’m going to end up doing multiple times, like server configurations. Simply put – I don’t want to do things manually every time I need to setup a server. Taking this approach saves me time and ensures that I don’t screw something up.

Because I’ve already tested that this script works as intended, I can be certain that future servers deployed using it are going to work properly.

If you’re the type that likes to know all the details of how to do something – I get you. I go over the heart of the script for those who’d prefer to do things themselves.

Nginx webserver script screenshot

If you find yourself wondering what is happening or why I’ve done something the way I’ve done it – please leave a comment below and I will do my absolute best to explain. I’m always open to answering questions.

With that said – let’s get started.

Cloning The Repository

Before you can clone the git repository, you’ll need to ensure that git is installed on your system.

sudo apt install git -y

Once you’re certain git is installed, clone the repository to your system with this command.

git clone

Next change to the directory that was created.

cd setup-nginx-webserver

If you take a listing of the files, you should see they match the files in the git repository.

ls -l

When you’re done, your terminal should look similar to this.

Clone Git Repository & File Listing

Run The Setup Script

From the setup-nginx-webserver directory you’re able to run the script. Important – the script checks if you’ve run it with root privileges so be sure you use sudo if you’re not already root.

sudo ./

The script will check for root permissions. If you forget it will tell you.

Setup Script Basics

Besides deploying my custom configuration files, the setup script does four basic things.

1 – Update The System

sudo apt update && sudo apt upgrade -y && sudo apt autoremove -y && sudo apt clean

2 – Install Packages

sudo apt install ntp sed git curl zip unzip nginx redis certbot ufw php-fpm php-curl php-mysql php-mbstring php-xml php-gd php-redis php-zip php-imagick php-bcmath php-intl php-tokenizer -y
sudo apt install mariadb-server -y

The PHP packages included in the script are the ones I require most often for hosting WordPress websites. Add additional packages or remove packages as necessary for your needs. You can opt to not install certbot if you intend to obtain SSL certificates from another source. Likewise, if you don’t have need for redis, curl, or other additional packages I’ve included you could opt to not install any of them as well.

3 – Secure MariaDB

sudo mysql_secure_installation

4 – Setup Firewall Ports

sudo ufw allow 80/tcp
sudo ufw allow 443/tcp


While the script has specified ports to open in the firewall, I intentionally left out enabling the firewall. If you have additional ports that need configured – such as your SSH port (default is port 22) that allows you to remotely access the system – you’ll want to be sure they’re configured before you enable the firewall.

Once you’re ready, enable the firewall with this command.

sudo ufw enable

Having the software installed and telling UFW what ports to allow will only get you so far.

Configuration Files

As I’ve mentioned, the setup script copies into place my modified and custom config files that I deploy with my systems – saving me from having to setup the configurations myself. If you’ve opted to use my config files, they can save you some headache – especially the real ip configs.

Here are some brief details on the config files.


The modifications I’ve made to this file are mostly for supporting web font mime types. This is useful for self hosting web based fonts instead of loading them from Google.


My modified nginx.conf file increases the number of Nginx worker connections to 1024 – allowing for improved performance without becoming overwhelming on other system resources – as well as increases the client max body size to 8MB – allowing for larger content requests than the default.

Additionally, I have setup – yet disabled – two additional optional config files you can use if your system is behind a reverse proxy to ensure the proper source IP address is forwarded to your servers – nginx-cloudflare-realip.conf and nginx-internal-realip.conf.

To enable either of these, uncomment the appropriate line.


As I just mentioned above, I have setup three optional config files for use behind a reverse proxy. Without the necessary real IP configuration in place your server logs will show all of it’s traffic as having originated with the reverse proxy instead of the originating IP.

To enable one of these configs, uncomment the appropriate line in nginx.conf. Here’s a quick explanation on when to use which config.


This one is simple enough to explain. Enable this configuration if your server is behind Cloudflare’s reverse proxy.


Enable this configuration if your server is behind a reverse proxy on your private 10.x.x.x, 172.x.x.x, or 192.168.x.x network.


Modifications to php.ini include increasing post_max_size and upload_max_filesize to 8MB as well as upping memory_limit to 256MB.

Example Configurations

Now comes the really important part of setting up your system – setting up server blocks. My setup script places two example files in the /etc/nginx/sites-available directory that you can use as templates for configuring your server.

One important feature I’ve added to my example configurations are security focused headers to help prevent things like cross-site scripting attacks or embedding the page into a third party frame.

I explain how to use one of these two files, example-redirect-80, below. This example shows how to setup a domain on your server such that unencrypted HTTP on port 80 gets redirected over to encrypted HTTPS on port 443. The other file, example-standard, works in the same basic way but without the redirect.


Let’s start by examining the configuration example. I’ve commented out the lines in this configuration that need modification so that they’re easily identified when you’re working with the example file on your server. Be sure to uncomment them after you make adjustments.

Let’s quickly go over what needs modified.

server {
	listen 80;
	listen [::]:80 ipv6only=on;
#	server_name www.yourdomain.ext yourdomain.ext;
#	return 301 https://$host$request_uri;
server {
	listen 443 ssl http2;
	listen [::]:443 ipv6only=on ssl http2;
	keepalive_timeout 70;

#        server_name www.yourdomain.ext yourdomain.ext;
#        ssl_certificate /etc/letsencrypt/live/yourdomain.ext/fullchain.pem;
#        ssl_certificate_key /etc/letsencrypt/live/yourdomain.ext/privkey.pem;
#        root /var/www/sitedir/;

	index index.php index.html;
	http2_push_preload on;

	add_header Strict-Transport-Security "max-age=15768000; includeSubDomains;";
        add_header X-XSS-Protection "1; mode=block";
        add_header X-Frame-Options "SAMEORIGIN";
        add_header X-Content-Type-Options nosniff;

	location = /favicon.ico { log_not_found off; access_log off; }
	location = /robots.txt { allow all; log_not_found off; access_log off; }
	location ~ /\. { deny all; }
	location ~* /(?:uploads|files)/.*\.php$ { deny all; }
	location / { try_files $uri $uri/ /index.php?q=$uri&$args; }
	location ~*  \.(ico|pdf|flv|jpg|jpeg|png|gif|js|css|swf|svg|ttf|woff2)$ { expires 365d; log_not_found off; }
	location ~ ^/\.user\.ini { deny all; }
	location ~ /\.ht { deny all; }

	error_page 404 /404.html;
	error_page 500 502 503 504 /50x.html;
	location = /50x.html { root /usr/share/nginx/html; }

	location ~ \.php$ {
		fastcgi_pass unix:/run/php/php&&&-fpm.sock;
		fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
		fastcgi_buffers 16 16k;
		fastcgi_buffer_size 32k;
		include fastcgi_params;
		include snippets/fastcgi-php.conf;


Anywhere you see www.yourdomain.ext or yourdomain.ext should be replaced with whatever domain you’re setting up on the server.

If, for example, you’re setting up a server for what I’ll term a primary domain – for example – it’s probably best you setup both the domain and the www subdomain wherever you specify a server_name, as I’ve shown in the example, to account for the large number of people who insist on typing in www for any website as if it were a requirement for it to work.

If you’re using a subdomain – like – you only need to specify the subdomain itself.


This one is simple enough – replace it with the website’s directory. I tend to match the directory to the domain it corresponds to. If the domain is, I’d use this for the directory. How you choose to name the directory is up to you.


If you’ve run my install script, every instance of &&& in my config files is automatically replaced with the version of PHP that comes with your distro. If you’re setting things up manually, you’ll need to be sure to do this manually. You can refer to the install script for additional details if needed.

Configuring A Website

Let’s take everything we’ve gone over above and see how to put it all to use.

Hypothetical Scenario

To help illustrate how to put all of this together, let’s consider this hypothetical scenario.

We’re going to setup a website in Nginx for the domain You’ve decided to use Certbot to obtain SSL certificates for your domain. Additionally, let’s assume that your domain is using Cloudflare for its DNS but with the Cloudflare reverse proxy disabled.

Enabling the Cloudflare reverse proxy creates extra configuration necessary to allow Certbot to function properly – which would greatly increase the length of this tutorial. If you’re intending to use Certbot behind Cloudflare’s reverse proxy, I’ve written a tutorial that shows you what you need to know to get this working.

1 – Setup DNS For Your Domain / Subdomain

While I can’t go over the exact interface for every service that might be hosting your DNS, the principles will be the same regardless.

In short, you’ll need what’s called an A record setup for your domain which will point to the IP address of the system hosting your reverse proxy. On Cloudflare, it would look something like this:

Cloudflare DNS Example

For our hypothetical example I specified that we’re going NOT using Cloudflare’s reverse proxy so their proxy is disabled in my settings.

2 – SSL Certificates

Before you setup your website in Nginx, you need to setup your SSL certificates. If you don’t have your certificates in place first, your configuration test will fail.

To obtain a free certificate for the example domain using Certbot from Let’s Encrypt, you’d run this command:

sudo certbot certonly --standalone -d

Certbot will ask you to provide an email address to send renewal and security notices to. Additionally, you’ll be required to accept the terms of service. Lastly, you’ll be asked if you’d like to join the EFF mailing list.

Certbot Example - 1 of 2

If everything goes without issue you’ll be congratulated and notified where your certificates have been stored. Certbot will always place your certificates in a directory named for the domain they were issued to.

Certbot Example - 2 of 2

For our example, the certificate files are stored in /etc/letsencrypt/live/ As I already demonstrated earlier, my example Nginx configuration files have this directory structure already in place where all you need to do is replace the example domain with the actual one.

3 – Setup Your Server Blocks

To setup your server blocks, start by creating a copy of the example file found in /etc/nginx/sites-available on your server. To change to that directory run:

cd /etc/nginx/sites-available

Once there, you can get a listing of the files in the directory with:

ls -l

Which should return a result similar to this:

Sites Available Initial Directory Listing

Copy the example file to a new file specific to what you’re going to reverse proxy. For our hypothetical example domain – – you might do something like this:

sudo cp example-redirect-80

Which should return a result like this when you list the files again:

Sites Available Directory Listing After File Copy

You can edit the new file using the nano editor like this:

sudo nano

When you’re finished making the necessary modifications, press CTRL + X on the keyboard and when prompted to save press Y for yes. The resulting configuration should look similar to this:

Nginx Web Server - Example Server Blocks

NOTE: If you’re configuring more than one website on your Nginx server, you’ll need to comment out listen directives for ipv6 on all but one of the sites. Nginx will complain if you try to enable it for more than one.

While this creates the configuration file with your server blocks, you’ll now need to enable it. Nginx will only actively use configurations that are found in /etc/nginx/sites-enabled. To enable your new config, we’re going to create a symbolic link to the file in the sites-enabled directory.\

Change to the sites-enabled directory:

cd ../sites-enabled/

Then run:

sudo ln -s ../sites-available/ .

You can verify that this worked by listing the files in the sites-enabled directory:

ls -l ../sites-enabled

Which should return results like this:

Symbolic Link + Sites Enabled Directory Listing

Now that you’ve added your configuration to sites-enabled, it’s time to test that your configuration works.

4 – Check Your Nginx Config Changes

Nginx includes a command for testing that your configuration is correct before you attempt to make the changes you’ve made live. From the terminal run:

sudo nginx -t

If everything went smoothly, your result should look similar to this:

Nginx Configuration Test Success

However, if Nginx detected a problem with your configuration it will give you an error and tell you on what line of what file the error exists for you to attempt to correct it. For example:

Nginx Configuration Test Failed

After you’ve made corrections, test your configuration again. When you’ve got no configuration errors, you’re ready now to enable your changes.

5 – Enable Your Nginx Config Changes

To enable your new Nginx configuration run:

sudo systemctl restart nginx

To test that everything is working properly run:

sudo systemctl status nginx

If Nginx is working you’ll get a result similar to this:

Check Nginx Status

With the changes applied, it’s time to make sure your system is working as intended.

6 – Website Directory / Files

The last thing necessary before testing that everything actually works is to create the directory that holds your website’s files. For this tutorial, we’re going to create a copy of the default html directory that comes with Nginx and rename it’s index file to something our configuration will accept.

First change to the /var/www directory:

cd /var/www

If you look at the existing files / directories you’ll see the default html directory:

ls -l

Copy the html folder to as we specified in our config:

sudo cp -R html

Check the file listing again:

ls -l

Last, we need to set the correct file ownership in order for Nginx to be able to properly work with the files.

sudo chown -R www-data:www-data *

If you check the file listing again, you’ll notice the file owner and group have changed.

ls -l

When you’ve got this far, your terminal should resemble this:

Create Website Directory

Next, change into the directory:


If you look at the file listing you’ll find the default index file:

ls -l

You’ll notice mine is named index.nginx-debian.html. If you’re using Ubuntu or any other Linux distro, your file might be named something different. For this file to work, we need it to be named index.html:

sudo mv index.nginx-debian.html index.html

Check the file listing again to verify the name is correct:

ls -l
Rename Default Nginx Index File

All files for your website will ultimately go into this directory. As you add additional files, you need to be certain to set the file ownership on the new files as you did a few commands above.

You’re now ready to test that everything is working.

7 – Testing

To test that everything is working, simply browse to the domain you’ve configured in your reverse proxy. For our example, it’s Please note that I’ve not left this example site running after completing the creation of this tutorial.

Default Nginx Page

If you get any result besides the default Nginx server test page, you’ll need to go back and check the configuration again.


You should now have a complete web server ready for most anything you want to deploy to it – from custom designed sites to WordPress or any other web based applications. As always, if you have questions regarding this tutorial, please don’t hesitate to ask in the comment section below. I’m always willing to help explain something better if needed.

If you found this tutorial helpful and would like to support our efforts to create additional resources like this, please consider making a donation. Your support is greatly appreciated!

If you can’t make a donation, please consider sharing this tutorial with others who may be interested. Thanks for reading, and I hope you visit again soon!

2 thoughts on “How To Setup A Complete Nginx Web Server”

  1. Moustapha MBAYE

    Hi Robert,

    Thank you for this very good article

    I would like to know how you did it with the reverse proxy without opening any port on your firewall?

  2. Hi Robert,
    Really great tutorial. I would also like to know how you set up the reverse proxy without opening firewall ports. Thank you!

Leave a Comment

Your email address will not be published. Required fields are marked *

I accept the Privacy Policy

Scroll to Top