Files
hermes-config/skills/vps-reverse-proxy/references/static-site-deployment.md
T
2026-07-12 10:17:17 -04:00

120 lines
3.3 KiB
Markdown

# 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>`:
```nginx
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:
```bash
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:
```bash
chmod -R a+rX /mnt/seagate8tb/Websites/<ProjectName>
```
## Step 3: Test and reload
```bash
sudo nginx -t && sudo nginx -s reload
```
Verify locally:
```bash
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:
```nginx
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
```bash
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
```bash
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>`:
```html
<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/`.