Skip to content

Instantly share code, notes, and snippets.

@osamaqarem
Last active July 20, 2025 15:50
Show Gist options
  • Save osamaqarem/f7f19ccff04c6e9be88d2c4645bb395c to your computer and use it in GitHub Desktop.
Save osamaqarem/f7f19ccff04c6e9be88d2c4645bb395c to your computer and use it in GitHub Desktop.
Nginx on MacOS

Install nginx (Homebrew)

brew install nginx

Commands

Start:

nginx

Stop:

nginx -s stop

Restart:

nginx -s reload

Lint:

nginx -t

Firewall

  • Firewall was already disabled.
  • Port forward 80 & 443 in router settings.

Simple Reverse Proxy

server {
        listen 80;
        server_name 192.168.100.190;

        location / {
            proxy_set_header   X-Forwarded-For $remote_addr;
            proxy_set_header   Host $http_host;
            # node app server IP
            proxy_pass         http://192.168.100.190:3000; 
        }
    }

Issues

1. Dynamic IP

  • In my case, the domain was under namecheap and they allow you to update the IP with a GET request.

  • Shell script to update DDNS namecheap record.

#!/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=ekyc-demo.xyz&password=mypassword&ip=""${IP4}"
echo "$URL"
curl --request GET \
  --url $URL

Executed via a Cronjob. Commands:

  • List cronjobs:

crontab -l

crontab -e

2. SSL

brew install certbot

Manual:

sudo certbot certonly --standalone -d ekyc-demo.xyz

Auto adjust nginx config:

sudo certbot --nginx

After this, nginx didn't have the permissions to access the certificates.

Temporary solution to folder permissions issue (probably very dangerous):

# Grant recursive read write permissions
sudo chmod -R 777 /etc/letsencrypt/archive 
sudo chmod -R 777 /etc/letsencrypt/live 

Full Setup

  • 2 Apps on localhost 3000 and 5000.
  • Each with own subdomain and SSL certificates.
#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 ekyc-demo.xyz;
        return 301 https://$server_name$request_uri;
    }
    server {
        listen 80;
        server_name app1.ekyc-demo.xyz;
        return 301 https://$server_name$request_uri;
    }
    server {
        listen 80;
        server_name app2.ekyc-demo.xyz;
        return 301 https://$server_name$request_uri;
    }
    # Root domain HTTPS #
    server {
        listen 443 ssl;
        ssl_certificate      /etc/letsencrypt/live/ekyc-demo.xyz/fullchain.pem;
        ssl_certificate_key  /etc/letsencrypt/live/ekyc-demo.xyz/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.ekyc-demo.xyz;
        ssl_certificate /etc/letsencrypt/live/app1.ekyc-demo.xyz/fullchain.pem; # managed by Certbot
        ssl_certificate_key /etc/letsencrypt/live/app1.ekyc-demo.xyz/privkey.pem; # managed by Certbot

        location / {
            proxy_pass         http://localhost:3000;
        }
    }
    
    # App 2 #    
    server {
         listen 443 ssl;
         server_name app2.ekyc-demo.xyz;
         ssl_certificate /etc/letsencrypt/live/app2.ekyc-demo.xyz/fullchain.pem; # managed by Certbot
         ssl_certificate_key /etc/letsencrypt/live/app2.ekyc-demo.xyz/privkey.pem; # managed by Certbot

         location / {
            proxy_pass         http://localhost:5000;
         }
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment