brew install nginx
Configuration file for nginx will be at /usr/local/etc/nginx/nginx.conf
Web apps can be stored at /usr/local/var/www
Start:
nginx
Stop:
nginx -s stop
Reload config:
nginx -s reload
Lint:
nginx -t
- Firewall was already disabled.
 - Port forward 80 & 443 in router settings.
 
The full nginx.conf file will be at the end - this here is a basic proxy example.
In the block shown below - we created a web server:
1- 80 is the web server port.
2- localhost is where the web server is listening.
3- proxy_pass is the location we would like to proxy to. Should be the app server.
Therefore, this block is saying: proxy all requests at http://localhost:80 to http://localhost:3000.
server {
        listen 80;
        server_name localhost;
        location / {
            proxy_pass         http://192.168.100.190:3000; 
        }
    }The webserver server_name will usually point to a domain name - which DNS records should be set up for so it resolves to our public IP address. However, if that IP is dynamic its a problem as it eventually changes.
In my case, I had a domain with namecheap and they allow you to update the IP address that a special DNS record points to with a GET request.
Therefore, we can automate that process with a bash script and a cronjob:
#!/usr/bin/env sh
IP4=$(dig @resolver1.opendns.com ANY myip.opendns.com +short)
echo "$IP4"
URL="https://dynamicdns.park-your-domain.com/update?host=%40&domain=mydomain.com&password=mypassword&ip=""${IP4}"
echo "$URL"
curl --request GET \
  --url $URLCrontab:
- List cronjobs:
 
crontab -l
- Add cronjob (useful: https://crontab.guru)
 
crontab -e
Using Certbot (Let's Encrypt client) guide it is straightforward:
Install certbot:
brew install certbot
To only generate certificate:
sudo certbot certonly --standalone -d mydomain.com
To generate certificate and update nginx.conf file automatically:
sudo certbot --nginx
The location of the files generated will be at /etc/letsencrypt/archive. It also generates symbolic links to those files at /etc/letsencrypt/live.
To change our initial basic setup to use SSL and our domain name, this is how it would look like:
server {
    listen 443 ssl;
    server_name mydomain.com;
    ssl_certificate /etc/letsencrypt/live/mydomain.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/mydomain.com/privkey.pem; # managed by Certbot
    location / {
        proxy_pass         http://localhost:3000; 
    }
}- 
"Managed by certbot" is a line added by certbot automatically after adjusting your nginx.conf file if you ran the automatic command. Otherwise you can amend your nginx.conf manually to include your SSL certificates.
 - 
One issue faced here is that the user process running nginx did not have the required permissions to read the certificates (error when running nginx -t). Grant the necessary permissions using chmod on the real SSL files at /etc/letsencrypt/archive/ekyc-demo.xyz/ and the symbolic ones at /etc/letsencrypt/live/ekyc-demo.xyz/.
 
Perform a comprehensive SSL test using SSL Labs.
- 2 Apps on localhost 3000 and 5000.
 - Each with own subdomain and SSL certificates.
 - HTTP redirects to HTTPS.
 
#user  nobody;
# Number of processes should not exceed number of cores #
worker_processes  1;
# MINIMUM (probably too low): worker_connections * 2 file descriptors = 512 #
# No need to multiply by worker_prcocesses as the limit is applied to each worker #
# 1 descriptor for client connection, 1 for proxied server #
# Could be more based on conf. Could be limited by system (ulimit -n) #
worker_rlimit_nofile 1024;
events {
    # Default 1024 #
    worker_connections  256;
}
# Error Log #
error_log  logs/error.log;
error_log  logs/error.log  notice;
error_log  logs/error.log  info;
# Process ID Log #
pid        logs/nginx.pid;
http {
    include       mime.types;
    default_type  application/octet-stream;
    # Access Logs #
    map $request_uri $loggable {
        default                                             1;
        ~*\.(ico|css|js|gif|jpg|jpeg|png|svg|woff|ttf|eot)$ 0;
    }
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                     '$status $body_bytes_sent "$http_referer" '
                     '"$http_user_agent" "$request_body_file"';
    access_log  logs/access.log  main buffer=32k flush=30m if=$loggable;
    sendfile        on;
    #tcp_nopush     on;
    keepalive_timeout  65;
    #gzip  on;
        
    # Max user upload size #
    client_max_body_size 20M;
    # Uploaded file RAM buffer instead of temp file #
    client_body_buffer_size 20M;
    # store request body in temp file for debugging #
    # client_body_in_file_only on;
    # Root domain HTTP #
    server {
        listen 80;
        server_name mydomain.com;
        return 301 https://$server_name$request_uri;
    }
    server {
        listen 80;
        server_name app1.mydomain.com;
        return 301 https://$server_name$request_uri;
    }
    server {
        listen 80;
        server_name app2.mydomain.com;
        return 301 https://$server_name$request_uri;
    }
    # Root domain HTTPS #
    server {
        listen 443 ssl;
        ssl_certificate      /etc/letsencrypt/live/mydomain.com/fullchain.pem;
        ssl_certificate_key  /etc/letsencrypt/live/mydomain.com/privkey.pem;
        ssl_session_cache    shared:SSL:1m;
        ssl_session_timeout  5m;
        ssl_ciphers  HIGH:!aNULL:!MD5;
        ssl_prefer_server_ciphers  on;
        location / {
            return 403;
        }
    }
    # App 1 #    
    server {
        listen 443 ssl;
        server_name app1.mydomain.com;
        ssl_certificate /etc/letsencrypt/live/app1.mydomain.com/fullchain.pem; # managed by Certbot
        ssl_certificate_key /etc/letsencrypt/live/app1.mydomain.com/privkey.pem; # managed by Certbot
        location / {
            proxy_pass         http://localhost:3000;
        }
    }
    
    # App 2 #    
    server {
         listen 443 ssl;
         server_name app2.mydomain.com;
         ssl_certificate /etc/letsencrypt/live/app2.mydomain.com/fullchain.pem; # managed by Certbot
         ssl_certificate_key /etc/letsencrypt/live/app2.mydomain.com/privkey.pem; # managed by Certbot
         location / {
            proxy_pass         http://localhost:5000;
         }
    }
}- 
Regex based HTTP redirects to HTTPS subdomains instead of multiple HTTP server blocks.
 - 
Wildcard SSL certificate instead of different SSL certificates for each subdomain.
 - 
The SSL certificate generated here achieves a rating of B on SSL labs. Todo for A+?