Pi Web Server – Initial Setup

Welcome to my corner of the interwebs. This is my nth version of chrislhood.com and this go ’round I’m focusing it on the major interests and outlets of my life. Lately (#2015) I’ve been on a Raspberry Pi kick. These little Linux boxes are amazing little gadgets have a limitless range of uses and I have yet to wrap up a Pi project without being impressed at what they’re capable of helping me create.

Most recently I’ve been focused on the Zeros. For $5 it’s hard not to be completely blown away by these computers.

My latest adventure has been to roll my own web server to host the latest iteration of this site.

Requirements for this project are:

  • All hardware housed locally
    • Raspberry Pi Zero W
    • Cable Modem
    • Airport Extreme Router
  • Reliable website server stack
    • Linux OS (Raspbian in this case)
    • Nginx
    • MySQL (MariaDB)
    • PHP7.0
  • Build site from 100% open-source technology
    • WordPress & assorted plugins
    • PHPMyAdmin
  • Track stats
    • Google Analytics
    • Google Webmaster Tools
  • Host email services
    • Work in progress 🙁
  • Encryption
    • Let’s Encrypt SSL
  • Provide good overall user experience through design best practices and adequate page load times
    • Currently registering about 1.5-2 second page loads

So those are the basic requirements, let’s talk about how we got to where we are now.

Web hosting is relatively cheap nowadays. You can find some hosts who provide great SLAs for web & email hosting, database management and host of other services. Some even including one-click installs of applications like WordPress or even full blown e-commerce solutions like Magento. I like a challenge though, so I decided to try to build it all myself, at home. On a $5 Raspberry Pi Zero W.

Set up the Pi

First things first. Run to https://www.raspberrypi.org/downloads/ and grab yourself a copy of the latest version of Raspian. I chose the Lite version for a couple of reasons. Firstly, I’m comfortable with the command line interface and don’t need a GUI for any of this work. I didn’t see any benefit of having desktop environment for this project. I also intend to run this Pi completely headless (no attached keyboard, mouse or monitor). But most importantly I didn’t want the excess overhead from running a full desktop environment on this Pi that could possibly slow down its ability to serve up web pages to visitors.

Next, find a way to get load Raspian to your SD card. My personal app of choice for this is Apple Pi Baker. It’s rock solid, lightweight and fast.

At this point, it’s helpful to go ahead and set up your network settings so you can get it online automatically once it’s booted. To get that configured just open your favorite text editor or Terminal and:

1
sudo nano /rootfs/etc/wpa_supplicant/wpa_supplicant.conf

and enter your network’s info at the bottom of the file:

1
2
3
4
network={
ssid="YourNetworkName"
psk="YourNetworkPassword"
}

Once that’s handled, transfer your SD card to your Pi and power it up!

Once your Pi has booted (first boot may take a couple of minutes or more), find it on your network and make note of its IP address. There are number of tools available out there to hunt down a Pi on your network. Recently I’ve been using Adafruit’s PiFinder app and have been pretty pleased with it.

Note the IP address and fire up your favorite SSH utility to connect. I use iTerm2 personally, but Terminal is a good option for Mac users. I hear Windows guys talk about Putty a good bit too. Then connect to your Pi with

1
ssh pi@YOUR_PI_IP

Your default password is raspberry

Once connected, run

1
sudo raspi-config

to set up your Pi. The most important steps here are to change your Pi’s password and expand the file system so Raspian can use your entire SD card.

Once the settings are to you likings, reboot your Pi and reconnect.

Once logged back in let’s update the repos so we can grab the latest versions of the software we need

1
sudo apt-get update -y && sudo apt-get upgrade -y

Now your Pi is ready to rock. Let’s start building a server!

Creating a LEMP stack

For my project, I chose to run a LEMP stack as opposed to a LAMP stack due to Nginx’s ability to serve pages faster than Apache on machines with limited resource. If I were running a server with more horsepower and had to perform more complex work I’d probably go with Apache.

Full disclosure, I compiled this set of instructions from various sources I found during my research on this project and I like to give credit where it’s due. The bulk of this part of the build came from https://www.pestmeester.nl/

Install the web server

Here we need to install the basics for our server. With this line we’re installing Nginx, PHP and a few extras we need and MySQL.

1
sudo apt-get install nginx php7.0-fpm php7.0-curl php7.0-gd php7.0-cli php7.0-mcrypt php7.0-mysql mysql-server

Be sure to set a super secret, super secure password for your root user when configuring MySQL. I like to use Secure Password Generator for this. Keep it tucked away in a safe place once you’ve generated your password.

Secure your Pi

Next let’s create a .pem file so we can connect securely and ditch using a password to ssh into the server.
This is going to take a while. A long while. So let’s set up Screen so we can run this in the background while we do other web servery things

1
sudo apt-get install screen -y

Once that’s installed

1
screen

Follow the on screen prompts to get to a new command prompt. With this utility installed, you can run instructions that

1
cd /etc/nginx

then

1
sudo openssl dhparam -out dh4096.pem 4096

Feel free to go ahead and close that terminal window for now. Remember you can always reload it to check on your progress with

1
screen -r

Once the pi has finished generating the file, let’s make another new file to house all the info we need for Perfect Forward Secrecy. Once that’s done we’ll load it into the Nginx configuration. This will protect all websites we end up hosting on the Pi.

1
sudo nano perfect-forward-secrecy.conf

Paste this into the new file:

1
2
3
ssl_ciphers "ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS";
ssl_dhparam /etc/nginx/dh4096.pem;
add_header Strict-Transport-Security "max-age=31536000; includeSubdomains;";

Save and exit your perfect-forward-secrecy.conf file.

Now we need to add this file to nginx.conf so that it will load with the service.

Configure nginx.conf

Open your Nginx config file:

1
sudo nano /etc/nginx/nginx.conf

Inside the http { … } block un-comment the ‘server_tokens off’ line;

Enabling this line stops nginx from reporting which version it is to browsers which helps prevent prying eyes from finding potential exploits to hack your server.

Comment the line:

1
keepalive_timeout: 65;

by placing a # in front of it. We’ll add a better setting in a bit

#keepalive_timeout 65;
Uncomment the line:

1
#server_names_hash_bucket_size 64;

by removing the # from in front of it. Again, we have some better settings for this.

Next, make sure these lines are uncommented:

1
2
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;

Add GZip compression

GZip helps to reduce page load times by compressing the files before they are sent over the network which mean faster transfer. Update your settings to match:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
##
# Gzip Settings
##

gzip on;
gzip_disable "msie6";

gzip_min_length   1100;
gzip_vary         on;
gzip_proxied      any;
gzip_buffers      16 8k;
gzip_comp_level   6;
gzip_http_version 1.1;
gzip_types        text/plain text/css applciation/json application/x-javascript text/xml application/xml application/rss+xml text/javascript images/svg+xml application/x-font-ttf font/opentype application/vnd.ms-fontobject;

Here’s a quick breakdown on what we’re outlining for GZip:
gzip_min_length: only Gzip files above a certain size
gzip_comp_level: setting how compressed to be in gzip_comp_level. It accepts values from 1 to 9; 1 is the least compressed but fastest to compute. 9 is the most compressed but slowest to compute. With the Pi being a low power CPU I’ve chosen a middle-ground value of 6.
gzip_types: telling GZip which filetypes to compress.

Next let’s add an include to the perfect-forward-secrecy.conf file we made in the section Virtual Host Configs:

1
2
3
4
5
6
7
##
# Virtual Host Configs
##

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

Hardening Nginx

Finally we’ll harden nginx against DDOS attacks by setting some a few more parameters. These lines should go inside the http block, just before the end bracket }

1
2
3
4
5
6
7
8
##
# Harden nginx against DDOS
##

client_header_timeout 10;
client_body_timeout   10;
keepalive_timeout     10 10;
send_timeout          10;

These settings limit the amount of time Nginx will wait for client connections. Keeping these short means that it’s a bit harder to flood Nginx into a state of unresponsiveness. Be sure to save all your changes!

Create fastcgi_params

We need to set a the defaults for Nginx when we want to use PHP with it. Enabling PHP support is not a global change, instead we can enable Nginx to use PHP for specific virtual hosts, or even for specific directories within a specific virtual host. To set up some nice working defaults we can import into virtual hosts:

1
sudo nano /etc/nginx/fastcgi_params

Now, make sure your file looks just like the one below (which is taken directly from the official nginx wiki article)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
fastcgi_param   QUERY_STRING            $query_string;
fastcgi_param   REQUEST_METHOD          $request_method;
fastcgi_param   CONTENT_TYPE            $content_type;
fastcgi_param   CONTENT_LENGTH          $content_length;

fastcgi_param   SCRIPT_FILENAME         $document_root$fastcgi_script_name;
fastcgi_param   SCRIPT_NAME             $fastcgi_script_name;
fastcgi_param   PATH_INFO               $fastcgi_path_info;
fastcgi_param   REQUEST_URI             $request_uri;
fastcgi_param   DOCUMENT_URI            $document_uri;
fastcgi_param   DOCUMENT_ROOT           $document_root;
fastcgi_param   SERVER_PROTOCOL         $server_protocol;

fastcgi_param   GATEWAY_INTERFACE       CGI/1.1;
fastcgi_param   SERVER_SOFTWARE         nginx/$nginx_version;

fastcgi_param   REMOTE_ADDR             $remote_addr;
fastcgi_param   REMOTE_PORT             $remote_port;
fastcgi_param   SERVER_ADDR             $server_addr;
fastcgi_param   SERVER_PORT             $server_port;
fastcgi_param   SERVER_NAME             $server_name;

fastcgi_param   HTTPS                   $https;

# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param   REDIRECT_STATUS         200;

Setting Up & Securing MySQL

MySQL ships with a few conveniences that are supposed to be removed when put on a real server, to do that type:

1
sudo mysql_secure_installation

Run through each setting, selecting the defaults. Be sure to use a secure password for your new root user login. I prefer to use a 32 character random password from Secure Password Generator In my experience MySQL doesn’t like special characters in passwords so be sure to uncheck that option when generating your password.

Once this is done you’re ready to build your website.

Chris Hood

Chris Hood is an internet marketer with more than 15 years experience in email, organic and paid search marketing for e-commerce businesses. Chris spends most of his downtime riding bikes, tinkering with Raspberry Pis and updating this website. Say hey to Chris sometime.

One thought on “Pi Web Server – Initial Setup

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.