A Friendly Introduction to NGINX

NGINX is a powerful web server. Right. If you’re here you probably know that already so I won’t spend more time convincing you it is also a noteworthy reverse proxy and load balancer. Many have reported the lack of a simple serial and step by step guide to help a complete beginner feel comfortable with NGNIX, in this guide, we set out to provide one and at the end, we do hope, you’ll be able to more confidently manage your NGINX instance and make critical decisions particular to your needs.

Get it installed.

While there are binaries for windows, it is not recommended to run it on windows as you miss out on tons of features and conveniences considering how easy it is to install linux on your windows machine using virtualization technologies like virtualbox.  I will be using an instance of Ubuntu16.04 running inside virtualbox on Windows7 64bit. That’s all about my environment. In any of the following sections, do choose the options relevant to your environment

We will be installing from the debian packages as described at the official guide. Head into one directory into your box and download the package key

root@ubuntu16:/home/gakwaya/myfiles# wget http://nginx.org/keys/nginx_signing.key
root@ubuntu16:/home/gakwaya/myfiles# ls -al

drwxrwxr-x  2 gakwaya gakwaya 4096 6月   2 13:59 .
drwxr-xr-x 19 gakwaya gakwaya 4096 6月   2 09:30 ..
-rw-rw-r--  1 gakwaya gakwaya 1561 6月  17  2016 nginx_signing.key

This downloads the key and saves it in a local file. This key will be used to let our system trust where the nginx packages will be coming from.add it to the apt program keyring with the following command:

sudo apt-key add nginx_signing.key

Now open the /etc/apt/sources.list file and add the following lines at the end :

deb http://nginx.org/packages/mainline/ubuntu/ codename nginx
deb-src http://nginx.org/packages/mainline/ubuntu/ codename nginx

The code name is dependent on your linux distribution as described here.  Now only remains updating your sources and doing the actual install of nginx using the command below :

apt-get update
apt-get install nginx

After the installation is done nginx should auto start. In case it doesn’t, just issue the

sudo service nginx start

command and it should be up and running. Test if your environment is working by typing the IP address of your linux box into the browser and you should see a page like shown below .

We are now done with the installation and time for some actual nginx stuff.

Basic Configurations

As an nginx system administrator, you will spend most of your time tweaking configuration files. Naturally you will want to know the location of these files for nginx. In standard installations like the one we did, the configuration files are located in /etc/nginx. Head in there and list the contents of the directory and you should see something like :

root@ubuntu16:/home/gakwaya/myfiles# cd /etc/nginx
root@ubuntu16:/etc/nginx# ls -alh
total 56K
drwxr-xr-x   3 root root 4.0K 6月   2 10:54 .
drwxr-xr-x 133 root root  12K 6月   2 09:11 ..
drwxr-xr-x   2 root root 4.0K 6月   1 18:05 conf.d
-rw-r--r--   1 root root 1007 4月  12 22:46 fastcgi_params
-rw-r--r--   1 root root 2.8K 4月  12 22:46 koi-utf
-rw-r--r--   1 root root 2.2K 4月  12 22:46 koi-win
-rw-r--r--   1 root root 3.9K 4月  12 22:46 mime.types
lrwxrwxrwx   1 root root   22 4月  12 23:48 modules -> /usr/lib/nginx/modules
-rw-r--r--   1 root root  784 6月   2 10:45 nginx.conf
-rw-r--r--   1 root root  643 6月   1 17:55 nginx.conf.backup
-rw-r--r--   1 root root    0 6月   1 21:23 off
-rw-r--r--   1 root root  636 4月  12 22:46 scgi_params
-rw-r--r--   1 root root  664 4月  12 22:46 uwsgi_params
-rw-r--r--   1 root root 3.6K 4月  12 22:46 win-utf
root@ubuntu16:/etc/nginx#

The main configuration file you should focus on isnginx.conf

. As we are going to wipe it out and try our own configurations, it is a good idea to save a backup of it as shown below.

cp nginx.conf nginx.conf.backup

Now we have something to come back to if we need to. Open the nginx.conf file just to see what is inside.

user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;
	 sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    include /etc/nginx/conf.d/*.conf;
}

There is a lot going on for the beginner and you might be understandably frustrated by how all this fits together. Note the line

include /etc/nginx/conf.d/*.conf;

right at the end of the configuration file .This line includes other configuration files that located in the/etc/nginx/conf.d/ directory. Head in there and you see the files below :

root@ubuntu16:/etc/nginx/conf.d# ls -al

drwxr-xr-x 2 root root 4096 6月   1 18:05 .
drwxr-xr-x 3 root root 4096 6月   2 14:40 ..
-rw-r--r-- 1 root root 1097 4月  12 23:48 default.conf
-rw-r--r-- 1 root root 1097 6月   1 18:05 default.conf.backup

The directory contains a default.conf . We saved a backup of the file just in case. Open the file and you should see the content similar to what is shown below

server {
    listen       80;
    server_name  localhost;

    #charset koi8-r;
    #access_log  /var/log/nginx/log/host.access.log  main;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }

    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

    # proxy the PHP scripts to Apache listening on 127.0.0.1:80
	 #
    #location ~ \.php$ {
    #    proxy_pass   http://127.0.0.1;
    #}

    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    #location ~ \.php$ {
    #    root           html;
    #    fastcgi_pass   127.0.0.1:9000;
    #    fastcgi_index  index.php;
    #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
#    include        fastcgi_params;
    #}

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    #location ~ /\.ht {
    #    deny  all;
    #}
}

Now why all this jumping around between files? nginx.conf  and default.conf ? It is done like this to actually guide you into writting good and compact configuration files but more on that later. For now go back to nginx.conf get ride of all the content and paste in the code below

events{}

http {
	server {
		listen       80;
		server_name  localhost;

		location / {
			root   /usr/share/nginx/html;
			index  index.html index.htm;
		}
	}
}

Save the file. Every time you change something in the configuration, nginx gives you a way to test that there are no typos. You just type the command

 
nginx -t

If there is an error, it tells you right away and points you to the line number. When the configuration is with no errors, for it to take effect you need to reload nginx with the command

service nginx reload

Now if you go to your browser and visit the IP address of your linux box, you will see the same nginx welcome page. The important thing now it that the configuration file has been reduced to the possible minimum for you to understand. We will start here and build our nginx knowledge gradually.

Contexts and Directives

nginx configuration files are broken into contexts and directives.

Context is represented by the boxes in the image above, they are like scope in programming languages and define a region where some setting has effect. Contexts have a parent child relationship. The root parent is the unnamed file context represented by the outer red box and it has two direct children mainly events{} and http{}, http in turn has a server{} child and server{} itself has a location child context.

Directives are key value pairs that are defined inside contexts that control some behaviour on the server. a good example is server_name localhost that defines the host that this server block is going to control. For those coming from programming languages beware, there is no colon between key and value and directives are terminated by a semicolon.

Virtual hosts

The http block conveniently specifies that the contents inside it are related to serving http content. Inside the http block we usually have one or multiple server blocks. Server blocks are logical representations of the virtual host, they are directly tied to IP addresses or domain names. Three paramenters directly come to mind when configuring any virtual host: a domain name or IP address, a port number (http is usually serven on port 80) and a location on the file system where the actual website files ( html, css, javascript…) are stored.The listen directive allows you to specify a port number for the virtual host, the server_name directive lets you specify an IP address ( or domain name ) and using the root directive you specify where your website files are located.

our root directive specifies that our website files are stored at

/usr/share/nginx/html;

and if we go there we do find an index.html file

root@ubuntu16:/etc/nginx# cd /usr/share/nginx/html/
root@ubuntu16:/usr/share/nginx/html# ls -al
drwxr-xr-x 3 www-data www-data 4096 6月   2 09:57 .
drwxr-xr-x 3 root     root     4096 6月   1 17:37 ..
-rw-r--r-- 1 www-data www-data  537 4月  12 22:46 50x.html
-rw-r--r-- 1 www-data www-data  612 4月  12 22:46 index.html

that contains the html that was returned every time we visited our linux box IP address in the browser. This hopefully exposes something important about the flow of working with nginx to server web content. You put your web project files ( html,css,…) somewhere on the filesystem on your server and you give nginx directory (root) where it will start when looking for assets requested by clients(browsers). The rest is really tweaking and controlling how that happens as we will see in the sections to follow.

Location Blocks

We have seen a location block

location / {
			root   /usr/share/nginx/html;
			index  index.html index.htm;
}

but we haven’t really touched more on it to understand more about it. Location blocks allow you to match different strings in the request uri and do something when a match occurs. To give a more particular example, lets say you have  a website located at YOUR.IP.ADDRESS and you want to reply with a “Hello there” message when someone visits YOUR.IP.ADDRESS/hello . With nginx you can easily achieve this with

events{
}

http {
	server {
		listen       80;
		server_name  192.168.56.101;

		location / {
			root   /usr/share/nginx/html;
			index  index.html index.htm;
		}
                location /hello {
			return 200 'Hello there';
		}
       } 
}

Test if you have no typos in the configuration file and reload nginx for changes to take effect. Now if you visit 192.168.56.101/hello  we get the friendly message as expected.

What we have done is known as prefix match in nginx.When the browser sends 192.168.56.101/hello , the server recognizes the appended /hello and it finds its closest match to be location /hello and the directives inside that block take effect. We return a status code of 200 and a body of ‘Hello there’.

This is called prefix match because it matches everything that starts with /hello , if you typed in the browser 192.168.56.101/hellorwanda ,you would get the same ‘Hello there’ response.

Besides prefix match, we also have exact matches. Consider the configuration file below :

events{
}

http {
	server {
		listen       80;
		server_name  192.168.56.101;

		location / {
			root   /usr/share/nginx/html;
			index  index.html index.htm;
		}

                # Prefix match
                location /hello {
			return 200 'Hello there';
		}
                #Exact match

                location = /exact {
			return 200 'Hello there : Exact match here';
		}
      } 
}

apply it in your configuration, save and nginx -t to see if the syntax is ok and service nginx reload for changes to take effect. If you visit http://192.168.56.101/exact, you get the friendly

'Hello there : Exact match here'

message,if you just add even one character, say visit http://192.168.56.101/exactly , the server conveniently gives you a 404 Not found. You have to exactly match /exact for the location = /exact block to be hit.

Now that you have a good feel about how matches work, lets quickly introduce regex matches. A configuration files says better

events{
}

http {
	server {
		listen       80;
		server_name  192.168.56.101;

		location / {
			root   /usr/share/nginx/html;
			index  index.html index.htm;
		}

                # Prefix match
                location /hello {
			return 200 'Hello there';
		}
                #Exact match

               location = /exact {
			return 200 'Hello there : Exact match here';
		}

                #Regex match [case sensitive]

                location ~/regex[0-9] {
			return 200 'Hello there : Regex case sensitive match here';
		}

                #Exact match [case insensitive]

                location ~* /regexcaseinsensitive {
			return 200 'Hello there : Regex case insensitive match here';
		}
      } 
}

The comments in the config file say it all, if you visit http://192.168.56.101/regex , you’ll get a 404 because the regular expression is not matched, if you visit http://192.168.56.101/exact123 though, you will get the

'Hello there : Regex case sensitive match here';

message as our regular expression matches regex followed by any number. But  http://192.168.56.101/Exact123 will also give us a 404 as our location block is case sensitive. If you want to match case insensitively use location ~* /stringToMatch as shown in the configuration. You can test and see how it plays out for you. The last type of location matching we’ll look at is preferential prefix match.

events{
}

http {
	server {
		listen       80;
		server_name  192.168.56.101;

		location / {
			root   /usr/share/nginx/html;
			index  index.html index.htm;
		}

                # Prefix match
                location /hello {
			return 200 'Hello there';
		}
                #Exact match

                location = /exact {
			return 200 'Hello there : Exact match here';
		}

                #Regex match [case sensitive]

                location ~/regex[0-9] {
			return 200 'Hello there : Regex case sensitive match here';
		}
                #Exact match [case insensitive]

                location ~* /regexcaseinsensitive {
			return 200 'Hello there : Regex case insensitive match here';
		}

               #Preferential prefix match

                location ^~ /prefprefix[0-9] {
			return 200 'Hello there : Preferential prefix match here';
		}
      } 
}

Preferential prefix match is like prefix match but has priority over regex. If this block and some other regex block match one url simultaneously, prefix preferential match is used.

Uri matching priority

When multiple location blocks match a uri simultaneously, which may happen, some  priority rules must be applied to resolve the conflict.

1. Exact match ( = )
2. Preferential Prefix Match ( ^~ )
3. Regex match ( ~ and ~* )
4. No modifier (Prefix match)

Exact match has high priority and Prefix match comes last with low priority. When regex case sensitive and case insensitive conflict, the first to come (top) wins.

Template for practical regx url matching

location ~ \.php${

}

This regex location matches anything that ends in .php. If you want to match multiple files, let say images like png,jpg,bmp,gif, you would do something like

location ~ \.(png|jpg|bmp|gif)$ {
}

this matches any request ending in .png, .jpg, .bmp or .gif. Of course there are many other regex matching goodies out there but these
should get you far enough.

A more practical example

Configure multiple domains to be served by nginx

Most of sys admins out there own a couple of domain names and times and times we need to run different services under these domains. Nginx can help you big time. Consider the configuration file below.

events{

}

http {

    server {
        listen       80;
        server_name  example.com www.example.com;

        location / {
            #       root   /usr/share/nginx/html;
            #       index  index.html index.htm;
            return 200 'Visiting www.example.com';
        }
    }

    server {
        listen       80;
        server_name  coolsite.com www.coolsite.com;

        location / {
            # root   /usr/share/nginx/html;
            #index  index.html index.htm;
            return 200 'Visiting www.coolsite.com';
        }
    }
}#End of http context

We are now trying to match real domain names example.com and coolsite.com . To be able to test that in a local test environment, we need some way to resolve these domain names to our  192.168.56.101 IP address our nginx instance is running on. On Windows systems, there is this file located at C:\Windows\System32\drivers\etc\hosts that allows you to resolve domain names localy. You basicaly specify an IP Adress and a domai name you want it to be matched to. Its easier when you see for yourself.

#
# For example:
#
#      102.54.94.97     rhino.acme.com          # source server
#       38.25.63.10     x.acme.com              # x client host

# localhost name resolution is handled within DNS itself.
#	127.0.0.1       localhost
#	::1             localhost

192.168.56.101 www.example.com
192.168.56.101 example.com
192.168.56.101 www.coolsite.com
192.168.56.101 coolsite.com

Adjust your resolutions as you need and save the file. If you open a local terminal and ping one of the domains we specified, we get replies and you see our IP addresses resolved perfectly

You can go to the browser as of now and visit www.example.com or www.coolsite.com and you will see the responses we expect. But most browsers do cache requests and I hate to be constanly clearing the cache to see the effects of my change. I am going to switch tools and use a command line http client curl . If you don’t have it, you can download it and put the binary somewhere it is seen by the system like in C:\Windows\System32 . This tool is also better when working with http as it has ways to let you see http headers and other cool stuff.Running the command curl -v www.example.com gives us the results below

[2017-06-03 09:17.39]  ~
[gakwaya.iwange] ➤ curl -v www.example.com
* Rebuilt URL to: www.example.com/
*   Trying 192.168.56.101...
* TCP_NODELAY set
* Connected to www.example.com (192.168.56.101) port 80 (#0)
> GET / HTTP/1.1
> Host: www.example.com
> User-Agent: curl/7.50.3
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.12.0
< Date: Sat, 03 Jun 2017 01:17:50 GMT
< Content-Type: text/plain
< Content-Length: 24
< Connection: keep-alive
<
* Curl_http_done: called premature == 0
* Connection #0 to host www.example.com left intact
Visiting www.example.com

The output starting with * is for you to see what is happening. The output starting with > is what is actualy sent to the server as the request and the output starting with < is the reply. At the end you just the the request body saying ‘Visiting example.com’ coming directly from our server configuration! You will see similar results if you visit www.coolsite.com as well.

The client request is coming in and nginx is noticing that the client is looking for www.example.com . The server block

server {
        listen       80;
        server_name  example.com www.example.com;

        location / {
            #       root   /usr/share/nginx/html;
            #       index  index.html index.htm;
            return 200 'Visiting www.example.com';
        }
    }

is then matched and the location block inside it is being matched (because / prefix match matches everything under that domain and the return directive inside is being evaluated. We could uncomment the root and index directives inside and comment out the return one and our server would start serving static content under that root. Try it out!

Redirect from one domain to another

Sometimes you want that when users visit say coolsite.com they are automatically redirected to www.example.com. This can be achieved through the configuration file below

user www-data www-data;
events {
}
http {
    include mime.types;
    server {
        listen 80 default_server;
        root /usr/share/nginx/html;
        index index.html index.htm index.php;
        server_name example.com www.example.com;
        location / {
            try_files $uri $uri/ /index.php$is_args$args;
        }
        location ~ \.php$ {
            # NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini
            fastcgi_split_path_info ^(.+\.php)(/.+)$;
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
            # With php7.0-fpm:
            fastcgi_pass unix:/run/php/php7.0-fpm.sock;
            include fastcgi_params;
        }
    }
    server {
        server_name www.coolsite.com coolsite.com;
        return 301 $scheme://www.example.com$request_uri;
    }
}

The redirect is taken care of by the highlighted block. If you visit coolsite.com , you will be now redirected as shown below

[2017-06-03 17:40.02]  ~
[gakwaya.iwange] ➤ curl -I coolsite.com
HTTP/1.1 301 Moved Permanently
Server: nginx/1.12.0
Date: Sat, 03 Jun 2017 09:40:09 GMT
Content-Type: text/html
Content-Length: 185
Connection: keep-alive
Location: http://www.example.com/

Logging

 

events {

}
http {

     include mime.types;
     server{
         listen 80;
         server_name localhost;
         root html;
 
         location /downloads {
             #Define the location for error logs .
             error_log /var/log/nginx/downloads.error.log;
             access_log /var/log/nginx/downloads.access.log;
 
             #Can also turn off logging in some context
             access_log off;
             error_log off;
             root /sites;
             try_files $uri =404 ;
         }

     }

}

Logging settings allow you to fine tune how and where your logs are saved. You can log when things happen using access_log ,  and when things go wrong with error_log. Logging also follows parent child context inheritance as in the log settings specified in parent contexts are inherited by child contexts unless specifically overridden in child contexts. For example to see the latest logs from your server you can print out the last part of the access.log file as shown below

gakwaya@ubuntu16:~$ tail /var/log/nginx/access.log
192.168.56.1 - - [03/Jun/2017:08:42:14 +0800] "GET /favicon.ico HTTP/1.1" 200 24 "http://www.example.com/" "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36"
192.168.56.1 - - [03/Jun/2017:08:42:50 +0800] "GET / HTTP/1.1" 200 24 "-" "curl/7.50.1"
192.168.56.1 - - [03/Jun/2017:08:43:32 +0800] "GET / HTTP/1.1" 200 25 "-" "curl/7.50.1"
192.168.56.1 - - [03/Jun/2017:09:09:36 +0800] "GET / HTTP/1.1" 200 25 "-" "curl/7.50.3"
192.168.56.1 - - [03/Jun/2017:09:17:50 +0800] "GET / HTTP/1.1" 200 24 "-" "curl/7.50.3"

You can see that it was indeed from the request we made from our latest curl request.

Types of nginx directives

There are 4 types of directives in nginx :

  • Standard directive
  • Array directive
  • Action directive
  • try_files directive

Consider the configuration file below that we use to explain these :

#These are array directives defined in the parent context here.	
access_log /var/log/nginx/access.log
access_log /var/log/nginx/access_notice.log notice;

http {
	include mime.types;
	gzip on; #This is a standard directive
	server{
		listen 80;
		server_name localhost;
		
		#Array directive defined with another value
		access_log /var/log/nginx/host_access.log main;
		root html;
		
		location /downloads {
						#Standard directive .Can also turn off logging in some context
						gzip off;
						
						#Array directive defined with another value in this context.
						access_log /var/log/nginx/downloads.log main;
                        root /sites;
						
			#try_files is kind of a directive on its own.
                        try_files $uri =404 ;
        }
		
		#Action directives.
		#directives that do something when hit.rewrite and return are good examples
		location /home {
			rewrite /index.html
		}
		
		location /blog {
			return 200 'Our blog is under construction!';
		
		}
	}
}

Standard directives : Can be defined once in a context. This standard directive is effective through out the http context until it is turned off explicitly in child contexts.Inheritance works normally from parent context to child context until overridden in child contexts.

Array directives can be defined multiple times in different places with different values
 NOTE : All parent declarations (we can have multiple) are completely overwritten by one child context declaration. For example in the /downloads block, all logs will now be writen to /var/log/nginx/downloads.log .

Action directives do something when hit. Action directives are not inherited. Look at location/home and location/blog in the example config above

the try_files directive allows you to specify a series of resources to try when a particular location is matched. try_files directives are not inherited. Look at the location /downloads block in the example config above. When a request http://www.domain.com/downloads/images/cutecat.jpg comes in, the server will try to serve the file as per uri /downloads/images/cutecat.jpg , if unsucessful, it will then serve the file i ndex.html and if it is not found just give a 404 page.

Configuring a PHP backend for nginx

Till now, we have only been serving static  content with nginx, things like html, css,javascript, plain text ….But we can also use all kinds of backend technologies like Ruby, NodeJS or PHP. I will show you how to use PHP with nginx but the similar techniques can easily be used with your favorite backend technology. Remember that I am on Ubuntu 16.04 and you should adapt to your own system. My Ubuntu version doesn’t support php5 right off the bat, so I will be using PHP7. Install PHP by issuing the command below :

sudo apt-get install libcurl3 libmcrypt4 libmemcached11 libxmlrpc-epi0 php7.0-cli php7.0-common php7.0-curl php7.0-fpm php7.0-gd php7.0-intl php7.0-json php7.0-mbstring php7.0-mcrypt php7.0-mysql php7.0-opcache php7.0-readline php7.0-xml php7.0-xmlrpc psmisc libmcrypt-dev mcrypt php-pear php-mysql php-mbstring php-mcrypt php-xml php-intl libmhash2 php-common

This should install php for you. When done with the install, you need to take care of one security loophole in php. Open your php.ini file

nano /etc/php/7.0/fpm/php.ini

Find cgi.fix_pathinfo. This will be commented out with a semi-colon (;) and set to “1” by default. We will change both of these conditions by uncommenting the line and setting it to “0” like this:

; http://php.net/cgi.fix-pathinfo
cgi.fix_pathinfo=0

; if cgi.discard_path is enabled, the PHP CGI binary can safely be placed outside

 

PHP is mostly taken care of by now. Switching focus to how it talks to nginx, lets take a look at a configuration file that lets them communicate

user www-data www-data;
events {
}

http {
        include mime.types;
        server {
        listen 80 default_server;
        root /usr/share/nginx/html;
        index index.html index.htm index.php;
        server_name example.com www.example.com;

        location / {
            try_files $uri $uri/ /index.php$is_args$args;
        }

        location ~ \.php$ {
            # NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini
            fastcgi_split_path_info ^(.+\.php)(/.+)$;
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
            # With php7.0-fpm:
            fastcgi_pass unix:/run/php/php7.0-fpm.sock;
            #Using TCP:Port combination
            #fastcgi_pass   127.0.0.1:9000;
           include fastcgi_params;
        }
    }
}

We will explain other configurations but for now focus on

 fastcgi_pass unix:/run/php/php7.0-fpm.sock;

This line lets you define how nginx will talk to the php process. Nginx can communicate with php-fpm either through IP:port combination or a unix socket. We are using unix sockets in this case : fastcgi_pass unix:/run/php/php7.0-fpm.sock . To get this work , also make sure php fpm is aware of this. Head to the file /etc/php/7.0/fpm/pool.d/www.conf and change the settings as shown below :

listen = /run/php/php7.0-fpm.sock
;listen = 127.0.0.1:9000

This instructs php-fpm to communicate through /run/php/php7.0-fpm.sock (it is uncommented:removed the ; ), you could have chosen to use TCP:PORT combination and they both work equally well. For changes to take effect, restart php-fpm

service php7.0-fpm restart

See if the nginx configuration file is ok

sudo nginx -t

and reload the nginx configuration file

service nginx reload

Now go to /usr/share/nginx/html and create a file called info.php . Inside, put the content

<?php phpinfo() ?>

Now visit http://www.example.com in your browser and voila!

Now that you see the whole thing working, lets explain the how our nginx configuration file makes this work.

user www-data www-data;

lets you configure the user and group nginx will run as, most web servers out there use the www-data user, may be because apache servers use this intensively. I keep this user but you are free to use whatever works for you.

include mime.types;

is including a list of mime types supported by your server. To see the effect of this configuration, head to your command line and issue the command curl -I www.example.com and you get a response

[gakwaya.iwange] ➤ curl -I www.example.com
HTTP/1.1 200 OK
Server: nginx/1.12.0
Date: Sat, 03 Jun 2017 02:46:37 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Wed, 12 Apr 2017 14:46:01 GMT
Connection: keep-alive
ETag: "58ee3da9-264"
Accept-Ranges: bytes

You see Content-Type defined as text/html, these data types, if I may call them so, are defined in that mime.types file. You can open it and see how they are defined yourself. It’s  a good way to learn.

try_files $uri $uri/ /index.php$is_args$args;

gives a list of things nginx will try to serve if the block is matched. In this case, it will try to serve the uri directly and if that fails it will try to interpret the uri as a directory and if that fails as well it will to append the arguments that came with the request to the uri. A good thing to note here, when you find a variable that you don’t know like $is_args or $args here, you can directly head to the nginx documentation page and look for it .

fastcgi_split_path_info ^(.+\.php)(/.+)$;

Just like for variables, if you see a directive you don’t recognize, head to the documentation page and look for it. Quoting the documentation itself, fastcgi_split_path_info

Defines a regular expression that captures a value for the $fastcgi_path_info variable. The regular expression should have two captures: the first becomes a value of the $fastcgi_script_name variable, the second becomes a value of the $fastcgi_path_info variable. For example, with these settings

location ~ ^(.+\.php)(.*)$ {
    fastcgi_split_path_info       ^(.+\.php)(.*)$;
    fastcgi_param SCRIPT_FILENAME /path/to/php$fastcgi_script_name;
    fastcgi_param PATH_INFO       $fastcgi_path_info;

and the “/show.php/article/0001” request, the SCRIPT_FILENAME parameter will be equal to “/path/to/php/show.php”, and the PATH_INFO parameter will be equal to “/article/0001”.

I honestly can’t come up with a better explanation than that. Now that you know to look up things you don’t recognize, you can go and look up the rest of the directives. A piece of advice here, don’t try to magically directly understand how everything fits togethere very fast if you are new to this, just take the configuration file I gave above, make sure it works for you, try things out and gradually even directives that you didn’t understand will start to make sense. There still are things I don’t understand myself and I have been using nginx for two years now!

Tweaking your nginx conf files

Now you’ve pretty much got all the basics of nginx nailed down and in the remaining sections we will just be giving you tips to better administer your server.

Expire headers

Static assets like js/css/images don’t change often and it is a good idea to cache them in  the client browser especially on mobile devices.You can take advantage of this with a location block like this

location \.(css|js|jpg|png|gif|ico|jpeg)${
expires 1M;
#Can also use day notation
#expires 20d;
}

This sets for static assets to expire after one month. The browser basically checks the last modified header to see if the set time is past. If not it just loads the cashed version of the static asset.

Using gzip to compress assets

When using gzip compression, the server compresses the files before sending them to clients and this hugely saves on bandwidth. You usually configure gzip globaly in the http or server context and then overridde in child contexts when necessary. An example configuration is shown below with comments to explain

   gzip on;
   gzip_min_length 100; # size in bytes at which to start compressing, default is 20
   gzip_comp_level 3 ; # The higher this number the higher compression is applied and the more cpu resources consumed.Keep this between 2~4
   gzip_types text/plain text/css text/javascript;
   #You can also put these on separate lines like
   #gzip_types text/plain
   #gzip_types text/css ...
   gzip_disable "msie6"; # For client requests with user agent header matching the specified string, gzip will be disabled.
                         # Internet explorer 6 is known to not handle gzip well so we disable it.

What do I do from here?

You have learned a great deal about nginx so far and that’s great! But you will probably see many horrible configurations online and it is good to know that using some of these blindly can seriously harm your server. The official nginx site maintains a good list of DO’S and DONT’S that is really worth your time. Please make sure you read it and you may learn some of the things I didn’t have a chance to talk about here.

We’ll stop here for the moment, and we will be adding more tweaks in the future as time allows. For now I hope this has been informative to you and thanks for reading.

Posted in Servers, Tutorials and tagged , , .

Daniel Gakwaya loves computer Hardware/Software.He is a Software Engineer at BLIKOON and lead developer of bliboard-The whiteboard system currently marketed by the company.He is known to hack around on any piece of tech that happens to pick his interest. More on his tech endeavors here

Follow him on Facebook
Follow him on Twitter
Follow him on LinkedIn
Follow him on Github

Leave a Reply

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

*