Created
September 8, 2024 15:46
-
-
Save peterkeen/565b5a43cb15d6bd722d3959a58da840 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Set in-memory cache-metadata size in keys_zone, size of video caching and how many days a cached object should persist | |
proxy_cache_path /cache/nginx/jellyfin-videos levels=1:2 keys_zone=jellyfin-videos:100m inactive=90d max_size=35000m; | |
proxy_cache_path /cache/nginx/jellyfin levels=1:2 keys_zone=jellyfin:100m max_size=15g inactive=30d use_temp_path=off; | |
map $request_uri $h264Level { ~(h264-level=)(.+?)& $2; } | |
map $request_uri $h264Profile { ~(h264-profile=)(.+?)& $2; } | |
server { | |
listen 80; | |
listen [::]:80; | |
server_name jellyfin.example.com; | |
return 301 https://$host$request_uri; | |
} | |
server { | |
listen 443 ssl http2; | |
listen [::]:443 ssl http2; | |
server_name jellyfin.example.com; | |
# Uncomment next line to Disable TLS 1.0 and 1.1 (Might break older devices) | |
# ssl_protocols TLSv1.3 TLSv1.2; | |
# use a variable to store the upstream proxy | |
# in this example we are using a hostname which is resolved via DNS | |
# (if you aren't using DNS remove the resolver line and change the variable to point to an IP address e.g `set $jellyfin 127.0.0.1`) | |
set $jellyfin jellyfin-upstream.example.com; | |
server_tokens off; | |
ssl_certificate /etc/letsencrypt/certificates/example.com.crt; | |
ssl_certificate_key /etc/letsencrypt/certificates/example.com.key; | |
ssl_session_timeout 1d; | |
ssl_session_cache shared:MozSSL:10m; # about 40000 sessions | |
ssl_session_tickets off; | |
# curl https://ssl-config.mozilla.org/ffdhe2048.txt > /path/to/dhparam | |
ssl_dhparam /etc/nginx/dhparam; | |
# intermediate configuration | |
ssl_protocols TLSv1.2 TLSv1.3; | |
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; | |
ssl_prefer_server_ciphers off; | |
# OCSP stapling | |
ssl_stapling on; | |
ssl_stapling_verify on; | |
client_max_body_size 1024M; | |
gzip off; | |
add_header X-Frame-Options "SAMEORIGIN"; | |
add_header X-XSS-Protection "0"; # Do NOT enable. This is obsolete/dangerous | |
add_header X-Content-Type-Options "nosniff"; | |
# COOP/COEP. Disable if you use external plugins/images/assets | |
add_header Cross-Origin-Opener-Policy "same-origin" always; | |
add_header Cross-Origin-Embedder-Policy "require-corp" always; | |
add_header Cross-Origin-Resource-Policy "same-origin" always; | |
# Permissions policy. May cause issues on some clients | |
add_header Permissions-Policy "accelerometer=(), ambient-light-sensor=(), battery=(), bluetooth=(), camera=(), clipboard-read=(), display-capture=(), document-domain=(), encrypted-media=(), gamepad=(), geolocation=(), gyroscope=(), hid=(), idle-detection=(), interest-cohort=(), keyboard-map=(), local-fonts=(), magnetometer=(), microphone=(), payment=(), publickey-credentials-get=(), serial=(), sync-xhr=(), usb=(), xr-spatial-tracking=()" always; | |
# Tell browsers to use per-origin process isolation | |
add_header Origin-Agent-Cluster "?1" always; | |
# Content Security Policy | |
# See: https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP | |
# Enforces https content and restricts JS/CSS to origin | |
# External Javascript (such as cast_sender.js for Chromecast) must be whitelisted. | |
# NOTE: The default CSP headers may cause issues with the webOS app | |
#add_header Content-Security-Policy "default-src https: data: blob: http://image.tmdb.org; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' https://www.gstatic.com https://www.youtube.com blob:; worker-src 'self' blob:; connect-src 'self'; object-src 'none'; frame-ancestors 'self'"; | |
location = / { | |
return 302 http://$host/web/; | |
#return 302 https://$host/web/; | |
} | |
location ~ /Items/(.*)/Images { | |
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 | |
} | |
location ~* ^/Videos/(.*)/(?!live) | |
{ | |
# Set size of a slice (this amount will be always requested from the backend by nginx) | |
# Higher value means more latency, lower more overhead | |
# This size is independent of the size clients/browsers can request | |
slice 2m; | |
proxy_cache jellyfin-videos; | |
proxy_cache_valid 200 206 301 302 30d; | |
proxy_ignore_headers Expires Cache-Control Set-Cookie X-Accel-Expires; | |
proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504; | |
proxy_connect_timeout 15s; | |
proxy_http_version 1.1; | |
proxy_set_header Connection ""; | |
# Transmit slice range to the backend | |
proxy_set_header Range $slice_range; | |
# This saves bandwidth between the proxy and jellyfin, as a file is only downloaded one time instead of multiple times when multiple clients want to at the same time | |
# The first client will trigger the download, the other clients will have to wait until the slice is cached | |
# Esp. practical during SyncPlay | |
proxy_cache_lock on; | |
proxy_cache_lock_age 60s; | |
proxy_pass http://$jellyfin:8096; | |
proxy_cache_key "jellyvideo$uri?MediaSourceId=$arg_MediaSourceId&VideoCodec=$arg_VideoCodec&AudioCodec=$arg_AudioCodec&AudioStreamIndex=$arg_AudioStreamIndex&VideoBitrate=$arg_VideoBitrate&AudioBitrate=$arg_AudioBitrate&SubtitleMethod=$arg_SubtitleMethod&TranscodingMaxAudioChannels=$arg_TranscodingMaxAudioChannels&RequireAvc=$arg_RequireAvc&SegmentContainer=$arg_SegmentContainer&MinSegments=$arg_MinSegments&BreakOnNonKeyFrames=$arg_BreakOnNonKeyFrames&h264-profile=$h264Profile&h264-level=$h264Level&slicerange=$slice_range"; | |
add_header X-Cache-Status $upstream_cache_status; # This is only for debugging cache | |
} | |
location / { | |
# Proxy main Jellyfin traffic | |
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; | |
# Disable buffering when the nginx proxy gets very resource heavy upon streaming | |
proxy_buffering off; | |
} | |
# location block for /web - This is purely for aesthetics so /web/#!/ works instead of having to go to /web/index.html/#!/ | |
location = /web/ { | |
# Proxy main Jellyfin traffic | |
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 Jellyfin Websockets traffic | |
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; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment