Why use a reverse proxy?

· 3 min read
Why use a reverse proxy?
Photo by 愚木混株 cdd20 / Unsplash

Why use a reverse proxy? Well, if my home lab was only providing one service then I wouldn’t need one as I’d just pass the traffic through the router to the server and that would be it. This leave me with the issue of any other applications, how do I access them?

One way is to just open the appropriate ports in the router to connect to the service, however, without obtaining real host certificates for the services you either risk sending unencrypted data over the internet which could be intercepted or you use unsigned certificates which potentially causes issues with applications or show the “scary” This site is not secure warning in your browser. Neither of these options appeals to me so, to get round using insecure communications I use nginx to route traffic and provide certificates for the encrypted traffic. I use Lets Encrypt for the certificates and they’re updated automatically via a cron job on the reverse proxy server as Lets Encrypt certificates are only valid for 90 days. An added bonus of using a reverse proxy is I only need to open the ports to the proxy server and not to any other server behind my router.

A default installation of nginx provides a web server providing a default page so in order to route traffic to the appropriate service I need a couple of config files so that nginx can work out where to send the traffic. As everything is served via a single IP address there’s a couple of ways to do this.

Option 1: Use “subfolders”, for example, www.example.com could be the main website and www.example.com/media could be used to redirect to a plex server. This could work even if you used the IP address to connect rather than using DNS to work out where to go.

Option 2: Use subdomains, for example www.example.com is the main website and media.example.com could be the plex server. This option uses Server Name Indication, or SNI, to detrmine what service you’re trying to connect to. The main drawback of using subdomains is that nginx has to examine the request to see which name was being used so if you connect to the domain by IP address the revsere proxy doesn’t know which service to provide so you’ll end up with whatever the default is.

I’ve used a mixture of both options for the services provided as rather than creating a new host for every service I’ve grouped related services together and use a combination of SNI and paths to get to the correct service.

nginx configuration for Plex media server

This is a typical nginx configuration file and the basis for mine.

# Redirect http to https
server {
    listen 80;
    server_name media.example.com;
    return 301 https://$host$request_uri;
}
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name media.example.com;
# SSL
    ssl_certificate /etc/letsencrypt/live/media.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/media.example.com/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/media.example.com/chain.pem;
# reverse proxy
    location / {
### IP address is permissible here
    proxy_pass      http://<plex server>:32400;
#Forward real ip and host to Plex
    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_set_header        Host            $host;
    proxy_set_header Sec-WebSocket-Extensions $http_sec_websocket_extensions;
    proxy_set_header Sec-WebSocket-Key $http_sec_websocket_key;
    proxy_set_header Sec-WebSocket-Version $http_sec_websocket_version;
#Websockets
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
  }
# security headers
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-XSS-Protection "1; mode=block;" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "no-referrer-when-downgrade" always;
### This is the header referenced above ###
    add_header Content-Security-Policy "default-src * data: 'unsafe-eval' 'unsafe-inline'; img-src * data: blob:; media-src * data: blob:" always;
    add_header X-Robots-Tag none;
    add_header X-Download-Options noopen;
    add_header X-Permitted-Cross-Domain-Policies none;
### Be careful with this header; this will cause your site to break if it ever stops serving over TLS
    add_header Strict-Transport-Security "max-age=15552000; includeSubDomains";
  # . don't show "hidden" files
    location ~ /\. {
    deny all;
  }
#Plex has A LOT of javascript, xml and html. This helps a lot, but if it causes playback issues with devices turn gzip off.
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_types text/plain text/css text/xml application/json application/javascript application/xml+rss application/atom+xml image/svg+xml;
}