# Nginx por delante del servidor de Node. # # sudo cp senzapaura.nginx.conf /etc/nginx/sites-available/senzapaura # sudo ln -sf /etc/nginx/sites-available/senzapaura /etc/nginx/sites-enabled/ # sudo rm -f /etc/nginx/sites-enabled/default # sudo nginx -t && sudo systemctl reload nginx # # Ese `rm` del sitio por defecto importa: mientras esté, es él quien contesta a # quien entra por IP, y se ve la página de bienvenida de Nginx en lugar de la # web. # # --------------------------------------------------------------------------- # Para que esto conteste desde internet hacen falta tres cosas fuera del # servidor, y en este orden: # # 1. DNS. En el panel del dominio, dos registros A apuntando a la IP pública: # # senzapaura.es. A 89.46.247.16 # www.senzapaura.es. A 89.46.247.16 # # Comprobar antes de seguir, desde cualquier sitio: dig +short senzapaura.es # # 2. Router. Redirigir los puertos 80 y 443 a la máquina interna: # # 80 TCP -> 192.168.18.171:80 # 443 TCP -> 192.168.18.171:443 # # Sin el 80 abierto, Certbot no puede emitir el certificado: su # comprobación entra por ahí. # # 3. Certificado. Ya con lo anterior funcionando: # # sudo apt install -y certbot python3-certbot-nginx # sudo certbot --nginx -d senzapaura.es -d www.senzapaura.es # # Certbot añade solo el bloque de 443 y la redirección desde el 80, y deja # puesta la renovación automática. Después: # # ORIGIN=https://senzapaura.es en /var/www/senzapaura/.env # sudo systemctl restart senzapaura # # Sin ese ORIGIN, SvelteKit rechaza todos los formularios con un 403 por # comprobación de origen y parece que la web está rota, aunque se vea. # # Y sin HTTPS no se puede abrir al público: aquí se escriben correos y se paga. # --------------------------------------------------------------------------- server { listen 80 default_server; listen [::]:80 default_server; # El dominio, y `_` para que siga contestando a quien entre por la IP # mientras el DNS no haya propagado del todo. server_name senzapaura.es www.senzapaura.es _; # Los mp3 completos pesan varios megas; los fragmentos, cientos de kilos. client_max_body_size 20m; gzip on; gzip_vary on; gzip_min_length 1024; gzip_types text/plain text/css application/javascript application/json image/svg+xml application/xml application/rss+xml; # ----------------------------------------------------------------------- # Estáticos con huella en el nombre # # Todo lo de `_app/immutable` lleva el hash del contenido en el nombre: si # cambia el archivo, cambia la URL. Por eso se puede cachear para siempre y # servirlo desde Nginx sin molestar a Node, que es lo que hace que abrir la # web no despierte al proceso para devolver un CSS. # ----------------------------------------------------------------------- location /_app/immutable/ { root /var/www/senzapaura/build/client; access_log off; expires 1y; add_header Cache-Control "public, immutable"; try_files $uri =404; } # Imágenes, fuentes, vídeo, los fragmentos de muestra y el panel. No llevan # huella, así que se cachean un día: lo justo para no pedirlos en cada # visita sin que un cambio tarde una semana en verse. location ~* ^/(images|background|video|muestras|fuentes|admin)/ { root /var/www/senzapaura/build/client; access_log off; expires 1d; add_header Cache-Control "public"; try_files $uri $uri/ =404; } # ----------------------------------------------------------------------- # Todo lo demás, a Node # # Incluidas /descargas/… —que comprueban la compra antes de entregar el # archivo—, la vuelta de Google y Facebook, y el webhook de Stripe. Ojo: # las descargas NO se pueden servir desde Nginx; el audio completo vive # fuera de `build/` justamente para que no haya forma de pedirlo por URL # sin haberlo comprado. # ----------------------------------------------------------------------- location / { proxy_pass http://127.0.0.1:3000; 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; proxy_set_header X-Forwarded-Host $host; # El limitador de envíos cuenta por IP. Sin las cabeceras de arriba, # todas las peticiones parecerían venir de 127.0.0.1 y el límite global # se dispararía con una sola persona insistiendo. # Un mp3 completo tarda en salir por una conexión lenta. proxy_read_timeout 300s; # Sin buffer: la descarga empieza a fluir en cuanto Node la manda, en # vez de esperar a que Nginx tenga el archivo entero. proxy_buffering off; } }