Building a reverse proxy server with Nginx, Certbot, Raspbian Stretch Lite and Raspberry Pi 3
The Nginx reverse proxy server runs well on Raspberry Pi 3 and you can use it behind a router to route HTTP traffic to upstream web applications.
A Raspberry Pi 3 reverse proxy server is a very useful appliance to help us host multiple websites from home.
Some examples of web applications that you may want to host at home includes:
- WordPress site on Raspberry Pi 3
- Raspberry Pi Zero W CCTV for security surveillance
- Fast Git Server based on Go Git Service and Raspberry Pi Zero W
- Codiad Web IDE on Raspberry Pi 3
- ProjectSend for sharing files with my friends
- Python 3 Flask MVP with Supervisor on Ubuntu Server 16.04
- Jenkins running on Raspberry Pi 3 and Raspbian Stretch Lite
This post discusses how you can build a reverse proxy server with Nginx, Certbot, Raspbian Stretch Lite and Raspberry Pi 3 to proxy HTTP traffic directed at applications on your home network.
Recommended hardware list for your reverse proxy server
So what can you buy for a Raspberry Pi 3 project like this? In case you need it, the following is a list of hardware that you may want to consider to get for your reverse proxy server:
- CanaKit Raspberry Pi 3 with 2.5A Micro USB Power Supply or CanaKit Raspberry Pi 3 B+ (B Plus) with 2.5A Power Supply (UL Listed)
- Raspberry Pi 2/3 official case Black/Grey or Raspberry Pi 2/3 official case Red/White
- Sandisk Ultra 32GB Micro SDHC UHS-I Card with Adapter
- AmazonBasics RJ45 Cat-6 Ethernet Patch Cable - 3 Feet (0.9 Meters)
Setting up Raspbian Stretch Lite with SSH server enabled on your microSD card
Once you had gathered all the necessary hardware, proceed to setup Raspbian Stretch Lite with SSH server enabled on your microSD card. Doing so will allow you to SSH into your Raspbian Stretch Lite to perform further configurations in this post.
Assembling the hardware for the Raspberry Pi 3 reverse proxy server
Once the ssh file is created in the boot partition of the microSD card, remove the microSD card from your SD card reader and insert it to the microSD card slot on the Raspberry Pi 3 board. After that, proceed to assemble the Raspberry Pi 3 board to the Official Raspberry Pi case.
Starting the Raspbian Stretch Lite operating system
With the assembly of your Raspberry Pi 3 board and Official Raspberry Pi case, connect one end of the RJ45 cable to the RJ45 port on the Raspberry Pi 3 board and the other end of the cable to one of the switch port of your home router. After that, connect the micro USB power supply to the Raspberry Pi 3 board and a wall socket. Turn on the power socket to supply power to the Raspberry Pi 3 board.
Changing default password, Locale and Timezone of your Raspbian Stretch Lite
There are a few configurations that you should perform on the first run of your Raspbian Stretch Lite. Proceed on to change the default password, Locale and Timezone of your Raspbian Stretch Lite.
Installing Nginx on Raspbian Stretch Lite
Once you are done with changing the the default password, Locale and Timezone of your Raspbian Stretch Lite, the next software to install is Nginx. To install Nginx on Raspbian Stretch Lite, run the following commands:
sudo apt-get update sudo apt-get install nginx -y
Once the installation for Nginx completes, you can verify whether it is successfully started with the following command:
systemctl status nginx.service
which should give you output similar to the following:
● nginx.service - A high performance web server and a reverse proxy server Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled) Active: active (running) since Thu 2018-02-01 15:39:41 UTC; 34s ago Docs: man:nginx(8) Process: 1715 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status=0/SUCCES Process: 1712 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status Main PID: 1716 (nginx) CGroup: /system.slice/nginx.service ├─1716 nginx: master process /usr/sbin/nginx -g daemon on; master_process on; ├─1717 nginx: worker process ├─1718 nginx: worker process ├─1719 nginx: worker process └─1720 nginx: worker process Feb 01 15:39:41 raspberrypi systemd[1]: Starting A high performance web server and a reverse proxy serve Feb 01 15:39:41 raspberrypi systemd[1]: Started A high performance web server and a reverse proxy server
Installing Certbot on Raspbian Stretch Lite
Once you had installed Nginx, proceed to install Certbot on Raspbian Stretch Lite. Certbot is a ACME client that can help us get free browser-trusted SSL/TLS certificates from Let's Encrypt. With Let's Encrypt certificates, our reverse proxy server will be able to serve HTTPS traffic on behalf of other upstream web applications.
Using Certbot to get free Let's Encrypt SSL/TLS certificates for your domains
Getting free Let's Encrypt SSL/TLS certificates for the domains that our reverse proxy will serve HTTPS traffic with is a three step process.
Step 1: Setting up a web directory for ACME challenges
We first help Certbot proof that the web server controls the domains that the SSL/TLS certificates will be issued to. To do so, we will need to configure Nginx to serve ACME challenges from a directory that Certbot will write to.
Suppose we are configuring HTTPS for the domain example.com
, we will create an nginx configuration file located at /etc/nginx/sites-enabled/example.com.conf
which the following contents:
server { listen 80; server_name example.com; root /var/www/example.com; location ~ /.well-known { allow all; } }
After saving /etc/nginx/sites-enabled/example.com.conf
, we will then proceed to create /var/www/example.com
directory and change the owner to www-data:
sudo mkdir /var/www/example.com sudo chown www-data:www-data /var/www/example.com
Once the directory is created, restart Nginx with the following command:
sudo systemctl restart nginx.service
Step 2: Running Certbot to acquire the artefacts that are needed for serving HTTPS traffic
Once we had configured Nginx to serve HTTP requests made by Let's Encrypt CA to retrieve the ACME challenges, we then proceed to run the following command to get Certbot to acquire the artefacts that are needed for Nginx to serve HTTPS traffic:
sudo certbot certonly -a webroot --webroot-path=/var/www/example.com -d example.com
Step 3: Configuring Nginx to serve HTTPS traffic
If you had not already computed a Diffie-Hellman group for Nginx to use for exchanging cryptographic keys with its clients, run the following command to generate one:
sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048
This process will take some time to complete.
Assuming that we want our Nginx to proxy HTTPS traffic directed at example.com
to an upstream web application reachable via http://192.168.1.123:12345
, we will update /etc/nginx/sites-enabled/example.com.conf
with the following content:
# Redirect HTTP requests to HTTPS server { listen 80; server_name example.com; return 301 https://$host$request_uri; } # For ssl server { ssl on; ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_prefer_server_ciphers on; ssl_dhparam /etc/ssl/certs/dhparam.pem; ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA'; ssl_session_timeout 1d; ssl_session_cache shared:SSL:50m; ssl_stapling on; ssl_stapling_verify on; add_header Strict-Transport-Security max-age=15768000; default_type application/octet-stream; listen 443; server_name example.com; root /var/www/example.com; location ~ /.well-known { allow all; } location / { proxy_pass http://192.168.1.123:12345; } }
Renewing the SSL certificate in the future
The SSL certificate issued by Let's Encrypt will expire. Before your SSL certificate for your domain expires, you should renew it so that browsers will continue to trust your website.
To renew the SSL certificate issued for example.com
and www.example.com
, run the following command:
sudo certbot certonly --force-renewal -a webroot --webroot-path=/var/www/example.com -d example.com
Common Nginx configurations for reverse proxies
Once we had performed the system administrative stuff for our Raspberry Pi 3 reverse proxy server, the final step will be to configure Nginx to perform the proxying of HTTP / HTTPS traffic for upstream servers.
Configurations for load balancing between multiple upstream servers
If you find that the WordPress site on your Raspberry Pi 3 is not coping up with your surging traffic and you want another WordPress site on your Raspberry Pi Zero W to share some load, you can configure Nginx to perform load balancing for HTTP / HTTPS traffic directed at the domain for your WordPress site.
Suppose that:
- One of your WordPress site is served by an instance of PHP 7.0 FPM residing on the same machine as your Nginx reverse proxy server.
- Another one is served by an instance of PHP 7.0 FPM via port 8000 of 192.168.1.155 as an IP address.
- Both WordPress sites share the same codebase with the same directory structure.
You will first define a server grouping with the upstream directive:
upstream wordpress_cluster { server 192.168.1.155:8000; server unix:/run/php/php7.0-fpm.sock; }
After you had defined the server group that represent your upstream PHP FastCGI Process Managers, you can then proceed with creating the configurations for proxying HTTP requests to the PHP FastCGI Process Managers.
Configurations for proxying to PHP FastCGI Process Manager
Undeniably, one popular administrative task is to configure Nginx for PHP web applications.
For example, the following is a set of configuration that was taken from WordPress's guide for configuring Nginx:
server { listen 80; ## Your website name goes here. server_name anewwebsite.com www.anewwebsite.com; ## Your only path reference. root /var/www/my_new_wordpress_site; ## This should be in your http block and if it is, it's not needed here. index index.php; location = /favicon.ico { log_not_found off; access_log off; } location = /robots.txt { allow all; log_not_found off; access_log off; } location / { # This is cool because no php is touched for static content. # include the "?$args" part so non-default permalinks doesn't break when using query string try_files $uri $uri/ /index.php?$args; } location ~ \.php$ { #NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini include fastcgi.conf; fastcgi_intercept_errors on; fastcgi_pass wordpress_cluster; fastcgi_buffers 16 16k; fastcgi_buffer_size 32k; } location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ { expires max; log_not_found off; } }
What the "location ~ \.php$" block does?
The gist of proxying HTTP traffic to instance(s) of PHP FastCGI Process Manager lies in the "location ~ \.php$" block which defines the behaviour of Nginx when it encounters a HTTP request for urls ending with .php for either the anewwebsite.com or www.anewwebsite.com domain.
In our example, we used the fastcgi_pass
directive to set the destination where such HTTP requests will be directed. In this case, we used the upstream block wordpress_cluster
that we had defined earlier for load balancing.
Along with the fastcgi_pass
directive, we also used directives from ngx_http_fastcgi_module
to describe the HTTP request to the PHP FastCGI Process Manager that will process the HTTP request on behalf of Nginx.
Since the fastcgi.conf
file contains common applications of directives from ngx_http_fastcgi_module
, we included them in the "location ~ \.php$
" block too.
Configurations for proxying to any web application
When in doubt, the best way to proxy HTTP requests to upstream servers is via the proxy_pass
directive.
As an example, the following is a set of Nginx configurations that proxies HTTPS traffic to the MotionEye web interface:
# Redirect HTTP requests to HTTPS server { listen 80; server_name cctv.adomainname.com; return 301 https://$host$request_uri; } # For ssl server { ssl on; ssl_certificate /etc/letsencrypt/live/cctv.adomainname.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/cctv.adomainname.com/privkey.pem; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_prefer_server_ciphers on; ssl_dhparam /etc/ssl/certs/dhparam.pem; ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA'; ssl_session_timeout 1d; ssl_session_cache shared:SSL:50m; ssl_stapling on; ssl_stapling_verify on; add_header Strict-Transport-Security max-age=15768000; default_type application/octet-stream; listen 443; server_name cctv.adomainname.com; root /var/www/cctv.adomainname.com; location ~ /.well-known { allow all; } location / { include proxy_params; proxy_pass http://192.168.0.107; } }
The proxy_pass
directive describes the HTTP request to the upstream server with mechanisms from the HTTP protocol. In the example, we simply pass the HTTPS request directed at cctv.adomainname.com
domain to the motionEye web interface at 192.168.0.107
.
We can use other directives in ngx_http_proxy_module
to describe the HTTP request to the upstream web server that will process the HTTP request on behalf of Nginx.
Buying the recommended hardware list to setup your own Raspberry Pi 3 reverse proxy server
If you do not have the Raspberry Pi 3 hardware mentioned in this post yet, you may want to purchase them from Amazon. Simply click on the button below to add the Raspberry Pi 3 hardware to your cart. You may remove anything that you already have or replace some of the hardware with other hardware.