Learn the basics of nginx

August 28, 2014Maxime Thoonsen7 min read

The purpose of this article is to explain the basic things you'll need when you start using nginx. I will assume that we are on a ubuntu Trusty(14.04) OS, I let the readers translate the command for their own OS.

Why Nginx?

Nginx is the "new" popular webserver that competes with Apache. So why use it?
Let's take a quote from the wiki:

"Apache is like Microsoft Word, it has a million options but you only need six. Nginx does those six things, and it does five of them 50 times faster than Apache."
-- Chris Lea

umad face

Installation and useful commands

It's very simple on ubuntu:

$ apt-get install nginx

If you need to run some PHP you need to also install FPM

$ apt-get install php7.0-fpm

Then you can manage it with the classic services commands like:

$ service nginx start
$ service php7.0-fpm restart

If your nginx webserver doesn't start, you may have a problem in your conf. To have more info you can test your nginx conf with this command:

$ nginx -t

If everything is good you should see something like:

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

Some basic vhosts

I will now show you some vhosts and explain their use cases but first let's have a look in the /etc/nginx/nginx.conf file. By default there are already basic settings.

user www-data;

This line means that Nginx will run as the www-data user (as Apache usually does).

If you continue through the file you can see where the logs will be recorded by default, and then you have those lines

##
# Virtual Host Configs
##

include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;

As you have guessed, thoses lines include all conf from conf.d directory and all vhosts defined in sites-enabled. Therefore all the vhosts files below need to be in the sites-enabled directory and will be automatically loaded in nginx (after a reload or restart).

How to host a basic html website

If you only need to serve html pages. All you need for your vhosts is:

server {
    listen      80 default_server;
    root        /var/www/;
}

It creates a default webserver that listens on the regular port 80 where basic http requests from browsers come. It also indicates that the root of your website is located at /var/www/. To display your website you only have to create a /var/www/index.html file and it will work.

Basic html website with a server_name

If you want to host many websites on the same machine. You need to use server_name directive so nginx can know which files it has to serve.

server {
    listen      80;
    server_name www.myamazingwebsite.com;
    root        /var/www/myamazingwebsite/;
}

server {
    listen      80;
    server_name www.notsobadwebsite.com;
    root        /var/www/notsobadwebsite;
}

You can also use it to protect yourself against people who create a fake domain name for your website. Like if your dns is like that:

myamazingwebsite.com.        A      173.194.41.191

Someone can buy "thisisashittywebsite.com" domain name and create this DNS A record:

thisisashittywebsite.com.    A      173.194.41.191

And people going on thisisashittywebsite.com will see your website unless you specified a server_name.

More informations about server_names

Basic PHP website

If you need to run some PHP pages, you will need to use the FastCGI-server.

server {
    listen 80;
    server_name nginx-php.demo;
    root        /var/www/php/;

    location ~ \.php {
        fastcgi_index index.php;
        fastcgi_pass unix:/var/run/php5-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

We use location directive to tell nginx that urls matching *.php need to use fastcgi. The fastcgi_pass tells nginx the socket on which FastCGI-server is listening.

Symfony website

This is an example conf for symfony websites in dev mode.

server {
    listen      80;
    server_name nginx-symfony.demo;
    root        /var/www/symfony/web;

    #We add the logs
    error_log /var/www/symfony/app/logs/nginx.error.log;
    access_log /var/www/symfony/app/logs/nginx.access.log;

    #We activate gzip
    gzip            on;
    gzip_min_length 1000;
    gzip_comp_level 9;
    gzip_proxied    any;
    gzip_types      application/javascript application/x-javascript application/json text/css;

    #default index
    index app_dev.php;

    #So we don't have to see the app_dev.php or the app.php of symfony
    try_files $uri @rewrite;
    location @rewrite {
        rewrite ^/?(.*)$ /app_dev.php/$1 last;
    }

    location ~ ^/(app|app_dev)\.php {
        fastcgi_index $1.php;
        fastcgi_pass unix:/var/run/php5-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;

        # Added to avoid 502 Bad Gateway errors
        fastcgi_buffer_size 512k;
        fastcgi_buffers 16 512k;
    }

    #HTTP 304 NOT CHANGED 
    location ~* \.(css|txt|xml|js|gif|jpe?g|png|ico)$ {
        expires 1y;
        log_not_found off;
    }
}

Https website

If your website needs secure communications between your server and the client, you will need to enable ssl on your webserver. The vhost below redirects every http requests to the https equivalent. It also configures your server to use a custom certificate that you would have put in the conf.d directory.

server {
    listen      80;
    root        /var/www/https;
    server_name nginx-https.demo;

    #Permanent redirection
    return 301 https://nginx-https.demo$request_uri;
}

server {
    listen      443 ssl; #default https port
    root        /var/www/https/;

    access_log /var/log/nginx/https.access.log;
    error_log /var/log/nginx/https.error.log;

    server_name         nginx-https.demo;
    ssl_certificate     conf.d/myssl.pem;
    ssl_certificate_key conf.d/myssl.key;
    ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers         ALL:!EXP:!LOW:!DSS:!3DES:!PSK:!aNULL:!eNULL:!RC4:HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers   on;
}

Most of the time, for production you won't generate your custom certificate but you'll buy one. Have a look at the nginx's documentation for more information.

Reverse proxy

chart reverse proxy

There are many cases where you can need a reverse proxy. Nginx is known to make it very easy the use of reverse proxies. At Theodo, we use it in most of our AngularJs apps that call a node server through a reverse proxy.

Here is an example where everything is proxified to the 8080 port.

server {
        listen 80;
        server_name nginx-proxy.demo;

        #Every url starting by '/' are proxified to 127.0.0.1:8080
        location / {
                #You can specify which dns server or /etc/hosts file to resolve the domain
                resolver localhost; 

                #Set the header to keep the IP of the client
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

                proxy_pass http://127.0.0.1:8080;
        }
}

#You can put here a lot of services that don't listen to the port 80. Like a node server.
server {
        listen 8080;
        root        /var/www/simple-proxy/;

        access_log /var/log/nginx/proxy.access.log myCustomLog;
        error_log /var/log/nginx/proxy.error.log;

}

The proxy_pass directive is the most important directive. It's here that you specify the machine and the port where you want to proxify the request. Behind a proxy, you might want to modify the way your logs are recorded. I have here chosen a custom format for the access_log.

You can create your own log_format by creating a myCustomLog.conf file in the conf.d directory

log_format myCustomLog 
    '$remote_addr forwarded for $proxy_add_x_forwarded_for - $remote_user [$time_local] '
    '"$request" $status $body_bytes_sent '
    '"$http_referer" "$http_user_agent"'
    'real_ip $http_x_real_ip';

Here is a quick list of nginx variables that you can use (The complete one is here).

  • $remote_addr The remote host
  • $remote_user The authenticated user (if any)
  • $time_local The time of the access
  • $request The first line of the request
  • $status The status of the request
  • $body_bytes_sent The size of the server's response, in bytes
  • $http_referer The referrer URL, taken from the request's headers
  • $http_user_agent The user agent, taken from the request's headers

Sandbox

We have seen some examples of nginx vhosts that will cover most of your basic needs. I have also made a sandbox with Vagrant and Ansible to allow you to easily test different nginx configurations. There is even a little exercise in the README.

If you have some ideas for pertinent vhosts or exercises don't hesitate to make a PR!

Maxime Thoonsen

Maxime Thoonsen

Theodo - CTO