3.3 KiB
3.3 KiB
Static Site Deployment via VPS Reverse Proxy
Deploying a static HTML/CSS/JS website through the VPS reverse proxy infrastructure.
Architecture
Internet → airrepairteam.graj-media.com (VPS nginx, SSL)
↳ Tailscale → 100.93.253.36:3451 (home nginx, SSL duckdns)
↳ root /mnt/seagate8tb/Websites/AirRepairTeam
Step 1: Home server nginx config
Static sites use root + try_files, not proxy_pass. Create in /etc/nginx/sites-enabled/<name>:
server {
listen <PORT> ssl;
server_name grajmedia.duckdns.org;
ssl_certificate /etc/letsencrypt/live/grajmedia.duckdns.org/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/grajmedia.duckdns.org/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
root /mnt/seagate8tb/Websites/<ProjectName>;
index index.html;
# Clean URLs — /hvac serves /hvac/index.html
location / {
try_files $uri $uri/ $uri/index.html =404;
}
# Cache static assets
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ {
expires 1h;
add_header Cache-Control "public, must-revalidate";
}
}
Port selection: use the next available in the 344x/345x range. Check existing ports:
grep -r "listen" /etc/nginx/sites-enabled/ | grep -oP '\d{4}' | sort -n
Step 2: File permissions
nginx workers run as uid 911. Static files must be world-readable:
chmod -R a+rX /mnt/seagate8tb/Websites/<ProjectName>
Step 3: Test and reload
sudo nginx -t && sudo nginx -s reload
Verify locally:
curl -sk -o /dev/null -w "%{http_code}" https://localhost:<PORT>/
curl -sk -o /dev/null -w "%{http_code}" https://localhost:<PORT>/hvac/
Step 4: VPS nginx config
Create /etc/nginx/sites-enabled/<name> on the VPS. Start with HTTP-only (port 80) for certbot:
server {
server_name <subdomain>.graj-media.com;
client_max_body_size 50M;
location / {
proxy_pass https://100.93.253.36:<PORT>;
proxy_http_version 1.1;
proxy_set_header Host grajmedia.duckdns.org;
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;
proxy_ssl_verify off;
proxy_read_timeout 86400;
proxy_buffering off;
}
listen 80;
}
Step 5: SSL via certbot
sudo nginx -t && sudo nginx -s reload
sudo certbot --nginx -d <subdomain>.graj-media.com --non-interactive --agree-tos -m admin@graj-media.com
Certbot issues a separate cert per subdomain and adds SSL to the config. No need for --expand.
Step 6: Verify
curl -sk -o /dev/null -w '%{http_code}' https://<subdomain>.graj-media.com/
curl -sk https://<subdomain>.graj-media.com/ | grep -o '<title>[^<]*</title>'
Step 7: Landing page
Add a link on graj-media.com by editing /etc/nginx/sites-enabled/landing and inserting before </body>:
<a href=https://<subdomain>.graj-media.com>Service Name</a>
Pitfalls
- 403 from home nginx: File permissions too restrictive. Fix:
chmod -R a+rX /path/to/site. - Host header mismatch: VPS must send
Host: grajmedia.duckdns.org(home nginx server_name), not the VPS domain. - Port already in use: Check with
grep -r "listen" /etc/nginx/sites-enabled/.