|
server { |
|
listen 80; |
|
listen [::]:80; |
|
server_name jf.example.com; |
|
add_header Content-Security-Policy "default-src 'self';"; |
|
return 301 jf.example.com; |
|
access_log off; |
|
} |
|
server { |
|
listen 443 ssl http2; |
|
listen [::]:443 ssl http2; |
|
# HTTP3 ONLY |
|
#listen 443 quic reuseport; |
|
#listen [::]:443 quic reuseport; |
|
#quic_retry on; |
|
#ssl_early_data on; |
|
#http3 on; |
|
#http3_hq off; |
|
|
|
server_name jf.example.com; |
|
|
|
set $jellyfin 127.0.0.1; |
|
|
|
keepalive_timeout 60; |
|
|
|
ssl_conf_command Options KTLS; |
|
#ssl_protocols READ GUIDE FOR THIS; |
|
#ssl_ciphers READ GUIDE FOR THIS; |
|
ssl_prefer_server_ciphers on; |
|
ssl_session_tickets off; |
|
ssl_session_timeout 24h; |
|
ssl_ecdh_curve secp256r1; |
|
ssl_buffer_size 4k; |
|
ssl_stapling on; |
|
ssl_stapling_verify on; |
|
resolver 1.1.1.1 1.0.0.1 [2606:4700:4700::1111] [2606:4700:4700::1001] valid=300s; |
|
ssl_session_cache shared:dotcomsecure:10m; |
|
ssl_ocsp_cache shared:dotcomstaple:10m; |
|
ssl_certificate "/etc/letsencrypt/live/example.com/fullchain.pem"; |
|
ssl_certificate_key "/etc/letsencrypt/live/example.com/privkey.pem"; |
|
ssl_trusted_certificate "/etc/letsencrypt/live/example.com/chain.pem"; |
|
ssl_dhparam "/etc/nginx/dh/dhparam.pem"; |
|
|
|
add_header Content-Security-Policy "base-uri 'none'; connect-src 'self'; default-src 'self'; font-src 'self' data:; form-action 'self'; frame-ancestors 'self'; frame-src 'self'; img-src 'self' blob: data: https:; manifest-src 'self'; media-src 'self' blob:; object-src 'none'; script-src 'self' 'unsafe-inline' blob:; script-src-elem 'self' https://www.gstatic.com/cv/js/sender/v1/cast_sender.js https://www.gstatic.com/eureka/clank/ blob:; style-src 'self' 'unsafe-inline'; worker-src 'self' blob:;"; |
|
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; |
|
add_header X-Frame-Options SAMEORIGIN always; |
|
add_header X-Content-Type-Options nosniff; |
|
add_header Referrer-Policy strict-origin-when-cross-origin; |
|
# HTTP3 ONLY |
|
#add_header Alt-Svc 'h3-29=":$server_port"; ma=86400, h3=":$server_port"; ma=86400'; |
|
add_header X-protocol $server_protocol always; |
|
# Private cache is used for security reasons. This isn't a public service. |
|
# Responses with Authorization headers while using Public can be problematic if you're behind a proxy from Amazon, Cloudflare, Fastly, etc. |
|
add_header Cache-Control "private$jf_content"; |
|
|
|
access_log /var/log/nginx/jellyfin.log main buffer=32k flush=5m; |
|
|
|
# Only allow these HTTP response types. |
|
# If you need to delete content via the UI, use the LAN:IP. |
|
# Adding DELETE here allows someone to delete content through Jellyfin TLD. |
|
if ($request_method !~ ^(GET|HEAD|POST)$ ) { |
|
return 405; |
|
} |
|
|
|
sendfile on; |
|
tcp_nopush on; |
|
|
|
location = / { |
|
return 301 https://$host/web/; |
|
} |
|
|
|
location / { |
|
proxy_pass http://$jellyfin:8096; |
|
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-Protocol $scheme; |
|
proxy_set_header X-Forwarded-Host $http_host; |
|
proxy_buffering on; |
|
proxy_buffers 16 4k; |
|
proxy_buffer_size 4k; |
|
proxy_busy_buffers_size 8k; |
|
proxy_temp_file_write_size 8k; |
|
proxy_max_temp_file_size 16k; |
|
proxy_connect_timeout 60s; |
|
proxy_send_timeout 60s; |
|
proxy_read_timeout 60s; |
|
# For use with TLS locally. See guide. |
|
#proxy_ssl_certificate /etc/nginx/certs/cert.pem; |
|
#proxy_ssl_certificate_key /etc/nginx/certs/priv.key; |
|
#proxy_ssl_protocols TLSv1.2 TLSv1.3; |
|
#proxy_ssl_session_reuse on; |
|
} |
|
location = /web/ { |
|
proxy_pass http://$jellyfin:8096/web/index.html; |
|
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-Protocol $scheme; |
|
proxy_set_header X-Forwarded-Host $http_host; |
|
} |
|
location /socket { |
|
proxy_pass http://$jellyfin:8096; |
|
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_set_header X-Forwarded-For $proxy_add_x_forwarded_for; |
|
proxy_set_header X-Forwarded-Proto $scheme; |
|
proxy_set_header X-Forwarded-Protocol $scheme; |
|
proxy_set_header X-Forwarded-Host $http_host; |
|
proxy_connect_timeout 60s; |
|
proxy_send_timeout 60s; |
|
proxy_read_timeout 60s; |
|
} |
|
location ~ /Items/(.*)/Images { |
|
# We give NGINX its own cache so Jellyfin doesn't have to serve the request. |
|
proxy_pass http://$jellyfin:8096; |
|
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-Protocol $scheme; |
|
proxy_set_header X-Forwarded-Host $http_host; |
|
proxy_cache jellyfin; |
|
proxy_cache_revalidate on; |
|
proxy_cache_lock on; |
|
add_header X-Cache-Status $upstream_cache_status; # This is only to check if cache is working |
|
} |
|
# Drop script execution. |
|
location ~* .(pl|cgi|py|sh|lua|asp|php)$ { |
|
return 444; |
|
} |
|
# Drop access to hidden files. |
|
location ~ /\. { |
|
return 444; |
|
} |
|
} |