Install nginx

From UNPM.org Wiki
Jump to: navigation, search

This article describes the installation and configuration of nginx onto Ubuntu Server 18.04 and is part of a series of articles covering creating a LEMP web server.

Nearly all of the commands in this article require root privileges, so elevate the session to a root session:

username@servername:~$ sudo -i

Install and configure nginx

Install nginx

For this installation and server configuration, a popular Personal Package Archive (PPA) will be added as the repository for nginx:

root@servername:~# add-apt-repository ppa:nginx/stable && aptitude update
root@servername:~# aptitude install nginx
root@servername:~# nginx -V

It is also an option to install the ppa:nginx/development PPA for those desiring the development version of nginx, now called 'mainline' version.

The nginx -V command will list the installed version of nginx and its compiled nginx modules.

Configure nginx

The main configuration file for nginx is nginx.conf. This nginx PPA is configured such that nginx.conf is generally only used for settings affecting how nginx runs and not site-specific rules. The default nginx.conf file contains many obsolete and unnecessary directives, so it won't be used. Make a backup of the original nginx.conf file and create a new one:

root@servername:~# mv /etc/nginx/nginx.conf /etc/nginx/original.nginx.conf
root@servername:~# nano /etc/nginx/nginx.conf

Paste into the new file:

user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
        worker_connections 1024;
        multi_accept on;
}

http {
       sendfile on;
       tcp_nopush on;
       tcp_nodelay on;
       types_hash_max_size   2048;
       server_tokens off;
       client_max_body_size  20000k;
       client_header_timeout 10;
       client_body_timeout   10;
       keepalive_timeout     10 10;
       send_timeout          10;

       index index.html index.htm;

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

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

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

Change worker_processes to the number of threads supported by the CPU. Setting this to auto is an easier way to set this, though a number may be used instead and can be found using the command lscpu, the output of which will display the details of the CPU. The CPU(s): row of the output is the number of threads supported by the server.

The worker_connections setting determines the maximum number of simultaneous connections that can be opened by a worker process. 1024 is likely sufficient unless the site experiences heavy traffic. The total simultaneous connections that the server can handle will be worker_connections*worker_processes.

By enabling multi_accept, nginx processes will grab connections more aggressively. From the nginx wiki: "If disabled, a worker process will accept one new connection at a time. Otherwise, a worker process will accept all new connections at a time."[1] This can become an issue for higher traffic sites.

The client_max_body_size sets the allowed maximum size of the client request body.[2] For some common web applications, this can become an issue. 20 MB is usually sufficient.

The index directive establishes what nginx will open when a directory is called from a browser, either by specific file name or by file type, and will do this in order from left to right.

Note the line further down which states include /etc/nginx/sites-enabled/*;. This is what tells nginx to find the configuration for each site on the server. It is possible to manage sites directly from the nginx.conf file, but this is not recommended as it can get rather cluttered, which tends to increase the prevalence of syntax errors and other misconfigurations.

Configure and enable virtual servers

As mentioned above, site settings are managed in their own files instead of directly editing nginx.conf. These files need to be created and configured.

Virtual server files

The configuration file for each website is located in the /etc/nginx/sites-available/ directory, but the files here are only for the settings of each site and do not tell nginx to serve the site nor do they contain the content of a site. Nginx will only serve sites that are in the /etc/nginx/sites-enabled/ directory as per the line in the /etc/nginx/nginx.conf that states include /etc/nginx/sites-enabled/*; and content is served from the root directory stated in the server block of the symlinked sites-available file. To see this currently in action, run the following:

root@servername:~# ll /etc/nginx/sites-available/ && ll /etc/nginx/sites-enabled/

With the initial nginx setup, /etc/nginx/sites-enabled/default is symlinked to /etc/nginx/sites-available/default. Note that it is normal for symlinks to have 777 permissions and that this does not alter the permissions of the symlinked target. In /etc/nginx/sites-available/default the line root /usr/share/nginx/html; tells nginx where to get the content for the site.

Create global-configs directory

Some configuration settings are desirable for nearly all virtual servers and some configuration settings are desirable for all servers running specific options. By making files with these common settings and using include, these settings can be easily and conveniently added and managed while keeping the sites-available files easier to read.

root@servername:~# mkdir /etc/nginx/global-configs
root@servername:~# nano /etc/nginx/global-configs/http_server.conf

Paste into the new file:

location ~ /\. { access_log off; log_not_found off; deny all; }
location ~ ~$ { access_log off; log_not_found off; deny all; }

location = /favicon.ico { log_not_found off; access_log off; }
location = /robots.txt { allow all; log_not_found off; access_log off; }

gzip on;
gzip_disable "msie6";
gzip_min_length 1100;
gzip_vary on;
gzip_proxied any;
gzip_buffers 16 8k;
gzip_types text/plain text/css application/json application/x-javascript
   text/xml application/xml application/rss+xml text/javascript
   image/svg+xml application/x-font-ttf font/opentype
   application/vnd.ms-fontobject;

The first two entries prevent nginx from opening files that are a potential security risk and the second two reduce noise in the access log. The gzip directives enable compression such that there is a balance between server load and bandwidth.

Create package-configs directory

Popular web packages can be universally configured for serving on multiple server blocks by using the include directive in a given site's server block. This is another way to reduce syntax errors in virtual server configurations, make the configuration files cleaner and easier to read, and reduce the time required to correct the package configuration issues that inevitably arise.

root@servername:~# mkdir /etc/nginx/package-configs

Most packages will require two files - one for the HTTP server block and one for the HTTPS server block, the latter of which is addressed in more detail when setting up SSL/TLS.

Note that when making package-configs files, it is better to use the package name than its category. For example, when installing Vanilla Forums, using the file names vanilla.conf and vanilla_https.conf are better than using forums.conf and forums_https.conf because the same server may also host a Phorum forum, which would require a different package-configs file.

Create a virtual server

Assuming a domain is ready to go and appropriate DNS records have been created and are pointing at the server, disable the default settings and create a new sites-available file.

root@servername:~# rm /etc/nginx/sites-enabled/default
root@servername:~# mv /etc/nginx/sites-available/default /etc/nginx/sites-available/original.sites-available.file
root@servername:~# nano /etc/nginx/sites-available/example.com

Paste the following into the new file:

# HTTP server

server {
    listen 80 default_server;
    listen [::]:80 default_server;

    root /var/www/example.com/public;
    access_log /var/www/example.com/logs/access.log;
    error_log /var/www/example.com/logs/error.log;
    server_name example.com www.example.com;
    include global-configs/http_server.conf;

    location / {
        try_files $uri $uri/ =404;
    }
}

Everything in the server brackets is referred to as the server block, and the file can have more than one server block. It is good practice to have the main HTTP server block be the first server block in a sites-available file.

The first listen setting defines ports to listen to for the IPv4 address and the the second listen is for the IPv6 address. If there is no IPv6 address assigned to the server, do not set this directive.

Adding default_server tells nginx to serve this when http requests hit the server and nothing else matches the request. Note that with this setting, the server will serve this sites-available file even when someone inputs the IP address. This setting can only be applied to one server block per port on the server.

The root setting establishes the location of the root directory for the website's content. Everything in this directory will be publicly available unless special rules are added to prevent access to specific locations.

The server_name is how nginx knows which server block to serve when a request comes in. The example file above uses the common www subdomain, but server blocks can also be made for other subdomains such as forum, blog, mail, or whatever is desired, and the subdomains can be in their own server blocks so that they can have their own unique configurations.

Logrotate

Adding additional locations of the access.log and error.log files means that the current nginx logrotate file needs to be updated.

root@servername:~# nano /etc/logrotate.d/nginx

Add the following below everything currently in the file:

/var/www/*/logs/*.log {
	daily
	missingok
	rotate 36500
	compress
	delaycompress
	notifempty
	create 0640 www-data adm
	sharedscripts
	prerotate
		if [ -d /etc/logrotate.d/httpd-prerotate ]; then \
			run-parts /etc/logrotate.d/httpd-prerotate; \
		fi \
	endscript
	postrotate
#		invoke-rc.d nginx rotate >/dev/null 2>&1
		start-stop-daemon --stop --signal USR1 --quiet --pidfile /run/nginx.pid --name nginx
	endscript
}

Using the location /var/www/*/logs/*.log means that the logs will be rotated for every website added to the server, assuming the same directory tree is used. The number after rotate is the number of rotations that logrotate will keep before deleting them. 36500 will keep 100 years of logs. Log archives are stored in the same directory as the logs are created. The most recent log will always be filename.log, yesterday's will be filename.log.1, the rest will be numbered .gz archives (filename.log.2.gz), with the lower numbers being the more recently created.

Note that the log files and archives will be owned by user www-data and group adm and have 0640 attributes. This could be an issue if different user is needing to read these files.

There is currently a bug in the nginx.org PPA that affects the operation of log rotations. The nginx package installs with the commented out line not commented by default and without the line below it, which is a fix, so it is necessary to change these lines in the default log rotation block to match the new one.

Establish the website content directory tree and add content

The content of the websites will be located in a separate directory tree from the nginx configuration files. Since the server can host many sites, it is good practice to use a uniform method for storing each site's content.

The first directory in the tree will be /var/www/. It is not required that this directory be accessible to users that are members of the www-data group and it can be convenient for servers with multiple users to create sub-directories of /var/www/ for storing common files. For this purpose, an additional group webdevs is created. Current users requiring access to the website directories should be added to this new group and the www-data group.

root@servername:~# addgroup webdevs
root@servername:~# adduser username webdevs
root@servername:~# adduser username www-data
root@servername:~# mkdir -p /var/www/webdevs
root@servername:~# chown -R root:webdevs /var/www/
root@servername:~# chmod -R g+s /var/www/
root@servername:~# mkdir -p /var/www/example.com/{backup,logs,private,public}/
root@servername:~# chmod -R 775 /var/www/
root@servername:~# touch /var/www/example.com/logs/{access.log,error.log}
root@servername:~# chmod 664 /var/www/example.com/logs/{access.log,error.log}
root@servername:~# chown www-data:adm /var/www/example.com/logs/*

In this setup, the directories are given 775 permissions and files 664 to make file editing and working with SFTP more convenient.

Note that any time a user's group membership is changed, the changes will not take effect in the current session. While this will not affect the root session currently being used, if the root session is exited, the user's session will have only the group memberships it had when it logged in.

The /webdevs/ directory is created to be a convenient location to store files that members of the webdevs group can all access without cluttering up the /var/www/ directory.

Add site content

Add an index.html file to the site's root directory.

root@servername:~# cp /usr/share/nginx/html/index.html /var/www/example.com/public/
root@servername:~# chmod 664 /var/www/example.com/public/index.html

It can be a good idea to change something in the file to make it unique, this way it can be accurately determined that the page being served is not from some other previous configuration. For example, open /var/www/example.com/public/index.html and change Welcome to nginx! to Welcome to example.com! in the header and body.

Now create the sites-enabled symlink to tell nginx to serve the site:

root@servername:~# ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/example.com

It is generally a good idea to test the nginx configuration after making changes to it. Note that the test should always be run with root privileges and it is not necessary to resart the nginx service to run the test. If anything other than the following output is displayed, investigate and repair immediately:

root@servername:~# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Whenever a change is made to an nginx configuration, such as editing nginx.conf, a sites-available file, global-configs file, package-configs file, adding a sites-enabled symlink, etc. (all effectively examples of changes to the nginx.conf via the include directive), the nginx service must be reloaded for the changes to take effect. Note that this is not necessary to restart nginx for changes to site content.

root@servername:~# service nginx reload

Open the firewall and test

Verify UFW is aware of nginx and enable nginx to be available through the firewall:

root@servername:~# ufw app list
Available applications:
 Nginx Full
 Nginx HTTP
 Nginx HTTPS
 OpenSSH
root@servername:~# ufw allow 'Nginx Full'

The index.html file in /var/www/example.com/public/ should now load when pointing a browser to http://www.example.com.

The server can now serve static http websites. Not very exciting, but very fast!

Common and useful configurations

Nginx is vastly configurable with nearly unlimited options, however, there are some configurations that administrators commonly wish to employ. The below configurations are just one way to accomplish the titled configuration goal.

Configurations below that include HTTPS blocks will only function after setting up SSL/TLS with letsencrypt, which is covered in the next article.

Redirecting subdomains

It is common to serve a site primarily as either http://example.com or http://www.example.com. The following two configurations will permanently redirect browsers to one or the other.

Note that access_log is set to off to reduce log noise, but the error log is still configured. Occasionally, there may be issues related to redirection and it can be useful to create a separate error log file just for redirects since the configurations below will log errors to the domain's main error log.

Force www subdomain

To force the server to only load a domain using the www subdomain, use the following configuration.

root@servername:~# nano /etc/nginx/sites-available/example.com

Change the following in the HTTP server block:

server_name www.example.com;

Add the following server block to the bottom of the file:

# Redirect to www.example.com

server {
    listen 80;
    listen 443 ssl;
    listen [::]:80;
    listen [::]:443 ssl;
    server_name example.com;
    access_log off;
    error_log /var/www/example.com/logs/error.log;
    include global-configs/https_server.conf;
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    return 301 $scheme://www.example.com$request_uri;
}

The access_log off; configuration reduces noise in the site's access log and the error_log configuration directs errors to be logged in the site's error log instead of the general nginx error log.

For servers or domains that do not offer HTTPS, use the following:

# Redirect to www.example.com

server {
    listen 80;
    listen [::]:80;
    server_name example.com;
    access_log off;
    error_log /var/www/example.com/logs/error.log;
    return 301 http://www.example.com$request_uri;
}

Force no www subdomain

To force the server to only load a domain and not use the www subdomain , use the following configuration.

root@servername:~# nano /etc/nginx/sites-available/example.com

Change the following in the HTTP server block:

server_name example.com;

Add the following server block below the HTTP server block:

# Redirect to example.com

server {
    listen 80;
    listen 443 ssl;
    listen [::]:80;
    listen [::]:443 ssl;
    server_name www.example.com;
    access_log off;
    error_log /var/www/example.com/logs/error.log;
    include global-configs/https_server.conf;
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    return 301 $scheme://example.com$request_uri;

#    root /var/www/example.com/public;
}

The commented line should be uncommented prior to letsencrypt certificate renewal, then commented after renewal.

For servers or domains that do not offer HTTPS, use the following:

# Redirect to example.com

server {
    listen 80;
    listen [::]:80;
    server_name www.example.com;
    access_log off;
    error_log /var/www/example.com/logs/error.log;
    return 301 http://example.com$request_uri;
}

HTML redirect

Although not an nginx configuration, it may be desired to redirect a page without configuring nginx. This can be accomplished by placing the following entry between the <head></head> tags in the page source:

<meta HTTP-EQUIV="REFRESH" content="0; url=//www.example.com/location" />

Force a directory to load a subdomain

To force the server to send a user to a subdomain when requesting a directory, such as redirecting www.example.com/blog/ to blog.example.com, use the following configuration.

root@servername:~# nano /etc/nginx/sites-available/example.com

Add the following location block to the HTTP server block:

    location ^~ /directoryname/ {
        rewrite ^ http://subdomain.example.com permanent;
    }

This should be included for any server block serving the domain. For example, an associated HTTPS server block would include this location block. Note that this configuration requires a server block for the subdomain (e.g. server_name subdomain.example.com; with appropriate directive set for the server block).

Custom error pages

Using the error_page directive allows for custom pages to be displayed based on error codes, the most common being 404. Here is an example of how to use the directive for this code.

Create a webpage that will be the error page. This can be named anything, but for this example the file is /var/www/example.com/public/404.html

Open the sites-available file and add the directive to the applicable server blocks:

username@servername:~$ sudo nano /etc/nginx/sites-available/example.com

In each server block that the directive will be applicable for, add:

error_page 404 /404.html;

If several error_page directives are being used for different errors, it may be desirable to create a global-configs/error_page.conf file and use the include directive to clean up the server block code.

The nginx wiki has additional details for this directive.

Autoindex

To enable nginx to load the contents of a directory with no index file and simply list the directory's contents in a browser, use the autoindex directive:

    location /subdirectory/ {
        autoindex on;
    }

Serve IP address as page

For various reasons it may be desirable to serve a page for the server's IP address. Without this configuration, nginx will send requests for the IP address to the configured default_server.

root@servername:~# mkdir /var/www/<ipv4 address>/
root@servername:~# mkdir /var/www/<ipv4 address>/{logs,public}
root@servername:~# mkdir /var/www/<ipv4 address>/public/{<ipv4 address>,<ipv6 address>}
root@servername:~# nano /etc/nginx/sites-available/<ipv4 address>

Add to the file:

# IPv4 HTTP server

server {
    listen 80;

    root /var/www/<ipv4 address>/public/<ipv4 address>;
    access_log /var/www/<ipv4 address>/logs/access.log;
    error_log /var/www/<ipv4 address>/logs/error.log;
    server_name <ipv4 address>;
    include global-configs/http_server.conf;

    location / {
        try_files $uri $uri/ =404;
    }
}

# IPv6 HTTP server

server {
    listen [::]:80;
    root /var/www/<ipv4 address>/public/<ipv6 address>;
    access_log /var/www/<ipv4 address>/logs/access.log;
    error_log /var/www/<ipv4 address>/logs/error.log;
    server_name [<ipv6 address>];
    include global-configs/http_server.conf;

    location / {
        try_files $uri $uri/ =404;
    }
}

Create the sites-enabled symlink, then verify and load the nginx configuration.

root@servername:~# ln -s /etc/nginx/sites-available/<ipv4 address> /etc/nginx/sites-enabled/
root@servername:~# nginx -t
root@servername:~# service nginx reload

It is possible to have only one server block and one index.html file to serve both IP addresses. The above configuration supports having separate files for displaying different pages for each IP address or even log to different logfiles. To visit an IPv6 address from a browser, enclose the address in brackets (e.g., http://[<ipv6 address>]). Also be sure to update /etc/logrotate.d/nginx with a new block specifying the location of the log files.

Block POST requests

In some rare cases it may be desired to block POST requests coming from bots to specific locations or URLs. The limit_except directive can be used to block these requests.

Block requests to a server by adding limit_except to a location block:

    location / {
        limit_except GET {
            deny  all;
        }
        try_files $uri $uri/ =404;
    }

Enable HTTP basic authentication

HTTP basic authentication is a simple way to control access to websites using a published standard. The nginx basic authentication directives can be used in http, server, and location blocks, and can be turned off using the off option.

In order to create passwords, an Apache tool is first installed.

root@servername:~# aptitude install apache2-utils
root@servername:~# htpasswd -c /etc/apache2/.htpasswd user1
New password:
Re-type new password:

Note that the -c option is only used to generate the file. Passwords for additional users do not require the option.

Require authentication for server

Simply add to the http, server, or location block:

    auth_basic            "Access restricted";
    auth_basic_user_file  /etc/apache2/.htpasswd;

satisfy

The satisfy directive can be used to specifically allow or disallow based on the conditions following the directive. Used in conjunction with auth_basic, it can be possible to allow the server to perform actions through PHP, such as when WordPress performs its version of cron and quoting in Vanilla Forums. The directive can be used with the options any or all and may be placed in the server or location blocks.

   satisfy any;
   allow <serveripaddress>;
   auth_basic            "Access restricted";
   auth_basic_user_file  /etc/apache2/.htpasswd;

.htaccess

There is no option in nginx to use .htaccess files. However, much of what is accomplished through .htaccess files can be done in location blocks. There is a converter at Winginx.com that can make some conversions of .htaccess files for use in nginx, but note that it should be considered only a guide to provide a place to start, as the generated configuration will most likely not be suitable for production use. If the .htaccess file is needing to be converted for a popular package, don't forget to search the web for a possible solution.

Next step

Now that nginx is set up, it's time to configure the server for SSL/TLS.

References

External links

nginx.org

How to set up a safe and secure Web server | Ars Technica

htaccess to nginx converter

Nginx/Location - WhyAskWhy.org Includes convenient reference for regular expressions commonly used in nginx location blocks.

Regular-Expressions.info Tutorials for learning regular expressions.