A while ago I discovered another "cyber benefactor": Let's Encrypt, which provides free SSL certificates. Buying an SSL certificate from GlobalSign costs several thousand RMB per year, yet Let's Encrypt offers them for free - and paired with Certbot, certificate provisioning and renewal can be fully automated.
This post documents how I configured Nginx and a free SSL certificate for a web server hosting a frontend/backend-separated deployment.
Server Status and Goals
Current Setup
Two projects deployed separately: the frontend uploads its Vue.js build output (pure static files) to the server, where a Node.js CLI HTTP tool serves it on port 80.
The backend is built with Nest.js. The server clones the full repository, runs the build, and executes the entry JS file of the build output, with the backend bound to port 8080.
Goals
Use Nginx to serve the frontend build output as static files and act as a reverse proxy for the backend.
Use Certbot + Let's Encrypt to configure a free SSL certificate.
The end state: the server only exposes port 443. Users access the frontend at https://example.com and the backend API at https://example.com/api.
Installing and Configuring Nginx
Install Nginx on Ubuntu
sudo apt update
sudo apt install nginx
Start Nginx
sudo systemctl enable nginx
sudo systemctl start nginx
Check the Nginx status
systemctl status nginx
Visiting http://example.com in a browser now shows the default Nginx page.
Store the frontend build output at /var/www/frontend
/var/www/frontend
├── index.html
├── assets
└── favicon.ico
Create the Nginx config file /etc/nginx/sites-available/my-site
server {
listen 80;
server_name example.com;
root /var/www/frontend;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
location /api/ {
proxy_pass http://127.0.0.1:8080/;
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 new Nginx config file
sudo ln -s /etc/nginx/sites-available/my-site /etc/nginx/sites-enabled/
Validate the Nginx config and reload
sudo nginx -t
sudo systemctl reload nginx
Visiting the site again, the frontend URL is http://example.com and the backend URL is http://example.com/api/xxx. At this point Nginx lets both services be reached through a single port 80.
Installing Certbot and Configuring the SSL Certificate
Because Certbot and Let's Encrypt update frequently, traditional apt package repositories lag behind - so installing Certbot via Snap is recommended.
Install Snap
sudo apt install snapd
sudo snap install core
sudo snap refresh core
Install Certbot
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
certbot --version
The snap
--classicflag disables Snap's default sandbox isolation and grants the program full access to the host system
Request an SSL certificate with Certbot
sudo certbot --nginx
Certbot will:
- Detect Nginx automatically
- Modify the config automatically
- Request the certificate automatically
- Configure HTTPS automatically
- Set up the HTTP -> HTTPS redirect (optional)
Certbot adds an extra port-80 listener to the Nginx config for Let's Encrypt domain validation
Automatic certificate renewal
Certbot installed via Snap automatically installs a systemd timer. Once the SSL certificate is configured, Certbot creates a scheduled task that periodically checks and re-issues certificates.
View the Certbot timer
systemctl list-timers | grep certbot
Test certificate renewal
sudo certbot renew --dry-run
On success it prints Congratulations, all simulated renewals succeeded
With that, Nginx proxies both the frontend and backend through a single port, and Certbot + Let's Encrypt issues and deploys SSL certificates automatically - no more manual work needed.