Files
indee-demo/nginx.conf
Dorian 5244fdef50 Fix poster 404: add ^~ to /storage/ locations to override static asset regex
Nginx was serving /storage/.../*.jpg from the local filesystem instead of
proxying to MinIO because the static asset regex location (~* \.(jpg|...)$)
takes priority over plain prefix locations. Adding ^~ ensures the /storage/
and /storage-private/ prefix locations always win over regex matches.

Same root cause as the earlier 405 on thumbnail uploads.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-13 21:18:11 +00:00

157 lines
5.5 KiB
Nginx Configuration File

server {
listen 7777;
server_name _;
root /usr/share/nginx/html;
index index.html;
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json application/vnd.apple.mpegurl video/MP2T;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
# ── MinIO direct proxy (for presigned URL uploads/downloads) ──
# MUST appear before the static-asset regex locations below,
# otherwise image uploads (.jpg, .png, etc.) match the asset
# caching rule first and return 405 on PUT.
location ~ ^/(indeedhub-private|indeedhub-public)/ {
resolver 127.0.0.11 valid=30s ipv6=off;
set $minio_upstream http://minio:9000;
proxy_pass $minio_upstream;
proxy_http_version 1.1;
# Pass the original Host so MinIO's signature verification matches
# the host the presigned URL was generated for.
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# Allow large file uploads (up to 5GB per chunk)
client_max_body_size 5g;
# No caching for upload responses
add_header Cache-Control "no-store";
}
# PWA Support - proper MIME types
location ~* \.(?:manifest|webmanifest|json)$ {
add_header Cache-Control "public, max-age=3600";
add_header Content-Type application/manifest+json;
}
location ~* \.(?:js|css|woff2|woff|ttf|otf|eot|svg|png|jpg|jpeg|gif|ico)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# ── Backend API proxy ──────────────────────────────────────
location /api/ {
resolver 127.0.0.11 valid=30s ipv6=off;
set $api_upstream http://api:4000;
rewrite ^/api(.*) $1 break;
proxy_pass $api_upstream;
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;
# Trust the outer reverse proxy's X-Forwarded-Proto when present,
# otherwise fall back to the connection scheme.
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
# Preserve the original /api prefix so NIP-98 URL verification
# can reconstruct the URL the client actually signed.
proxy_set_header X-Forwarded-Prefix /api;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
# Handle large video uploads (up to 5GB)
client_max_body_size 5g;
}
# ── MinIO storage proxy (public bucket) ────────────────────
# Serves poster images, HLS segments, etc. with caching
# ^~ ensures this prefix takes priority over the static-asset
# regex that would otherwise intercept .jpg/.png requests.
location ^~ /storage/ {
resolver 127.0.0.11 valid=30s ipv6=off;
set $minio_upstream http://minio:9000;
rewrite ^/storage/(.*) /indeedhub-public/$1 break;
proxy_pass $minio_upstream;
proxy_http_version 1.1;
proxy_set_header Host minio:9000;
# Cache static assets aggressively
proxy_cache_valid 200 1d;
proxy_cache_valid 404 1m;
expires 1d;
add_header Cache-Control "public, max-age=86400";
add_header X-Cache-Status $upstream_cache_status;
}
# ── MinIO storage proxy (private bucket -- for HLS key delivery) ─
location ^~ /storage-private/ {
resolver 127.0.0.11 valid=30s ipv6=off;
set $minio_upstream http://minio:9000;
rewrite ^/storage-private/(.*) /indeedhub-private/$1 break;
proxy_pass $minio_upstream;
proxy_http_version 1.1;
proxy_set_header Host minio:9000;
# Do NOT cache private content
add_header Cache-Control "no-store";
}
# ── WebSocket proxy to Nostr relay (Docker service) ────────
location /relay {
resolver 127.0.0.11 valid=30s ipv6=off;
set $relay_upstream http://relay:8080;
rewrite ^/relay(.*) /$1 break;
proxy_pass $relay_upstream;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
}
# ── Vue Router - SPA fallback ──────────────────────────────
location / {
try_files $uri $uri/ /index.html;
}
# Service Worker
location /sw.js {
add_header Cache-Control "no-cache";
proxy_cache_bypass $http_pragma;
proxy_cache_revalidate on;
expires off;
access_log off;
}
location /workbox-*.js {
add_header Cache-Control "no-cache";
proxy_cache_bypass $http_pragma;
proxy_cache_revalidate on;
expires off;
access_log off;
}
# Health check endpoint
location /health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
}