NGINX Reverse Proxy
Remy Gardette
Posted on July 30, 2018
My home server setup is composed of several Raspberry Pi, where I host different web applications (this blog, an RSS reader, some home IOT apps…). I’ve decided to setup a front gateway, that proxies the request to the right server:
The requests are proxied by an NGINX reverse proxy, running in a Docker container on the gateway. It redirects the HTTP requests based on the host (eg. remyg.ovh
runs on rpi1
when rss.remyg.ovh
runs on rpi2).
NGINX Configuration
The main NGINX conf file (nginx.conf
) looks like this:
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/sites-enabled/*.*;
}
The only difference with the base conf file (from the default NGINX Docker image) is the last line:
include /etc/nginx/conf.d/*.conf;
is replaced by
include /etc/nginx/sites-enabled/*.*;
It ignores the default configuration (/etc/nginx/conf.d/default.conf
) and uses the proxy configuration files that I defined.
Hosts Configuration
Each host has its own configuration file:
- for remyg.ovh , running on rpi1 (with a local IP 192.168.0.10, and port 8080):
server {
listen 80;
server_name remyg.ovh;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
location / {
proxy_pass http://192.168.0.10:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect off;
}
}
- for rss.remyg.ovh , running on rpi2 (with a local IP 192.168.0.11, and port 8081):
server {
listen 80;
server_name rss.remyg.ovh;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
location / {
proxy_pass http://192.168.0.11:8081;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect off;
}
}
These files indicate that a request incoming to rss.remyg.ovh:80 (server_name
and listen
) will be redirected to 192.168.0.11:8081 (proxy_pass
).
That’s all the configuration you need to serve websites on HTTP.
Running in Container
To run the reverse proxy in a Docker container, the file tree looks like this:
nginx-reverse-proxy
-> conf
-> nginx.conf
-> sites
-> remyg.ovh
rss.remyg.ovh
With this structure, the command launching the container will be:
docker run --name mynginx-proxy \
-v /home/pi/nginx-reverse-proxy/sites:/etc/nginx/sites-enabled:ro \
-v /home/pi/nginx-reverse-proxy/conf/nginx.conf:/etc/nginx/nginx.conf:ro \
-p 80:80 -d nginx:alpine
HTTPS
To enable HTTPS on the different sites, I’m using Let’s Encrypt, and their utility app Certbot.
I’m starting by installing the certbot
package:
sudo apt install certbot
When generating a certificate, Certbot will need to validate that it can access a specific file that it generates, pointing to the URL http://your-host/.well-known/acme-challenge/{token}
. To do that, start by creating and mounting a new volume on the reverse proxy container:
docker run --name mynginx-proxy \
-v /home/pi/nginx-reverse-proxy/sites:/etc/nginx/sites-enabled:ro \
-v /home/pi/nginx-reverse-proxy/conf/nginx.conf:/etc/nginx/nginx.conf:ro \
-v /home/pi/letsencrypt_www:/var/www/letsencrypt \
-p 80:80 -p 443:443 -d nginx:alpine
Then specify in the sites proxy configuration that this volume is used when pointing to /.well-known/acme-challenge/
:
server {
listen 80;
server_name remyg.ovh;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
location /.well-known/acme-challenge/ {
root /var/www/letsencrypt;
}
location / {
proxy_pass http://192.168.0.10:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect off;
}
}
And reload your NGINX config:
docker exec -it mynginx-proxy nginx -s reload
Now you can generate the certificate(s) :
sudo certbot certonly --authenticator webroot -w /home/pi/letsencrypt_www -d remyg.ovh -d rss.remyg.ovh
This will generate the ACME challenge files in /home/pi/letsencrypt_www
, and validate the challenge. It will also generate the certificates, in /etc/letsencrypt/certs/live/remyg.ovh/
and /etc/letsencrypt/certs/live/rss.remyg.ovh/
.
The last step is to use the new certificates, and only allow HTTPS requests.
Start by mounting a new volume, containing the certificates:
docker run --name mynginx-proxy \
-v /home/pi/nginx-reverse-proxy/sites:/etc/nginx/sites-enabled:ro \
-v /home/pi/nginx-reverse-proxy/conf/nginx.conf:/etc/nginx/nginx.conf:ro \
-v /etc/letsencrypt:/etc/nginx/certs \
-v /home/pi/letsencrypt_www:/var/www/letsencrypt \
-p 80:80 -p 443:443 -d nginx:alpine
Then update your proxy configuration:
server {
listen 80;
server_name remyg.ovh;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
location /.well-known/acme-challenge/ {
root /var/www/letsencrypt;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
server_name remyg.ovh;
ssl_certificate certs/live/remyg.ovh/fullchain.pem;
ssl_certificate_key certs/live/remyg.ovh/privkey.pem;
location / {
proxy_pass http://192.168.0.10:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect off;
}
}
Reload the NGINX configuration, and you’re all set!
Posted on July 30, 2018
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.