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