From charlesreid1

I finally got fed up with Apache's endless permissions problems and incomprehensible labyrinth of config files and virtualhosts that never, ever work, ever, not even a single time.

I switched to nginx.

Also see Gunicorn page for running Python apps on top of nginx.

You can also run nginx with Apache. I did this because, as much as I hate Apache, all things PHP (MediaWiki and Wordpress and Mysqlaadmin) integrate better with it. So I set up nginx as a reverse proxy, so that any requests sent to nginx that are for the wiki or for wordpress are sent through to apache. Nginx Apache page has details.

Installing

Ubuntu

sudo apt-get install nginx

Basic Info

By default, nginx serves files out of

/usr/share/nginx/html

The default config file is located in

/etc/nginx/sites-available/default

To start/stop nginx, use it as a service,

sudo service nginx start
sudo service nginx stop

It is generally a good idea to add virtual servers into the default configuration file, despite what I do below.

Virtual Hosts

Directory Structure and Permissions

I have created two http root directories, to serve two virtual hosts:

/www/example.com/public_html/
/www/test.com/public_html/

Both contain an index.html file with a simple hello world message.

Now I transfer ownership of these two directories to my regular username,

sudo chown -R $USER:$USER /www/example.com/public_html
sudo chown -R $USER:$USER /www/test.com/public_html
sudo chmod -R 755 /www/

Config File

Create a copy of the config file for each site:

sudo cp /etc/nginx/sites-available/default /etc/nginx/sites-available/example.com
sudo cp /etc/nginx/sites-available/default /etc/nginx/sites-available/test.com

The contents:


example.com config file

/etc/nginx/sites-available/example.com
--------------------------

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

    root /www/example.com/public_html;
    index index.html index.htm;

    server_name example.com www.example.com;

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


test.com config file

/etc/nginx/sites-available/test.com
--------------------------
server {
    listen 80 default_server;
    listen [::]:80 default_server ipv6only=on;

    root /www/test.com/public_html;
    index index.html index.htm;

    server_name test.com www.test.com;

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


Enabling Site/Site Config

To enable the site whose config files we just created, we create symlinks in nginx's sites-enabled:

sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/test.com /etc/nginx/sites-enabled/

Then restart the nginx service:

sudo service nginx restart

Changing Hash Bucket Size

Mentioned by [1]. Edit the nginx config file:

sudo vim /etc/nginx/nginx.conf

and uncomment this line:

server_names_hash_bucket_size 64;

Restarting nginx to Implement Changes

sudo service nginx restart

Works

Works like a dream!

Virtual Hosts Revisited

I wasn't able to successfully implement virtual hosts with nginx using the procedure above, so I gave it another shot using the procedure outlined below.

Domain Records

I have two domains, doxuments.com and doxcompany.com, through NameCheap. I log in to name cheap and change the domain records of each domain.

For each domain, I create an A Name record for @ and an A Name record for www. Both point to the IP address of the server running nginx.

This makes two IP addresses, with two A Name records each, all pointing to the same IP address.

A.B.C.D

Set Permissions

I have two sites, served by a single instance of nginx out of two directories, /www/domain.com and /www/domain2.com. The permissions on these folders are set following [2]:

$ sudo chown -R www-data:www-data domain.com/ domain2.com/

$ sudo chmod 755 domain.com/ domain2.com/

Now onto the nginx settings.

Nginx

I change the domain records so that I have two domains, domain.com and domain2.com, pointing to the same server - one single IP address.

$ cd /etc/nginx/sites-available/

$ cat domain.com
server {
    listen 80;
    listen [::]:80;

    root /www/domain.com/htdocs;
    index index.html index.htm;

    server_name domain.com;

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

$ cat domain2.com
server {
    listen 80;
    listen [::]:80;

    root /www/domain2.com/htdocs;
    index index.html index.htm;

    server_name domain2.com;

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

domain.com points to /www/domain.com/htdocs and domain2.com points to /www/omain2.com/htdocs. Couldn't be simpler.

Enable site

I enable these sites by copying these sites-available files to the sites-enabled folder:

$ cd /etc/nginx/sites-enabled 

$ sudo ln -fs ../sites-available/domain.com

$ sudo ln -fs ../sites-available/domain2.com

Restart Nginx

Now restart the nginx service.

$ sudo service nginx restart

Works!

And whaddya know, it works.

Using /www

If you want to use a particular directory structure, like /www/htdocs, you can do it this way:

Edit the file corresponding to the desired site name, something like /etc/nginx/sites-available/basic. Change the line:

    root /www/htdocs;

to reflect whatever directory structure you want. Then restart the service:

sudo service nginx restart

and you're off!


Secure Directories

Add Secure Directory to Nginx Config File

To secure a directory, add the following to your nginx sites-available configuration file's server block:

server {

    [....]

    location /media {
        root /www/;
        index index.html index.htm;
        autoindex on;

        # htaccess
        auth_basic "Restricted";
        auth_basic_user_file /etc/nginx/.htpasswd;
    }

    [.....]
}

Create Credentials for Secure Directory

Now you can create user credentials in the file /etc/nginx/.htpasswd and those credentials can be used to access the restricted location.

To add user to a new httpassd file:

htpasswd -c /etc/nginx/.htpasswd username 

you will then be prompted for a password.

If you already have a password file:

htpasswd /etc/nginx/.htpasswd username 

This will prompt you for a password. It will then be appended to the existing password file.

Using HTTPS/SSL with Nginx

!!!!!!!!!!!!!!!!!!!!!!! WARNING: OUTDATED INFORMATION. USE LETS ENCRYPT. !!!!!!!!!!!!!!!!!!!!!!

This is a three step procedure.

First, you'll create a self-signed certificate request for a commercial SSL certificate company to grant you a certificate.

Second, you'll purchase a certificate from a third party. I used thesslstore.com and was pretty happy with the price. It'll set you back about $18 for one year or $40 for three years, which all in all is pretty reasonable, considering these things can be thousands of dollars.

Third, you'll use the certificates provided by the Certificate Authority (thesslstore.com in this case) with the key you used to create the certificate request to do SSL with Nginx. This will require a few changes to the Nginx configuration file.

So, let's get started!

Creating Self-Signed Request

Equivalent to saying "trust me" at the end of every sentence.

Linux.com guide to creating a self-signed certificate: https://www.linux.com/learn/creating-self-signed-ssl-certificates-apache-linux

This will result in a key file (private key) and a certificate file (public key). These are specified in the server block that listens on 443 and implements HTTPS/SSL.

Purchasing Certificate

This is another process that broke down into several steps.

First, purchase a cheap certificate.

Then, verify I am the owner of the web domain.

Next, submit my certificate request.

Finally, download and extract the certificates.

Using Certificates with Nginx HTTPS/SSL

See [3] and [4]

HTTPS Most Of The Things

Use a 301 to redirect requests to a safer and more secure protocol.

For example, you can turn specific requests into HTTPS, e.g., a login page:

server {
    listen 80;
    ...
    location /login {
        return 301 https://domain.com$request_uri;
    }
    ...
}

Alternatively, you can turn everything into HTTPS:

server {
      listen 80 default;
      server_name www.domain.com domain.com
      access_log off;
      error_log off;
      return 301 https://$server_name$request_uri;
}

Nginx and Git

Redirecting Subdomains to Ports: Gitea

If you have apps running on other ports (I have a pseudo-Github service called Gitea running on port 3000), you can redirect certain requests to that port and let that app handle serving up HTTP content. Note this is similar to the way we reverse-proxy requests to a local-only Apache server and let Nginx handle caching, whitelisting, etc.

I created an A Name record for a subdomain, git.domain.com, to point to the server running Nginx. I then created a server block that listens for requests for that subdomain, and redirects requests to HTTPS on port 3000, which is where the Gitea service takes over. It uses Go, and has an HTTP handler built in. Requests to git.domain.com are rewritten to domain.com:3000 and sent off to Gitea.

Gitea expects HTTPS requests, but git.charlesreid1.com expects HTTP or HTTPS. So add two nginx configuration blocks:

server {
    # Rewrite requests for git.domain.com to https://domain.com:3000
    listen 80;
    server_name git.domain.com;
    return 301 https://domain.com:3000$request_uri;
}

server {
    listen 443;
    server_name git.domain.com;
    location / {
        proxy_pass https://domain.com:3000;
    }
}

Note that these can be defined in addition to any other servers listening on the same ports (80 and 443). They cannot and should not, however, listen on the same port as the app they're directing to (3000 in this case). One app at a time...

Note

Question: is it possible for nginx to intercept requests for particular files, and serve as a kind of proxy server? (Maybe that answers my question - maybe you need a proxy server.)

Flags