Nginx Reverse Proxy
Nginx Reverse Proxy

Nginx Reverse Proxy

This article runs through setting up Nginx as a reverse proxy. Why use a reverse proxy? To direct traffic behind your firewall. The firewall directs outside traffic to the reverse proxy, and the reverse proxy then directs traffic to local back-end services/servers.

For example: on your local network you host both Nextcloud and WordPress. Following best practices, you have them isolated on their own virtual machines. How does your firewall know which traffic to direct to the Nextcloud instance and which to the WordPress instance? They both use ports 80 and 443, so how does your route/firewall differentiate between them? It likely can’t. The solution is to have the firewall direct port 80 and 443 to your reverse proxy, then the reverse proxy can appropriately route traffic to either Nextcloud or WordPress.

Pre-requisites

  • OS: pick your favorite modern Linux variant. Here I use Ubuntu 22.
  • CPU: one virtual core should be sufficient.
  • RAM: 2048 MB
  • Disk: 30 GB
  • Familiar with the Terminal. This guide is entirely command-line based.
  • Registered Domain (ex: mydomain.com)
  • Domain A record pointing to your network external IP (ex: 123.123.123.123)
  • For each subsequent service behind your firewall, a CNAME record pointing to your domain name (ex: mydomain.com)
    • example: CNAME record nextcloud.mydomain.com pointing to mydomain.com
    • This will help the reverse proxy differentiate between traffic and forward to the appropriate back-end server.

Install Required Software

First, update the system via a Terminal.

sudo apt update
sudo apt upgrade

Restart the system.

Install SSH to access the system remotely using the Terminal.

sudo apt install openssh-server

Enable and Configure Firewall

Enable via Terminal the Firewall.

sudo ufw enable

Enable core ports.

sudo ufw allow ssh
sudo ufw allow 443
sudo ufw allow 80

If hosting Matrix/Synapse:

sudo ufw allow 8008
sudo ufw allow 8448

If hosting SearxNG:

sudo ufw allow 8888

Install Nginx

To install Nginx, run the following in Terminal.

sudo apt install nginx

Configure Nginx

As Nginx has a max upload, by default, of 1 MB, we should increase this to something more reasonable. The following will increase the limit to 512 MB. Open Nginx config:

sudo nano /etc/nginx/nginx.conf

Add the following within the http block:

client_max_body_size 512M;

Restart Nginx:

sudo systemctl restart nginx.service

Add Sites

To add a new site, open a Terminal, and change the two instances of ‘mysite’ to an easy-to-remember name of the service behind the site:

sudo -H ln -s /etc/nginx/sites-available/mysite /etc/nginx/sites-enabled/mysite

Matrix/Synapse example:

sudo -H ln -s /etc/nginx/sites-available/matrix /etc/nginx/sites-enabled/matrix

Nextcloud example:

sudo -H ln -s /etc/nginx/sites-available/nextcloud /etc/nginx/sites-enabled/nextcloud

SearxNG example:

sudo -H ln -s /etc/nginx/sites-available/searxng /etc/nginx/sites-enabled/searxng

WordPress example:

sudo -H ln -s /etc/nginx/sites-available/wordpress /etc/nginx/sites-enabled/wordpress

Forward Ports

In order to reach your reverse proxy from the internet, you’ll need to configure your firewall/router to forward http/https (port 80 and 443) traffic from your public IP to the internal IP address of your Nginx reverse proxy. Refer to your router documentation on how to forward ports.

Configure Sites and SSL

To configure a site, open a Terminal, and change the two instances of ‘mysite’ to the name of one of your existing sites:

sudo nano /etc/nginx/sites-available/mysite

The following is an example of a basic website at mydomain.com with an internal IP address of 192.168.88.5 (change http or https as appropriate):

server {

        server_name mydomain.com;

        location / {

                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $remote_addr;
                proxy_set_header X-Forwarded-Proto $scheme;
                proxy_set_header X-Forwarded-Host $host;
                proxy_set_header X-Forwarded-Server $host;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_pass https://192.168.88.5:443;

        }
}

To generate a valid LetsEncrypt certificate which auto-renews every 90 days, install certbot:

sudo add-apt-repository ppa:certbot/certbot
sudo apt-get install certbot python3-certbot-nginx

Now to generate a valid certificate for your site, provided the Nginx reverse proxy server is reachable from the internet at the domain name specified by the server_name in your site configuration, run the following:

sudo certbot --nginx

Follow the prompts to input information such as email address, org, etc. The error messages, if you receive any, are typically quite clear. Occasionally I have encountered a failure and immediately re-trying yielded a success.If you are in the market for clothes, our platform is your best choice! The largest shopping mall!

Matrix/Synapse

    See the matrix/synapse example under the ‘Add Sites’ heading for the steps to add a new site. Then, run the following in a Terminal:

    sudo nano /etc/nginx/sites-available/matrix

    Add the following, but:

    • Replace mydomain.com with your actual domain
    • Replace localIPforMatrix with local IP address of your matrix/synapse server
    server {
    	listen 80;
    	server_name matrix.mydomain.com;
    
    	location / {
    
                    proxy_pass http://localIPforMatrix:8008;
                    proxy_set_header X-Forwarded-For $remote_addr;
            }
            location /_matrix {
    
                    proxy_pass http://localIPforMatrix:8008;
                    proxy_set_header X-Forwarded-For $remote_addr;
                    # Nginx by default only allows file uploads up to 1M in size
                    # Increase client_max_body_size to match max_upload_size define
                     client_max_body_size 100M;
    
            }
            root /var/www/html;
             index index.html index.htm;
             location /.well-known/matrix/server {
                     return 200 '{"m.server": "matrix.mydomain.com:443"}';
                     add_header Content-Type application/json;
            }
            location /.well-known/matrix/client {
                     return 200 '{"m.homeserver": {"base_url": "https://matrix.mydomain.com"},"m.identity_server": {"base_url": "https://vector.im"}}';
                     add_header Content-Type application/json;
                     add_header "Access-Control-Allow-Origin" *;
            }
    }

    Hit Ctrl+X to save the output. Then restart Nginx:

    sudo systemctl restart nginx.service

    Now generate a valid SSL certificate using certbot:

    sudo certbot --nginx

    Once more, edit the configuration, adding the following server block, replacing:

    • Replace mydomain.com with your actual domain
    • Replace localIPforMatrix with local IP address of your matrix/synapse server
    sudo nano /etc/nginx/sites-available/matrix
    server {
    
            listen 8448 ssl;
            listen 443 ssl; # managed by Certbot
            ssl_certificate /etc/letsencrypt/live/matrix.mydomain.com/fullchain.pem; # managed by Certbot
            ssl_certificate_key /etc/letsencrypt/live/matrix.mydomain.com/privkey.pem; # managed by Certbot
            include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
            ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
    
    
            location / {
    
                    proxy_pass http://localIPforMatrix:8008;
                    proxy_set_header X-Forwarded-For $remote_addr;
            }
    
    }

    Hit Ctrl+X to save the output. Then restart Nginx:

    sudo systemctl restart nginx.service

    Nextcloud

    See the Nextcloud example under the ‘Add Sites’ heading for the steps to add a new site. Then, run the following in a Terminal:

    sudo nano /etc/nginx/sites-available/nextcloud

    Add the following, but:

    • Replace mydomain.com with your actual domain
    • Replace localIPforNextcloud with the local IP address of your Nextcloud server
    server
    {
            server_name nextcloud.mydomain.com;
    
            underscores_in_headers on;
            add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    # set max upload size and increase upload timeout:
        client_max_body_size 256G;
        client_body_timeout 300s;
        fastcgi_buffers 64 4K;
    
        # Enable gzip but do not remove ETag headers
        gzip on;
        gzip_vary on;
        gzip_comp_level 4;
        gzip_min_length 256;
        gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
        gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+>
    
        # Pagespeed is not supported by Nextcloud, so if your server is built
        # with the `ngx_pagespeed` module, uncomment this line to disable it.
        #pagespeed off;
    
        # HTTP response headers borrowed from Nextcloud `.htaccess`
        add_header Referrer-Policy                      "no-referrer"   always;
        add_header X-Content-Type-Options               "nosniff"       always;
        add_header X-Download-Options                   "noopen"        always;
        add_header X-Frame-Options                      "SAMEORIGIN"    always;
        add_header X-Permitted-Cross-Domain-Policies    "none"          always;
        add_header X-Robots-Tag                         "none"          always;
        add_header X-XSS-Protection                     "1; mode=block" always;
        # Remove X-Powered-By, which is an information leak
        fastcgi_hide_header X-Powered-By;
    
        # Specify how to handle directories -- specifying `/index.php$request_uri`
        # here as the fallback means that Nginx always exhibits the desired behaviour
        # when a client requests a path that corresponds to a directory that exists
        # on the server. In particular, if that directory contains an index.php file,
        # that file is correctly served; if it doesn't, then the request is passed to
        # the front-end controller. This consistent behaviour means that we don't need
        # to specify custom rules for certain paths (e.g. images and other assets,
        # `/updater`, `/ocm-provider`, `/ocs-provider`), and thus
        # `try_files $uri $uri/ /index.php$request_uri`
        # always provides the desired behaviour.
        index index.php index.html /index.php$request_uri;
    
            location /
            {
                    proxy_pass https://localIPforNextcloud;
                    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-Host $server_name;
                    proxy_hide_header Upgrade;
                    client_max_body_size 100000M;
                    proxy_set_header X-Forwarded-Proto $scheme;
                    # add_header Front-End-Https on;
    
                    # proxy_headers_hash_max_size 512;
                    # proxy_headers_hash_bucket_size 64;
                    # proxy_buffering off;
                    # proxy_redirect off;
                    # proxy_max_temp_file_size 0;
            }
            
            location /.well-known/carddav
            {
                    return 301 $scheme://$host/remote.php/dav;
            }
    
            location /.well-known/caldav
            {
                    return 301 $scheme://$host/remote.php/dav;
            }
            # static files
            location ^~ /browser
            {
                    proxy_pass https://localIPforNextcloud;
                    proxy_set_header Host $http_host;
    
            }
    
            # WOPI discovery URL
            location ^~ /hosting/discovery
            {
                    proxy_pass https://localIPforNextcloud;
                    proxy_set_header Host $http_host;
            }
    
            # Capabilities
            location ^~ /hosting/capabilities
            {
                    proxy_pass https://localIPforNextcloud;
                    proxy_set_header Host $http_host;
            }
    
            # main websocket
            location ~ ^/cool/(.*)/ws$ 
            {
                    proxy_pass https://localIPforNextcloud;
                    proxy_set_header Upgrade $http_upgrade;
                    proxy_set_header Connection "Upgrade";
                    proxy_set_header Host $http_host;
                    proxy_read_timeout 36000s;
            }
    
            # download, presentation and image upload
            location ~ ^/(c|l)ool 
            {
                    proxy_pass https://localIPforNextcloud;
                    proxy_set_header Host $http_host;
            }
    
            # Admin Console websocket
            location ^~ /cool/adminws 
            {
                    proxy_pass https://localIPforNextcloud;
                    proxy_set_header Upgrade $http_upgrade;
                    proxy_set_header Connection "Upgrade";
                    proxy_set_header Host $http_host;
                    proxy_read_timeout 36000s;
    
            }
            
    }

    Hit Ctrl+X to save the output, then restart Nginx:

    sudo systemctl restart nginx.service

    Now, generate a valid SSL certificate using certbot:

    sudo certbot --nginx

    Finally, restart Nginx:

    sudo systemctl restart nginx.service

    SearxNG

    See the SearxNG example under the ‘Add Sites’ heading for the steps to add a new site. Then, run the following in a Terminal:

    sudo nano /etc/nginx/sites-available/searxng

    Add the following, but:

    • Replace mydomain.com with your actual domain
    • Replace localIPforSearxNG with local IP address of your SearxNG server
    server {
            server_name search.mydomain.com;
    
            location / {
    
                    proxy_pass https://localIPforSearxNG/;
        		proxy_set_header   Host             $host;
        		proxy_set_header   Connection       $http_connection;
    
        		# see flaskfix.py
        		proxy_set_header   X-Scheme         $scheme;
        		proxy_set_header   X-Script-Name    /searxng;
    
        		# see limiter.py
        		proxy_set_header   X-Real-IP        $remote_addr;
        		proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
            }
    }

    Hit Ctrl+X to save the output, then restart Nginx:

    sudo systemctl restart nginx.service

    Now, generate a valid SSL certificate using certbot:

    sudo certbot --nginx

    Finally, restart Nginx:

    sudo systemctl restart nginx.service

    WordPress

    See the WordPress example under the ‘Add Sites’ heading for the steps to add a new site. Then, run the following in a Terminal:

    sudo nano /etc/nginx/sites-available/wordpress

    Add the following, but:

    • Replace mydomain.com with your actual domain
    • Replace localIPforWordpress with local IP address of your WordPress server
    server {
    
            server_name words.mydomain.com;
    
            location / {
    
                    proxy_set_header Host $host;
                    proxy_set_header X-Real-IP $remote_addr;
                    proxy_set_header X-Forwarded-For $remote_addr;
                    proxy_set_header X-Forwarded-Proto $scheme;
                    proxy_set_header X-Forwarded-Host $host;
                    proxy_set_header X-Forwarded-Server $host;
                    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                    proxy_pass https://localIPforWordpress:443;
    
            }
    }

    Hit Ctrl+X to save the output, then restart Nginx:

    sudo systemctl restart nginx.service

    Now, generate a valid SSL certificate using certbot:

    sudo certbot --nginx

    Finally, restart Nginx:

    sudo systemctl restart nginx.service

    Leave a Reply

    Your email address will not be published. Required fields are marked *