loading...

Nginx – Nginx Core Directives

Installing MySQL On CentOS 8

By now, your Nginx server should be up and running. It is time to go ahead and take control over it! You can consider the directives as the nuts and bolts of your web server, using which ones you tweak by the way your web server performs. Nginx has a lot of directives and it is important that you know the basic semantics, so that you can fine-tune your server according to your requirements. You will learn about directives in detail in this chapter.

Location of Configuration Files

The Nginx configuration file

is named nginx.conf. Depending on how you have installed Nginx, you can find the configuration in /etc/nginx, /usr/local/etc/nginx, or /usr/local/nginx/conf. You can run the following command to get the configuration path currently in use by Nginx:

ps -ax | grep nginx

For more concise output, use the following:

ps -ax -o command | grep nginx

The output will be something like this:

nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf
nginx: worker process

The -c switch
above tells you the active configuration that is loaded in the process /usr/sbin/nginx

What Are Directives?

“Directive” is defined as an instruction or to direct. Directives define how Nginx runs on your server. As you already know from chapter 1, Nginx is modular. You can compile Nginx with the modules that you need in your environment and configure the modules using directives. Directives are of two types: simple directives and block directives.

  • Simple directive – A simple directive

    can be as straightforward as a set of names and parameters separated by spaces, and ending with a semicolon. For example, the directive for worker processes looks like this:

    worker_processes    1;

    This tells Nginx to spawn only one worker process. You will learn in the upcoming chapters about why you should make this value equal to the number of processors on your server. For now, the core idea is to understand that this directive is giving direction to the master process of Nginx about how to spawn worker processes.

  • Block directive– As the name suggests, it looks like a block of text enclosed by curly braces { } and contains a set of simple directives.

A typical Nginx configuration file is comprised of blocks as you can see in Figure 3-1. You can refer to the blocks as contexts. The outermost context is called the main context and contains simple directives along with other contexts like Context A and Context B. The contexts, in turn, contain a set of related directives.

Figure 3-1.
Structure

of Nginx Configuration file

Tip

You can add comments in your configuration file by using a pound ( #) sign.

Context Types

Every module in Nginx has a very discrete purpose and is controlled by the directives. The documentation

clearly informs you about the context in which the directives can be used. Understanding this convention is helpful when configuring a specific module according to your needs.

There are quite a few different contexts available in Nginx: for example, main, events, HTTP, server, location, upstream, if, stream, mail, etc. Out of these, HTTP, events, server, and location are most commonly used. The contexts could be nested as well. This is what the basic overall structure of the configuration looks like:

# main block is not explicitly called as main, it is implied
main {
      simple_directives parameters;
      ...
      events{
         event_directives parameters;
         ...
      }
      http{
         http_directives parameters;
         ...
   server{
      server_directives parameters;
      ...
      location{
         location_directives parameters;
           ...
      }
    }
  }
}

Let’s configure the server so that you can see the directives and contexts in action.

Understanding the Default Configuration

The default configuration

of Nginx looks similar to the following:

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

Simple Directives

The whole body could be referred to as the main context. There are a few simple directives defined in the main block:

  • user directive has a default value of nobody. You can add user directive to define the account under which Nginx worker process will be executed on the server. The syntax for user directive is like user <user_name> <group_name>. The user should exist on the server before Nginx starts or else there will be an error while starting Nginx services.

Tip

Nginx should be executed under a least-privileged account.

  • worker_processdirective has a default value of 1 and it implies the number of worker processes that will be spawned by Nginx. Setting the value to auto is also permitted and in that case Nginx tries to autodetect the number of cores.

  • error_log directive
    can be applied in multiple contexts like main, http, mail, stream, server, and location. Here, you see it applied in the main context. The first parameter /var/log/nginx/error.log tells you the the name of the file where the log file will be created, whereas the second parameter warn informs you that anything above the warning level will be logged.

    The logging levels

    are defined in increasing order as you can see from the list below and it is important to note that if you set the level to error, then the logs of type warning, notice, and info will be ignored. Keeping it to info is not recommended in production since the logs may become massive if your website is hit very frequently. The logs should be periodically analyzed to verify if it contains anything alarming.

    • info – Information

    • notice – Notice

    • warn – Warnings

    • error – Error

    • crit – Critical

    • alert – High Alert

    • emerg – Emergency

Tip

You should always set the log level to either warn or error.

  • pid directive has a parameter that defines the file name that stores the process ID of the master process /var/run/nginx.pid. You may be thinking why does nginx log the PID to a file? Glad you asked! Imagine a scenario where you are supposed to check the uptime of a process. Running a command like ps -ax | grep nginx will help you get the current status and process id (PID),
    but you cannot really tell how long the process has been alive. To get this duration you may use a couple of commands like the following:

    # ps -ax | grep nginx
    30212 ?        Ss     0:00 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf
    30213 ?        S      0:00 nginx: worker process
    • The first column contains the PID. In this case, 30212 is the PID of the master process.

    # ps -p 30212 -o etime=
          15:00 <<< Process uptime
    • You can now write a script or command to get the result like the following:

    # ps -p `cat /var/run/nginx.pid` -o etime=
          15:40 <<< Process uptime
    • This one liner is enough to get you the uptime and as you can guess, it can come in handy when you have to automate the monitoring of processes.

Events Context

After the simple directives in the default configuration, you will find a context called events. The events context can be declared only in the main context and there can be only a single events context defined within the Nginx configuration. With the use of directives in the event context, you can fine-tune the way Nginx behaves. There are just six different event directives.

  • worker_connections directive

    allows a maximum of 1024 concurrent worker connections. The defaults in Nginx usually suffice. The number of concurrent connections you may get on a web server can be calculated roughly using the following (N = average number of connections per request):

    (worker_processes x worker_connections x N) / Average Request Time
  • use directive does not need to be applied explicitly since Nginx tries to use the most efficient method automatically. Basically, the use directive allows Nginx to support a variety of connection methods depending on the platform.

    • use select is the worst performing and is used when more efficient methods are not available on a platform. If you run Nginx in a Windows environment, this is what will get used. This reason alone should be a big deterrent for you to use Nginx on a Windows platform.

    • The other modes are poll (standard method), kqueue (efficient method on FreeBSD, OpenBSD, NetBSD and Max OSX), epoll (Linux), /dev/poll (Solaris, IRIX) and eventport (efficient method on Solaris 10).

  • multi_accept is set to off by default. It means that a worker process will accept only one new connection at a time by default. It is a generally a good idea to enable multi_accept so that Nginx can accept as many connections as possible.

  • accept_mutex is set to on by default, and it is generally left untouched. Basically, it means that the worker processes will get the requests one by one. This implies that the worker processes will not jump up for every request and go back to sleep if the number of requests is low.

  • accept_mutex_delay comes into effect only when accept_mutex is enabled. As the name implies it is the maximum time to wait for the existing worker process to accept the new connection before initiating a new process to execute the request.

HTTP Context

The HTTP context

(or block) can be considered the heart of the configuration system for an Nginx web server. In the default configuration you will notice the following directives:

  • include /etc/nginx/mine.types – The include directive keeps the core configuration file clean. You can use this directive to keep related configurations in a separate file. Nginx will ensure that the file is loaded in-place when loading the configuration. At the same time, it will keep the main configuration readable and manageable.

    If you view /etc/nginx/mime.types you will find a block of text that is nothing but another directive called types. It maps file name extension to MIME types of responses. The extensions are case insensitive and there can be many extensions mapped to one type. The following snippet shows the structure of this file. Notice how html htm shtml extensions are all mapped to text/html MIME type.

    types {
        text/html                             html htm shtml;
        text/css                              css;
        text/xml                              xml;
        image/gif                             gif;
        image/jpeg                            jpeg jpg;
        application/javascript                js;
    ...
    ...
        audio/midi                            mid midi kar;
        audio/mpeg                            mp3;
    ...
    ...
        video/x-flv                           flv;
        video/x-m4v                           m4v;
    }
Tip

MIME types describe the media type of content and guides the browser so that it renders the content appropriately in the browser instead of downloading the file. Assume you want to serve a file called
myfile.data. If you simply create a file called myfile.data in your root folder ( /etc/nginx/html), you might think that the browser will be able to render it when you type http://localhost/myfile.data. However, you will notice that the browser simply downloads the file since it doesn’t know what kind of data it contains! MIME type completes this story. If you know that your file is a text file, you can simply modify the MIME type for text/plain so that it reads as the following:

text/plain       txt data;

By doing this, Nginx guides the browser that the extension of this file is data, but the content is plain text and could be displayed inside the browser. The browser obliges and renders the content right away, instead of downloading it!

MIME types

can be helpful in other scenarios where you create a custom content with a new MIME type altogether. You can then create a client that understands the specific extension. Example: application/pdf is a MIME type that helps the clients like Adobe or Foxit Reader to read the PDF content directly from the web server.

  • default_type directive has a value of application/octet-stream. It specifies the default MIME type if Nginx fails to find a specific one in the /etc/nginx/mine.types. It is this MIME type that guides the browser that it has to download the file directly.

  • log_format directive configures the ngx_http_log_module. It writes the log in a specified format. The first parameter is the name of the format, in this case main. The second parameter is a series of variables (you will learn about it in detail soon) that will contain a different value for every request. Once you have named a log_format, you will need to use it.

              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 directive requires a path ( /var/log/nginx/access.log) and name of a format ( main). There is much more to access_log that you will learn in the upcoming chapters, but for now you can simply understand that every request you make from the server can be logged to a file so that you can analyze it later. A good web administrator takes very good care of these logs, and analyzes it periodically to find out issues that sometimes go unnoticed. These logs also prove to be helpful during troubleshooting scenarios.

  • The default value for sendfile directive is off if the directive is not present. Nginx default configuration hence, turns it on. It is generally a good idea to enable it, since it ensures that the function is called with
    SF_NODISKIO. In simple words, it means that the call will not block on disk I/O. The data is loaded in chunks and sent appropriately to the client. As you can guess, it has a huge advantage and enables Nginx to scale very well, especially while serving large files.

  • tcp_nopush directive
    is commented by default and the default value is off. This comes into effect only when you are using sendfile and basically directs the Nginx server to send the packets in full. Typically, you can leave it disabled.

  • keepalive_timeout directive has a value of 65. Normally, when a connection is made to the server, you need not disconnect the connection straightaway. That is because a web page normally comprises of a lot of assets. It will not be very effective to create a new connection for every asset that is sent to the client.

    Hence, the first few connections are made to the server and then, they are kept alive. The idea is to deliver the rest of the assets on the same set of connections one after the other. Now, let’s say there were 125 assets (css, js, html, images, etc.) in a page. When the client accesses the URL, it might create 2 connections or more (modern browsers open a lot more connections!). Assuming 5 connections were made, those assets will be delivered one by one to the client in parallel over 5 different connections! What do you think will happen to the open connections if the page is delivered in just 3 seconds? Well, they will continue to live and, as you can guess, will waste resources on the server. This is the reason why this directive exists. It allows the server to close the connection in 65 seconds automatically if the client doesn’t return and the connection is idle. On a busy server, you may choose to reduce this timeout.

  • gzip directive
    compresses the output so that lesser bandwidth is consumed per request. By default it is turned off, and it is recommended to turn it on.

  • The last line in the configuration is yet another include and it is an interesting one! You can see that it accepts wild cards ( include /etc/nginx/conf.d/*.conf;) and it implies that it will load all the configuration file sat once from the folder /etc/nginx/conf.d. In the next section you will see what is included in the conf.d folder.

The conf.d Folder

The /etc/nginx/conf.d folder contains two files, default.conf and example_ssl.conf. The example_ssl.conf file is fully commented out and not used until you have a requirement to host SSL. You will learn about SSL in chapter 13. In this section you will learn about the directives in default.conf.

The default configuration file looks like the following:

server {
    listen       80;
    server_name  localhost;

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

    location / {
        root   /etc/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;
    #}
}

Please keep in mind that even though you see this section starting with server { } block, it is nested inside http!

Server Context

The server block

can be set in multiple contexts to configure various modules (you will learn about modules in chapter 4), as shown in Table 3-1.

Table 3-1.
Server directive in different modules

Module Name

Context

Details

ngx_http_core_module

http

Sets the configuration for a virtual server using server_name directives.

ngx_http_upstream_module

upstream

Sets the address and other parameters of a server. Useful in reverse proxy and load balancing scenarios.

ngx_mail_core_module

mail

Sets the configuration for a mail server.

ngx_stream_core_module

stream

Sets the configuration for a streaming server.

ngx_stream_upstream_module

upstream

Similar to ngx_http_upstream_module.

As you can imagine, it wouldn’t be effective to have just one application per server. This is where the concept of
virtual servercomes in to play. Nginx has a mechanism that allows it to select a specific server and location block based on the request. Every request gets handled based on the configuration in a single server context.

Another interesting thing about the server directive is that it allows for multiple declarations adjacent to each other. In the configuration mentioned earlier, you can see one server block being configured such that it listens to port 80 with the server_name as localhost. You can override the access_log directive here if you wish. By default, it is commented out along with a charset directive.

Setting up a virtual server and its location context is one of the most common tasks when using Nginx; hence it is strongly adviced to do the following exercise to get the feel of it and play around.

How To Set Up A Basic Server To Serve Static Content

Start from a blank slate

You can leave the defaults at the global level ( /etc/nginx/nginx.conf) untouched for the purpose of this exercise. Start with navigating to the conf.d directory. cd /etc/nginx/conf.d.

  • Take a backup of the default.conf file (so that it doesn’t get loaded due to the include directive in nginx.conf) sudo mv default.conf default.backup

  • The idea is to keep the configuration as organized as possible. Create a file called virtual_servers.conf with the following content. The intention is to host 2 different applications that should be browsable using

    http://app1.com or http://www.app1.com
     http://app2.com or http://www.app2.com
    
    server {
            listen 80;
            server_name app1.com www.app1.com;
            location / {
                    root /etc/nginx/html/app1;
            }
    }
    
    server {
            listen 80;
            server_name app2.com www.app2.com;
            location / {
                    root /etc/nginx/html/app2;
            }
    }

Tip It is important to keep the port as 80 for a public-facing website, or else the end user will have to type the port explicitly in the URL (ex. http://app1.com:8080
), and it won’t be a good idea!

  • Notice that there are 2 server blocks and both of them are listening on port 80 using the listen directive.

  • There are 2 different directories called /etc/nginx/html/app1 and /etc/nginx/html/app2. Both are mapped inside the location block, using the root directive.

  • Set up your applications by navigating to the web root folder by executing cd /etc/nginx/html.

  • Create 2 directories by executing mkdir app1 app2.

  • Create an index.html file in both folders with text “Index for App1,” and “Index for App2.” This will make it easy for you to identify if you are hitting the right file.

  • Let Nginx know that the configuration has changed. You can either restart Nginx by using systemctl restart nginx or reload the configuration without killing the nginx service using nginx -s reload. (-s stands for signal. The upcoming chapters will explain the signals in detail.)

Routing Rule #1

  • If you browse to http://localhost using curl http://localhost the output will be Index for App1. Why? Basically, Nginx tries to match the host name (localhost here) to the values in server_name directive for each server block. In this case, it was not able to match either app1.com, app2.com,
    www.app1.com

    , or
    www.app2.com
    . Since it failed, it defaulted to the FIRST server block it countered that was listening on default port (80).

  • Change the port to 81 for the first server block and execute nginx -s reload. Try curl http://locahost now, and you will see the output as Index for App2. This is because the default port is 80, and as per the previous point, the fallback happens to the first server block listening on port 80. Change the port back to 80.

Routing Rule #2

  • Now, you have two blocks with the same port. How can you explicitly tell Nginx that the second server block is the default server block? To do this, you will have to add a directive called default_server to the listen directive. Change the listen directive of the second server block so that it reads listen 80 default_server;

  • Try curl http://localhost again, and you will find the output as Index for App2.

Note The default_server property is set on the listen port and not on the server name as you might guess!

Routing Rule #3

  • You have a hawkeye if you found a problem with default_server already! What if you wouldn’t want the first block or any server block to be the default block? In other words, you may want to NOT serve the request if the host header field is empty.

  • Add the following text at the start of the virtual_servers.conf file and reload nginx configuration.

    server {
            listen 80;
            server_name "" localhost 127.0.0.1;
            return 444;
    }
  • If you try curl http://localhost or curl http://127.0.0.1 you should get a non-standard output like this:

    Empty reply from server

Routing Rule #4

  • If Nginx is listening on different IPs, Nginx first reads the IP address and port, and then tests the host header fields. This means that if you have a structure like the following, and a request is received for app3.com on 10.0.0.1, you will see the output from app1.com instead! This happens because Nginx couldn’t find app3.com on 10.0.0.1 and hence defaulted to the first server block.

    server{
         listen 10.0.0.1:80;
         server_name app1.com www.app1.com;
         ...
    }
    
    server{
         listen     10.0.0.1:80;
         server_name app2.com www.app2.com;
         ...
    }
    
    server{
         listen     10.0.0.2:80;
         server_name app3.com www.app3.com;
         ...}

Routing Rule #5

  • You can use default_server on multiple ports. But on one IP and port, you cannot have two default servers. If you try doing that, you will get an emergency error message similar to the following:

    nginx: [emerg] a duplicate default server for 0.0.0.0:80 in /etc/nginx/conf.d/virtual_servers.conf:15

Visualizing Routing Rules

You can visualize the request flow in Nginx as shown in Figure 3-2.

Figure 3-2.
Request flow

in Nginx

Wildcards Names

Wildcards

are allowed in Nginx configuration with few rules. An asterisk is allowed only on the name’s start and end and has to be suffixed or prefixed by a dot respectively. Hence *.app.com and www.app.* are valid. On the contrary, www.*.app.com and www.app.*.org are not valid. For more complicated strings, you can use valid (Perl) regular expressions, if required.

The good thing is that *.app.com matches www.​app.​com, blog.app.com, www.​blog.​app.​com, etc.!

Tip

Try to keep your configuration file organized. In cases where you may have multiple values, it is generally a good idea to split the line like this:

server{
     listen     80;
     server_name app.com
                                www.app.com
                                *.app.com;
     ...
}

Location Context

You have already seen the usage of root directive inside a location block. The root directive tells Nginx to return /image/myimage.jpg instead of fully qualified path /etc/nginx/html/app1/image/myimage.jpg.

Another important directive in location block is index, which specifies the name of default files that is sent to the client if the file name is not specified. For example, if you add index index.html index.htm default.htm; inside location directive, you are telling Nginx to return the index.html or index.htm or default.htm if the URI is simply
http://www.app.com
. In other words, if index.html is not found, it will search for index.htm, and so on.

Location directive points to the actual content on the web server. You can point to different locations using different location directives and include regular expressions as well. When a request is received, the text in the URI is decoded and the adjacents slashes (if any) are replaced with a single slash. To find the appropriate location, Nginx then goes through its locations defined in the configuration.

Based on the requested URI, it selects and remembers the
longest matching prefix . Then it proceeds to match regular expressions (if any). If one regular expression doesn’t match, it proceeds with the next one. It stops processing the next regular expressions at the first match!

The location directive has four different modifiers: =, ∼, ∼*, and ^∼. These prefixes have the meanings given in Table 3-2.

Table 3-2. Location Modifiers

Modifier

Meaning

∼*

Case insensitive search. Ideal for most cases.

Case sensitive search.

^∼

Do not check any regular expressions if the matching prefix location has ^∼

=

Directs Nginx to do an exact match of URI and location. It is a good idea to provide exact matches for every URL that is frequently used so that Nginx doesn’t need to do a search.

It is very crucial that you follow along in this section. The more you play with the configuration the better you will be able to handle it. Location directive happens to be one of the most important configurations in Nginx and it is extremely important that you master it.

Location Configuration

Prepare Server with some files

In your /etc/nginx/html create a folder called common and a file called common/index.html. Before you proceed, please ensure that you have the following structure. If there is a file missing, simply create that and key in some text so that you can identify the file. For the png files, you can download a couple of png files from the Internet and save it inside the common folder.

html
|-- 50x.html
|-- app1
|   `-- index.html
|-- app2
|   |-- home.html
|   `-- index.html
|-- common
|   |-- app.js
|   |-- index.html
|   |-- nginx.png
|   `-- nginx.PNG
`-- index.html

Tip You can use wget http://image_url command to download any image from the Internet using a command line.

Load different configuration based on location directive

  • Replace the content of the file you created earlier /etc/nginx/conf.d/virtual_server.conf with the following:

    server {
            listen 80;
            server_name 127.0.0.1 localhost;
    
            location /app1/ {
                    root /etc/nginx/html;
                    index index.html;
            }
    
            location /app2/ {
                    root /etc/nginx/html;
                    index home.html;
            }
    }
  • Execute nginx -s reload and try accessing the pages:

  • lynx http://localhost/app1 > Should work and you will see index.html

  • lynx http://localhost/app2 > Should work and you will see home.html

  • As you can see, different sections and configurations can be loaded based on your configuration. You will also notice that there are certain elements that are repeated and have the same value, like root attribute. Since the root directory is allowed in server context, you can restructure your configuration like the following:

    server {
            listen 80;
            server_name 127.0.0.1 localhost;
            root /etc/nginx/html;
    
            location /app1/ {
                    index index.html;
            }
    
            location /app2/ {
                    index home.html;
            }
    }

Handling Common Files using pattern matching

  • Assume that the common folder contains a lot of assets that are common to both applications. Also assume that the files don’t need to be changed often. In that case, you may decide to change the configuration for this section such that the file is cached at the client side for a much longer duration using certain directives.

  • Modify your configuration file so that you have two additional location tags as follows. Notice the subtle prefixes. The first location block contains ∼ prefix and matches the URIs that have png, jpg, or jpeg. If it matches, it adds an expires directive. In the second location block, the expires directive is set to 10 days. The expires directive adds some special headers to the files so that the browser caches it and serves it from the local cache. Expires max will set the value to Thu, 31 Dec 2037 23:55:55 GMT.

    server {
            listen 80;
            server_name 127.0.0.1 localhost;
            root /etc/nginx/html;
    
            location ∼ \.(png|jpg|jpeg)$ {
                    expires max;
            }
    
            location ∼* \.(png|jpg|jpeg)$ {
                    expires 10d;
            }
    }
  • Once you have reloaded the configuration access the files: http://localhost/common/nginx.png & http://localhost/common/nginx.PNG

  • Look at Figures 3-3 and 3-4 and notice how the headers got added.

    Figure 3-3.
    Response for
    http://localhost/common/nginx.png

    Figure 3-4.
    Response for
    http://localhost/common/nginx.PNG
  • Notice how the headers have been added based on different location URIs. This gives you a very robust way of handling different kind of files.

Avoid Regular Expression Matching

  • You have already seen case sensitive (∼) and insensitive (∼*) search. What if you would like to avoid regular expressions completely for some unique cases? You can prefix the URI with (^∼).

  • Change your configuration file as follows and access http://localhost/common/nginx.PNG (Figure 3-5) and notice that max-age = 172800 seconds (2 days). The interesting thing to note here is that the other two locations still exist and they are unchanged. Essentially, when you have a ^∼ prefix before the longest matching URIs in the location directive, Nginx will skip checking the other regular expressions.

    Figure 3-5. Response for http://localhost/common/nginx.PNG
    server {
            listen 80;
            server_name 127.0.0.1 localhost;
            root /etc/nginx/html;
    
            location ^∼ /common/nginx.PNG {
                    expires 2d;
            }
    
            location ∼ \.(png|jpg|jpeg)$ {
                    expires max;
            }
    
            location ∼* \.(png|jpg|jpeg)$ {
                    expires 10d;
            }
    }

Exact modifier

  • Last, but not the least, there is an Exact modifier (=). Before you see it in action, it will be helpful to take a little detour and change the configuration so that you can visualize it easily. Update your /etc/nginx/nginx.conf so that your log_format looks similar to the following:

    log_format  main  '$remote_addr - $remote_user [$time_local] "$document_root$document_uri" "$request"'
                          '$status $body_bytes_sent "$http_referer"';
  • Basically, you are telling Nginx to log the information like $document_root and $document_uri (you will learn about it in detail later).

  • Update your conf.d/virtual_server.conf as follows:

    server {
            listen 80;
            server_name 127.0.0.1 localhost;
    
            location = / {
                    root /etc/nginx/html/app2;
                    index home.html;
            }
    }
  • Reload your configuration using nginx -s reload.

  • In this block you are telling Nginx that http://localhost implies that it should consider /etc/nginx/html/app2 as the root path and home.html as its default page.

  • Try executing curl http://localhost and you will be surprised to find a 404! Why?

  • Take a look at the access log using tail /var/log/nginx/access.log

    "/etc/nginx/html/home.html" "GET / HTTP/1.1"404 168 "-"
  • Notice that it is not pointing to /etc/nginx/html/app2/home.html. Instead! Does it mean that Nginx didn’t catch the location directive? Of course not, it did catch it, but it did an internal redirect to the / page and since there was no location defined, it failed with a 404. To fix this, change the configuration as follows and retry:

    server {
            listen 80;
            server_name 127.0.0.1 localhost;
    
            location = / {
                    root /etc/nginx/html/app2;
                    index home.html;
            }
    
            location / {
                    root /etc/nginx/html/app2;
            }
    }
  • In other words, the exact modifier will speed up your processing even more, since Nginx will not need to parse other regular expressions. It will terminate its search as soon as it finds an exact match and does an internal redirect to the page that you are looking for!

Location Context Special Cases

You have already seen an interesting internal redirection case using index directive. As discussed earlier, there are two location blocks in play at tandem, and that is what makes it interesting and confusing at times. There are other directives as well that cause some special redirections.

try_files

This directive checks if the file exists in a specified order. If it finds a file among a predefined list, it is processed and the files next in the list are ignored. If you would like to check a directory, you need to suffix a slash (/) at the end of the name. If none of the files are found, an internal redirect happens as per the last parameter in the list. try_files directive allows you to get rid of if directive since you no longer have to check if the file exists or not. if directive is extremely inefficient since it is evaluated every time for every request. A good rule of thumb is to avoid if directive completely (you will learn more about if directive in coming chapters).

An example should clarify this. Take a look at the configuration and notice that the root is set to /etc/nginx/html. The two location blocks are pretty similar to each other. Both of them are applying similar rules to jpg, jpeg, and png files. The only difference is that the second location block has a try_files block while the first one doesn’t. The
try_files directive

is set such that it will look for the file ($uri), and if it doesn’t find the file, it looks for the folder called ($uri/). If it doesn’t find the folder either, it returns /nginx.png from the root that is defined in this location context ( /etc/nginx/html/common).

server {
        listen 80;
        server_name 127.0.0.1 localhost;
        root /etc/nginx/html;

        location ∼* \.(jpg|jpeg)$ {
                root /etc/nginx/html/common;
        }

        location ∼* \.(png)$ {
                root /etc/nginx/html/common;
                try_files $uri $uri/ /nginx.png;
        }
}

This implies that if you browse:

  • http://localhost/common/nginx.png > It will work.

  • http://localhost/common/file.png > Returns /nginx.png since file.png doesn’t exist.

  • http://localhost/nginx.PNG > It will work and return the actual nginx.PNG file since it exists.

  • http://localhost/nginx.png > It will work too!

  • http://localhost/non_existent.png > It will return the fallback image file /nginx.png.

  • http://localhost/non_existent.jpg > ERROR! 404, since there are no files to try!

  • http://localhost/existingfile.jpg > Works if you have the file in the defined root folder.

If you replace the line below,

try_files $uri $uri/ /nginx.png;

with

try_files $uri $uri/ /nginx.png =404;

it will mean that if the fallback file (/nginx.png) doesn’t exist, Nginx will return a 404 code.

There is yet another interesting thing you can do with try_files! You can give it a named location and have it handled in a different location block altogether.

location ∼* \.(png)$ {
        root /etc/nginx/html/common;
        try_files $uri $uri/ /nginx.png @mylocation;
}

location @mylocation{
        ...
        #do something here
        ...
}

rewrite

rewrite directive
is another very flexible directive. It takes regex as an input and redirects (or terminates if needed) the requests. It supports four flag parameters:

  • last – Stops the current processing and starts a search for a new location matching the changed URI.

  • break -Stops processing using break directive.

  • redirect – A temporary redirection using status code 302.

  • permanent – A permanent redirection using status code 301.

The bigger question is this: why would you redirect your traffic?

A small but clichéd answer is this: change is the only constant. Sometimes, even after a lot of planning the content location changes due to myriad reasons like change in framework, reorganization of assets, back-end change, etc. In these cases, the
rewrite directive
comes in really handy. Imagine that the folder structure you have been working with has changed, and common is now called
vendor_assets.

server {
        listen 80;
        server_name 127.0.0.1 localhost;
        root /etc/nginx/html;

        location /common/ {
                rewrite ^(/common/)(.*) /vendor_assets/$2 last;
        }
}

Writing good Perl regular expressions is beyond the scope of this book, but basically what is happening in the rewrite directive above is no rocket science. Parenthesis implies groups and it gets extracted in variables like $1, $2, and so on. In the group, the expression says to extract everything that starts with /common/ into a group ($1) and everything after that (.*) in another variable $2. This regular expression is now replaced with a constructed string, /vendor_assets/$2 (where $2 implies the rest of the URI originally sent to the server). The attribute last, as discussed earlier, tells Nginx to stop processing the current request and start a new search for this new location.

In short, it tells Nginx to redirect the current URL http://localhost/common/nginx.png to a new url http://localhost/vendor_assets/nginx.png.

error_page

error_page directive
is a another straightforward directive that helps Nginx to return a specific error page using internal redirect. You can customize your error pages to your liking and make it more meaningful for the end users. It can be used inside http, server, location, and if blocks.

error_page 404     /404_not_found.html;      #Applies to status code 404.
error_page 500 502 503 504 /50x_server_error.html;  #Applies to status code 500, 502, 503 & 504.
error_page 404     =200    /funnypic.png;       #Instead of sending 404, it will send a png with status code 200.

An error_page block like the following can be used to handle the errors in an elegant way. As you can see, an exact modifier is used for 50x.html so that no other location expressions are evaluated in case of errors.

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

Verify the Correctness of Configuration

When you make changes to the configuration file, it is a good idea to check if the configuration file has any issues. You can execute the following command to check it. The -t switch tells Nginx to test the configuration without loading it and -c is the path of the configuration that has to be checked.

A success looks like this:

nginx -t -c /etc/nginx/nginx.conf
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

In case of failure, it points you to the precise line where there is an issue:

[root@wsfe1 ∼]# nginx -t -c /etc/nginx/nginx.conf
nginx: [emerg] unknown directive "xx_junk_directive" in /etc/nginx/conf.d/virtual_server.conf:8
nginx: configuration file /etc/nginx/nginx.conf

Allow Directory Listing

Athough it is not considered a good idea to allow listing of a directory, at times it is required to enable directory listing of specific areas of your website. Nginx has directory listing disabled by default, and to enable it you will need to use autoindex directive. This is how you can use it:

server {
        listen 80;
        server_name 127.0.0.1 localhost;
        root /etc/nginx/html;

        location /common/ {
                root /etc/nginx/html;
                index NON_EXISTENT_FILE;
                autoindex on;
        }
}

If you recall, the directory structure for common has index.html file existing already. If you access http://localhost/common now, it will show you the index file instead. To enure that you see a directory listing, set the index directive so that the file that doesn’t exist. If Nginx finds there are no index files, and autoindex is turned on, it will automatically generate a page for you showing the directory listing as can be seen in Figure 3-6.

Figure 3-6.
Directory listing of a folder in Nginx

Deny Access to Any Specific Location

A common request for a web server is to block access to specific folders for authorization or stopping the configuration file from being downloaded, etc. As you can guess, this is fairly easy to do as well.

server {
        listen 80;
        server_name 127.0.0.1 localhost;
        root /etc/nginx/html;

        location /vendor_assets/ {
                deny all;
        }
}

If you try to accees http://localhost/vendor_assets/ now, you will get a 403 forbidden message from Nginx.

Proxy the Requests to Apache

You will learn about proxying in detail in the coming chapters. For now, you can simply take a quick look at the configuration file ( conf.d/default.conf) that has been renamed earlier to conf.d/default.backup.

Assume that you want to use Apache for listening to PHP requests, you can use the following block for location so that the requests for *.php will get proxied to Apache:

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

Proxy the Requests to FastCGI

For the reasons similar to the previous section, you can choose to redirect the traffic using the following to FastCGI listening on a different port.

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

Nginx Variables

Nginx has a concept of variables

that comes in quite handy at times. You have already seen a few predefined variables like $document_root and $document_uri in action.

Even though they look very convenient, it should be used sparingly. The reason is that they get evaluated at runtime. In other words, every request where you encounter a variable will be evaluated at runtime taking extra CPU cycles! As you can guess, if you are using too many variables, it can toll on the server. Don’t use variables as template macros. Think about using an include directive instead whereever you can. With that little bit of caution, lets take a look at a practical situation where variables can help you.

  • Open your /etc/nginx/nginx.conf and change the log_format to a string like this:

log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer"';
  • Reload your configuration, and make a request using curl http://localhost/

  • Take a look at your access.log using tail command.

  • tail /var/log/nginx/access.log

127.0.0.1 - - [14/Sep/2015:06:18:22 -0400] "GET / HTTP/1.1" 200 23 "-"
  • The log is helpful, but can you tell where exactly it is serving the file from?

  • While configuring the Nginx server, it makes sense to extend the logging so that it logs a little more information. This way, you can check your path and troubleshoot your location expressions quite easily.

  • Open your nginx.conf again, and change the log_format so that it contains a couple of extra variables, like this:

log_format  main  '$remote_addr - $remote_user [$time_local] "$document_root$document_uri"'
                       '"$request" $status $body_bytes_sent "$http_referer"';
  • Reload your configuration, make a new request, and check your acess logs using tail again. Notice the presense of the document path and file name. You can clearly see where the page is being served from!

127.0.0.1 - - [14/Sep/2015:06:25:48 -0400] "/home/nginx_new_home/index.html""GET / HTTP/1.1" 200 23 "-"

Make sure that you remove these variables once your troubleshooting is done. There is more to variables than what you have seen just now and we will cover that in appropriate chapters.

A Quick Note about Nginx Official Documentation

Nginx is pretty well documented, and there are a couple of links you should keep handy. We have made conscious efforts of not replicating what can be easily found online in the official documentation. The book is written in a conversational tone with practical situations in mind. Here are the links that we just talked about:

  • Nginx official documentation page –
    http://nginx.org/en/docs/

  • Alphabetical index of directives –
    http://nginx.org/en/docs/dirindex.html

  • Alphabetical index of variables –
    http://nginx.org/en/docs/varindex.html

If you go the index of directives, you will see every directive listed there. Take a look at the documentation of gzip, for example, in Figure 3-7.

Figure 3-7.

Gzip documentation on the official site

Notice that the first line talks about syntax followed with the default value, and ends with Context where it tells you that you can use gzip in http, server, location, and if blocks! Once you understand the meaning of directives, context, and Nginx configuration structure, it gets really easy to read through the documentation and find out more about the directives and different variables.

Summary

This chapter has introduced you to configuring Nginx primarily as a web server. You have learned about the modular nature of the configuration files. By now, you should be pretty comfortable with the way the default configuration files have been created for you during installation time. Try reading the /etc/nginx/conf.d/default.backup (or default.conf if you haven’t already renamed it) file and you should be able to grasp everything that’s going on there. The core focus of this chapter was to configure Nginx in order to serve static files and give you a decent idea of the jargon used in Nginx.

You have also learned about the routing principles used in Nginx, and how to use different directives in appropriate contexts. You can now configure Nginx for common tasks like serving static files, allowing directory listing, denying access to files/folders, and handling or rewriting requests using location directive.

If you take a look at Nginx documentation now, you should feel right at home since you will be able to decipher the different terminology commonly used in Nginx esoteric circles.

In the next chapter, you will learn about Nginx modules.

Comments are closed.

loading...