# 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/`: ```nginx server { listen 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/; 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/ ``` ## 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:/ curl -sk -o /dev/null -w "%{http_code}" https://localhost:/hvac/ ``` ## Step 4: VPS nginx config Create `/etc/nginx/sites-enabled/` on the VPS. Start with HTTP-only (port 80) for certbot: ```nginx server { server_name .graj-media.com; client_max_body_size 50M; location / { proxy_pass https://100.93.253.36:; 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 .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://.graj-media.com/ curl -sk https://.graj-media.com/ | grep -o '[^<]*' ``` ## Step 7: Landing page Add a link on graj-media.com by editing `/etc/nginx/sites-enabled/landing` and inserting before ``: ```html .graj-media.com>Service Name ``` ## 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/`.