You have an app running on localhost:3000. You want it accessible at app.yourdomain.com with HTTPS. That's exactly what nginx as a reverse proxy is for.
A reverse proxy is a server that sits between the user and your application. The user connects to nginx on port 80/443, and nginx forwards the request to your app running on an internal port.
User → nginx (:443) → app (:3000)
This means:
# Ubuntu/Debian
sudo apt update && sudo apt install nginx -y
# Check if it's running
sudo systemctl status nginx
Create a config file:
sudo nano /etc/nginx/sites-available/app.yourdomain.com
Basic configuration:
server {
listen 80;
server_name app.yourdomain.com;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
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-Proto $scheme;
}
}
Enable the config:
sudo ln -s /etc/nginx/sites-available/app.yourdomain.com /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
Certbot automatically fetches and renews Let's Encrypt certificates — for free.
# Install
sudo apt install certbot python3-certbot-nginx -y
# Get a certificate (single domain)
sudo certbot --nginx -d app.yourdomain.com
# Multiple subdomains at once
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com -d api.yourdomain.com -d dashboard.yourdomain.com
Certbot automatically modifies the nginx config and adds an HTTP → HTTPS redirect.
Each subdomain is a separate server block in nginx:
# API
server {
listen 443 ssl;
server_name api.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
location / {
proxy_pass http://localhost:8234;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
# Dashboard
server {
listen 443 ssl;
server_name dashboard.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
location / {
proxy_pass http://localhost:8236;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Certbot certificates expire after 90 days, but certbot sets up a cron job for renewal automatically:
# Check if auto-renewal works
sudo certbot renew --dry-run
nginx + certbot is the simplest way to expose an app to the world with HTTPS. One server, multiple domains, one wildcard certificate — and everything works. This is the foundation of any production infrastructure.