(Web) Serving Raspberry Pi: Part 2 - nginx
Welcome to part two of my (Web) Serving Raspberry Pi series, this time we’ll be setting up the Web Server itself, using nginx.
nginx?
“Why nginx? What about Apache?”
Well, I’m ultimately intending to use Ghost and they strongly recommend it, but there are quite a few other great reasons to use nginx over Apache.
Performance and scalability
There’s a great article about it over on the nginx site, but in a nutshell: nginx is lighter weight and faster, perfect for a Raspberry Pi. Actually, that makes perfect sense because nginx was specifically designed to be faster and more scalable than Apache. Well, some would argue about that till they are blue in the face, but nginx most certainly is faster out of the box. That’s not to say that Apache can’t be pretty nippy too, it just takes a lot more tweaking and optimisation to make it competitive, and that removes one of Apache’s main benefits - the comparative ease of setting it up.
Dynamic content
While nginx is the clear winner for static content, you could spend days arguing over which is best for serving dynamic content.
Apache (as it comes from the Raspbian package) benefits from having a lot of the more popular methods for serving dynamic content either built in out of the box or activated very easily via a module (such as mod_php). Whereas nginx offloads dynamic content to an external processor (e.g. php-fpm).
Because of the ease of activating modules, Apache certainly wins for simplicity when it comes to dynamic content. By way of example: It’s a lot easier to setup php on Apache than it’s is on nginx. However, there are benefits to offloading dynamic content to an external processor. It means nginx only needs to call it when absolutely necessary, making it theoretically faster most of the time. Apache can be setup that way, but again it loses the ease of configuration advantage.
Conclusion
To cut a long story short and over simplify matters somewhat: If you don’t fancy a deep dive into configuration and optimisation, nginx will give you the best performance.
nginx!
Ok, so we’re using nginx, now what? We install it of course! As previously you will need to run most of the following with root privileges (or just use sudo
before each of the commands below):
sudo -s
Install nginx
This part is quite straight forward, because Raspbian contains a package for nginx. Therefore, we just need to install that:
apt-get install nginx
Once nginx is installed successfully you should be able to point a browser to your Raspberry Pi’s IP address (run hostname -I
if you don’t know what it is) and see the default nginx page.
Configure nginx
While it runs perfectly well at this stage we need to start adjusting nginx for our needs. The main configuration file for nginx is nginx.conf
, so we begin by editing that:
nano /etc/nginx/nginx.conf
Start by changing # multi_accept on;
to multi_accept on;
, this uncomments the line and allows nginx to accept as many connections as it can, rather than one at a time. Next we should tweak compression by adding:
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;
to the gzip section of nginx.conf
. Here we are making sure that only files over a certain size are compressed, setting the gzip buffers, and ensuring we compress as many appropriate types of file as possible.
Next we change # server_tokens off;
to server_tokens off;
. Uncommenting this line prevents nginx from reporting it’s version number. Advertising what version you are running isn’t the best idea from a security standpoint, we want to at least make the hackers work for it.
There are a few other tweaks which I use. They mostly relate to my particular usage, so may or may not be appropriate for you. For example:
client_max_body_size 4096k;
client_header_timeout 10;
client_body_timeout 10;
keepalive_timeout 10 10;
send_timeout 10;
server_names_hash_bucket_size 64;
client_max_body_size
tweaks the maximum size of the client request body (which can restrict things like file uploads), I’ve increased it to 4MB from the default of 1MB.
The *_timeout
settings make sure the server doesn’t timeout when we don’t want it to, and does when we do. You can find more information on the specifics of each setting in the nginx documentation. In this case I have decreased the default timeouts to make the site less vulnerable to distributed denial of service attacks (DDoS), by limiting the amount of time nginx waits for slow requests.
Finally server_names_hash_bucket_size
is increased to 64 simply because I have an unusually long server name.
At this point it’s probably wise to make sure everything is still working as expected, by reloading nginx:
systemctl reload nginx
Add your site(s)
nginx is now ready for us to start adding sites. As things stand nginx will only serve static html (Though once properly configured, nginx will serve things like php and/or whatever else you may wish). Our html lives in /var/www/
(for example /var/www/mysite/
) and you will find /var/www/html/
already hosts the default nginx site, which contains a simple html page.
However nice it may be to tinker around with the default site, you will want to start setting up your own at some point. To do that the important thing to realise is that you need to tell nginx where to find your site’s files and what to do with them. Therefore you need to start editing /etc/nginx/sites-enabled/
and /etc/nginx/sites-availible/
. You will find the default site’s configuration files /etc/nginx/sites-enabled/default
and /etc/nginx/sites-availible/default
are already populated, where the former is a symbolic link pointing to the latter.
To quickly summarise the difference between to two locations: sites-enabled
contains your currently active sites, whereas sites-availible
contains all of your available sites, which are activated by symlinking them from sites-enabled
. For example:
ln -s /etc/nginx/sites-available/mysite /etc/nginx/sites-enabled/mysite
would enable mysite
(assuming you have a mysite
configuration in sites-availible), and:
rm /etc/nginx/sites-enabled/default
would disable the default site. The default site includes pretty much everything you need to get going, if you have a look in the file:
nano /etc/nginx/sites-available/default
You will see a bunch of stuff about servers, locations etc. and it will look a little something like this:
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/html;
# Add index.php to the list if you are using PHP
index index.html index.htm index.nginx-debian.html;
server_name _;
location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
try_files $uri $uri/ =404;
}
}
Let’s take a closer look at the contents of that file:
listen 80 default_server;
listen [::]:80 default_server;
This tells nginx to listen on port 80 (the default http port), these two lines cover IPv4 and IPv6 respectively.
root /var/www/html;
This tells nginx where the root of your site is, in other words: Where your site’s html files are.
index index.html index.htm index.nginx-debian.html;
The index directive tells nginx what files will be used as an index, they are checked in the order specified i.e. index.html will be checked before index.htm.
server_name _;
This is where you’d specify your domain if using your own, in my case that’s:
server_name megalomaniacslair.co.uk www.megalomaniacslair.co.uk;
You can also use wildcards and regular expressions. However, you should use exact names where possible, see the nginx documentation for more information on why that’s the case.
location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
try_files $uri $uri/ =404;
}
The location blocks tell nginx how to process the request URI (the part of the request that comes after the domain name or IP address/port). The default setup simply serves the files it finds and displays a 404 if it finds none to serve.
If all you want is to serve basic HTML via HTTP then that’s pretty much all there is to it. Simply copy and adjust for however many sites you have, start adding your HTML into /var/www/yoursite
and you’ll be serving your sites with nginx in no time.
However, serving basic HTML pages via HTTP is only the staring point for us. When you’re hosting webpages on the big bad Internet you need to consider things like security and privacy, you may also need to serve dynamic content (e.g. PHP, Node.js, etc.). Tune in next time when I’ll be looking at securing your connections with encrypted HTTPS.
Next time: (Web) Serving Raspberry Pi: Part 3 - HTTPS with Let’s Encrypt