275 lines
8.5 KiB
Nginx Configuration File
275 lines
8.5 KiB
Nginx Configuration File
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;
|
|
}
|
|
}
|
|
}
|