# 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/`.
