Running a Simple Web Server on Debian

This how-to describes how to set up a web server serving a static html website. Debian GNU/Linux is used as the operating system so every command can be used also in Raspberry Pi's Raspbian system. I am using lightweight nginx for this reason too.

Created
September 5, 2016

More information

I was inspired especially by the following articles. It's easy to find another great tutorial using any web search engine.

Install nginx and Prepare Directories

Web server needs to be installed to serve our web pages to the world. Proper permissions must be set on these pages and their directories in order to be accessible but not vulnerable to attacks.

First, we need to install the nginx package and start the service.

$ sudo apt-get update
$ sudo apt-get install nginx
$ sudo /etc/init.d/nginx start

You can try typing your server's IP address into your browser address bar, for example http://192.168.1.1, to verify it's working.

I will be running two websites in this example, example.com and example.net, so I need to create directories for storing their files. The directories will be placed in /srv. Debian/nginx default locations are /var/www/html or /usr/share/nginx/html. Nginx uses www-data user for accessing the files.

$ sudo mkdir -p /srv/www/example.com/html
$ sudo mkdir -p /srv/www/example.net/html
$ sudo chown -R "$USER":www-data /srv/www

The website data will need to be copied into the html directories. You can do it now or later.

Next step is to set SGID bit on all website directories for the files to be owned by www-data group. I recommend checking all permissions as well.

$ sudo find /srv/www -type d -ok chmod g+s {} \;
$ #or just 'sudo chmod g+s /srv/www' if www directory is empty

I will also add the user into the www-data group.

$ sudo usermod -a -G www-data "$USER"

Configure nginx

Now we will tell nginx where our web files are saved and which files belong to which web address. One IP address can contain many websites with different host names.

Server blocks are located in /etc/nginx/sites-available, enabled servers are then symlinked to /etc/nginx/sites-enabled.

$ cd /etc/nginx/sites-available

Make copy of the default file. Actually, we need two copies.

$ sudo cp default default-my
$ sudo cp default www-com

The default file is divided in two parts, in default-my we need to keep the first part specifying the default block to be used when no host is specified on page request. In www-com we will keep the second, now commented-out, part.

Edit our www-com file.

$ sudo nano /etc/nginx/sites-available/www

Uncomment the block between curly braces. Then modify server_name, in my example server_name www.example.com example.com; specify file location root /srv/www/example.com/html; and index index.html;

Now make copy of www-com file as www-net and modify server name to server_name www.example.net example.net; in it.

Now modify some global settings.

$ sudo nano /etc/nginx/nginx.conf

Modify value of worker_processes to match the number of your CPU cores (or set to auto). It might be also useful to increase server_names_hash_bucket_size to 64.

Disable the original default virtual server and enable all ours.

$ sudo rm /etc/nginx/sites-enabled/default		
$ sudo ln -s /etc/nginx/sites-available/www-com /etc/nginx/sites-enabled/
$ sudo ln -s /etc/nginx/sites-available/www-net /etc/nginx/sites-enabled/

And now tell nginx to reload the configuration. There are multiple ways of doing this, the following commands should have the same effect.

$ sudo /etc/init.d/nginx reload
$ sudo service nginx reload
$ sudo systemctl reload nginx

Make It a Little Bit More Secure

It might be a good idea to not show our nginx version to everyone on error pages. Uncomment server_tokens off in /etc/nginx/nginx.conf. Then, add the following lines below the server-tokens setting.

client_max_body_size  4096k;
client_header_timeout 10;
client_body_timeout   10;
keepalive_timeout     10 10;
send_timeout          10;

Set key authentication for ssh and disable password authentication. A good article is on Ubuntu help. In short, run these commands. Be careful when generating new client key as it deletes the existing one.

$ ssh-keygen -t rsa
$ ssh-copy-id user@192.168.1.1

Password authentication can be disabled by adding PasswordAuthentication no to /etc/ssh/sshd_config. It should not be needed to install fail2ban because no one should be able to guess our key.

We haven't touched our default-my file in /etc/nginx/sites-available yet. I want to forbid direct IP access to my server. The contents of default-my file would be as follows.

server {
    listen 80;
    server_name _;
    return 404;
}

We can replace return 404; with return 403; or better, deny all;.

Finally, we can set up a firewall and point DNS records to our IP address.

$ sudo apt-get install ufw
$ sudo ufw default deny incoming
$ sudo ufw default allow outgoing
$ sudo ufw app list
$ sudo ufw allow 'Nginx HTTP' #or sudo ufw allow 80 or sudo ufw allow http
$ sudo ufw allow 22 #or sudo ufw allow ssh
$ sudo ufw enable ### BE CAREFUL ###
$ #You can verify the change by typing:
$ sudo ufw status verbose

Some Tweaks

We can log events separately for every server block. Write access_log /var/log/nginx/access.log; or error_log /var/log/nginx/error.log error; note: error level must be set if specified in server block.

Sometimes it can be useful to redirect example.com to www.example.com. More info in this Stack Overflow discussion. Check this nginx documentation too.

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

        server_name example.com;

        return 301 $scheme://www.example.com$request_uri;
}

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

        server_name www.example.com;
        ...
}

We can also install goaccess log viewer.

sudo apt-get install goaccess

Examples of Config Files

We should end up with something like this.

This is our /etc/nginx/sites-available/default-my file.

# Default server configuration, file default-my
#
server {
	listen 80 default_server;
	listen [::]:80 default_server;

	server_name _;

	return 404;
}

And /etc/nginx/sites-available/www-com. Now example.com and www.example.com are two websites with the same content.

# Virtual Host configuration for example.com, file www-com
#
# You can move that to a different file under sites-available/ and symlink that
# to sites-enabled/ to enable it.
#
server {
	listen 80;
	listen [::]:80;

	server_name www.example.com example.com;

	root /srv/www/example.com/html;
	index index.html;

	error_page 404 /err404.html;

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

	access_log /var/log/nginx/example.com/access.log;
	error_log /var/log/nginx/example.com/error.log error;
}

In this configuration of /etc/nginx/sites-available/www-com, example.com is redirected to www.example.com.

# Virtual Host configuration for example.net, file www-net
#
# You can move that to a different file under sites-available/ and symlink that
# to sites-enabled/ to enable it.
#
server {
        listen 80;
        listen [::]:80;

        server_name example.com;

        return 301 $scheme://www.example.com$request_uri;
}

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

	server_name www.example.com;

	root /srv/www/example.com/html;
	index index.html;

	error_page 404 /err404.html;

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

	access_log /var/log/nginx/example.com/access.log;
	error_log /var/log/nginx/example.com/error.log error;
}