worker_processes auto; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; # CORS origin validation - only allow configured origins # Validates Origin header against whitelist; returns empty string for disallowed origins # Production domain injected via envsubst from APP_BASE_URL env var map $http_origin $cors_origin { default ""; # Localhost development (various ports) "~^http://localhost(:[0-9]+)?$" $http_origin; # Docker internal network "~^http://host\.docker\.internal(:[0-9]+)?$" $http_origin; # Production domain (injected from APP_BASE_URL) "${APP_BASE_URL}" $http_origin; } # Enable UTF-8 charset for proper handling of special characters in URLs charset utf-8; charset_types application/json text/plain text/css text/javascript; # Logging access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; # Optimizations for video streaming sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; # Gzip compression for API responses (reduces JSON payload size by 60-80%) gzip on; gzip_vary on; gzip_proxied any; gzip_comp_level 6; gzip_min_length 256; gzip_types application/json application/javascript application/xml text/plain text/css text/javascript text/xml; # Increase buffer sizes for large video files client_max_body_size 0; proxy_buffer_size 128k; proxy_buffers 4 256k; proxy_busy_buffers_size 256k; server { listen 80; server_name _; # 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 "strict-origin-when-cross-origin" always; # Hide nginx version server_tokens off; # Curated public videos location /videos/public/ { alias /media/public/curated/; # Enable byte-range requests (essential for video seeking) add_header Accept-Ranges bytes; # CORS headers for cross-origin video playback (validated origin only) add_header Access-Control-Allow-Origin $cors_origin always; add_header Access-Control-Allow-Methods "GET, OPTIONS"; add_header Access-Control-Allow-Headers "Range"; # Cache control expires 1d; add_header Cache-Control "public, max-age=86400"; # Proper MIME types for video types { video/mp4 mp4; video/webm webm; image/jpeg jpg jpeg; image/png png; } } # Compilation videos from playback directory location /videos/playback/ { alias /media/playback/; add_header Accept-Ranges bytes; add_header Access-Control-Allow-Origin $cors_origin always; add_header Access-Control-Allow-Methods "GET, OPTIONS"; add_header Access-Control-Allow-Headers "Range"; expires 1d; add_header Cache-Control "public, max-age=86400"; types { video/mp4 mp4; video/webm webm; } } # Compilation symlinks location /videos/compilations/ { alias /media/compilations/; add_header Accept-Ranges bytes; add_header Access-Control-Allow-Origin $cors_origin always; add_header Access-Control-Allow-Methods "GET, OPTIONS"; add_header Access-Control-Allow-Headers "Range"; expires 1d; add_header Cache-Control "public, max-age=86400"; types { video/mp4 mp4; video/webm webm; } } # Public videos directory location /videos/videos/ { alias /media/videos/; add_header Accept-Ranges bytes; add_header Access-Control-Allow-Origin $cors_origin always; add_header Access-Control-Allow-Methods "GET, OPTIONS"; add_header Access-Control-Allow-Headers "Range"; expires 1d; add_header Cache-Control "public, max-age=86400"; types { video/mp4 mp4; video/webm webm; } } # Quickies short-form videos - optimized for fast loading location /videos/quickies/ { alias /media/quickies/; # Enable byte-range requests (essential for video seeking) add_header Accept-Ranges bytes; add_header Access-Control-Allow-Origin $cors_origin always; add_header Access-Control-Allow-Methods "GET, OPTIONS, HEAD"; add_header Access-Control-Allow-Headers "Range, If-Range"; add_header Access-Control-Expose-Headers "Content-Range, Accept-Ranges, Content-Length"; # Extended cache for immutable video content (7 days) expires 7d; add_header Cache-Control "public, max-age=604800, immutable"; # Optimize output buffers for video streaming output_buffers 2 512k; types { video/mp4 mp4; video/webm webm; } } # Also serve quickies at /media/quickies/ path for direct URL access location /media/quickies/ { alias /media/quickies/; # Enable byte-range requests (essential for video seeking) add_header Accept-Ranges bytes; add_header Access-Control-Allow-Origin $cors_origin always; add_header Access-Control-Allow-Methods "GET, OPTIONS, HEAD"; add_header Access-Control-Allow-Headers "Range, If-Range"; add_header Access-Control-Expose-Headers "Content-Range, Accept-Ranges, Content-Length"; # Extended cache for immutable video content (7 days) expires 7d; add_header Cache-Control "public, max-age=604800, immutable"; # Optimize output buffers for video streaming output_buffers 2 512k; types { video/mp4 mp4; video/webm webm; } } # Library videos (studios, OnlyFans, gifs, etc.) location /videos/library/ { alias /media/local/; add_header Accept-Ranges bytes; add_header Access-Control-Allow-Origin $cors_origin always; add_header Access-Control-Allow-Methods "GET, OPTIONS"; add_header Access-Control-Allow-Headers "Range"; expires 1d; add_header Cache-Control "public, max-age=86400"; types { video/mp4 mp4; video/webm webm; } } # Thumbnails location location /thumbnails/ { alias /media/thumbnails/; expires 7d; add_header Cache-Control "public, max-age=604800"; add_header Access-Control-Allow-Origin $cors_origin always; } # Ad images location location /ads/ { alias /media/ads/; expires 1d; add_header Cache-Control "public, max-age=86400"; add_header Access-Control-Allow-Origin $cors_origin always; types { image/jpeg jpg jpeg; image/png png; image/gif gif; image/webp webp; } } # User gallery images location location /gallery/ { alias /media/gallery/; expires 7d; add_header Cache-Control "public, max-age=604800"; add_header Access-Control-Allow-Origin $cors_origin always; types { image/jpeg jpg jpeg; image/png png; image/gif gif; image/webp webp; } } # Digest frames location (extracted video frames for AI analysis) location /digest-frames/ { alias /media/digest_frames/; expires 7d; add_header Cache-Control "public, max-age=604800"; add_header Access-Control-Allow-Origin $cors_origin always; types { image/jpeg jpg jpeg; image/png png; } } # Health check location /health { return 200 'OK'; add_header Content-Type text/plain; } } }