Reference configuration files for using Jellyfin with NGINX.
-
-
Save dmgolembiowski/f478d043fcbaf4d6a4a5c97292b35a9d to your computer and use it in GitHub Desktop.
Jellyfin + NGINX
This file contains 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
server { | |
# Always use "default_server" for direct WAN IP! | |
# A client who has SNI disabled will get the fake IP certificate and NOT the real domain certficate. | |
listen 80; | |
server_name _; | |
return 404; | |
access_log /var/log/nginx/block.log main buffer=32k flush=5m; | |
} | |
server { | |
listen 443 ssl http2; | |
server_name _; | |
# A reject causes problems with SSL tests. | |
#ssl_reject_handshake on; | |
ssl_protocols TLSv1.2 TLSv1.3; | |
ssl_ciphers TLS13-AES-256-GCM-SHA384:TLS13-AES-128-GCM-SHA256:ECDHE-ECDSA-AES128-CCM:ECDHE-ECDSA-AES128-CCM8:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-CCM:ECDHE-ECDSA-AES256-CCM8:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-PSK-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-CHACHA20-POLY1305; | |
ssl_prefer_server_ciphers on; | |
ssl_session_tickets off; | |
ssl_session_timeout 10m; | |
ssl_ecdh_curve prime256v1; | |
ssl_buffer_size 1400; | |
ssl_session_cache shared:ipsecure:10m; | |
ssl_certificate "invalid/invalid.pem"; | |
ssl_certificate_key "invalid/invalid.key"; | |
ssl_dhparam "dh/dhparam.pem"; | |
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; | |
add_header X-Frame-Options "SAMEORIGIN" always; | |
add_header X-XSS-Protection "1" always; | |
add_header X-Content-Type-Options "nosniff" always; | |
add_header Referrer-Policy "strict-origin" always; | |
return 404; | |
access_log /var/log/nginx/block.log main buffer=32k flush=5m; | |
} |
This file contains 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
upstream jellyfin { | |
server 127.0.0.1:8096; | |
#server 127.0.0.1:8920; | |
keepalive 16; | |
} | |
server { | |
listen 80; | |
server_name jf.example.me; | |
add_header Content-Security-Policy "default-src 'none';"; | |
return 301 https://jf.example.me; | |
access_log /var/log/nginx/me.example.jf_301.log main buffer=32k flush=5m; | |
} | |
server { | |
listen 443 ssl http2; | |
#listen 443 http3 reuseport; # Do not uncomment unless using HTTP/3 + QUIC build. | |
server_name jf.example.me; | |
keepalive_timeout 60; | |
ssl_conf_command Options KTLS; | |
ssl_protocols TLSv1.2 TLSv1.3; | |
# Recommended | |
ssl_ciphers TLS13-AES-256-GCM-SHA384:TLS13-AES-128-GCM-SHA256:ECDHE-ECDSA-AES128-CCM:ECDHE-ECDSA-AES128-CCM8:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-CCM:ECDHE-ECDSA-AES256-CCM8:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-PSK-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-CHACHA20-POLY1305; | |
# Recommended & Secure | |
#ssl_ciphers TLS13-AES-256-GCM-SHA384:TLS13-AES-128-GCM-SHA256:TLS-AES-128-CCM-SHA256:DHE-PSK-AES256-CCM8:DHE-PSK-AES128-CCM8:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-PSK-CHACHA20-POLY1305:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-CCM8:ECDHE-ECDSA-AES256-CCM:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-CCM8:ECDHE-ECDSA-AES128-CCM:DHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-CCM8:DHE-RSA-AES256-CCM:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-CCM8:DHE-RSA-AES128-CCM:DHE-PSK-CHACHA20-POLY1305:DHE-PSK-AES256-GCM-SHA384:DHE-PSK-AES256-CCM:DHE-PSK-AES128-GCM-SHA256:DHE-PSK-AES128-CCM:DHE-DSS-AES256-GCM-SHA384:DHE-DSS-AES128-GCM-SHA256; | |
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 valid=300s; | |
ssl_session_cache shared:dotmesecure:10m; | |
ssl_ocsp_cache shared:dotmestaple:10m; | |
ssl_certificate "/etc/letsencrypt/live/example.me/fullchain.pem"; | |
ssl_certificate_key "/etc/letsencrypt/live/example.me/privkey.pem"; | |
ssl_trusted_certificate "/etc/letsencrypt/live/example.me/chain.pem"; | |
ssl_dhparam "/etc/nginx/dh/dhparam_dotme.pem"; | |
add_header Content-Security-Policy "base-uri 'none'; connect-src 'self'; default-src 'none'; font-src 'self' data:; form-action 'self'; frame-ancestors 'self'; frame-src 'self'; img-src 'self' https: data:; manifest-src 'self'; media-src 'self' blob:; object-src 'none'; prefetch-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 always; | |
add_header Referrer-Policy strict-origin always; | |
#add_header Alt-Svc 'h3=":443"'; # Do not uncomment unless using HTTP/3 + QUIC build. | |
add_header Cache-Control "private"; | |
expires $jfcache; | |
access_log /var/log/nginx/me.example.jf.log main buffer=32k flush=5m; | |
if ($request_method !~ ^(GET|HEAD|POST|DELETE)$ ) { | |
return 444; | |
} | |
sendfile on; | |
tcp_nopush on; | |
location = / { | |
return 302 http://$host/web/; | |
#return 302 https://$host/web/; | |
} | |
location / { | |
proxy_pass http://jellyfin; | |
#proxy_pass https://jellyfin; | |
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_buffer_size 4k; | |
proxy_busy_buffers_size 8k; | |
proxy_buffers 8 4k; | |
proxy_temp_file_write_size 8k; | |
proxy_max_temp_file_size 1024m; | |
proxy_connect_timeout 60s; | |
proxy_send_timeout 60s; | |
proxy_read_timeout 60s; | |
#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/web/index.html; | |
#proxy_pass https://jellyfin/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; | |
#proxy_pass https://jellyfin; | |
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; | |
} | |
location ~ /Items/(.*)/Images { | |
proxy_pass http://jellyfin; | |
#proxy_pass https://jellyfin; | |
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 ~\.(php)$ { | |
return 404; | |
} | |
location ~ /\. { | |
return 404; | |
} | |
} |
This file contains 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
user www-data; | |
worker_processes auto; | |
pid /run/nginx.pid; | |
worker_rlimit_nofile 2048; | |
events { | |
worker_connections 1024; | |
} | |
http { | |
# Base performance and settings | |
#server_tokens off; | |
tcp_nodelay on; | |
client_body_buffer_size 1m; | |
client_max_body_size 20m; | |
client_header_buffer_size 1k; | |
large_client_header_buffers 4 16k; | |
# Proxy performance | |
open_file_cache max=5000 inactive=20s; | |
open_file_cache_valid 60s; | |
open_file_cache_min_uses 4; | |
open_file_cache_errors on; | |
include /etc/nginx/mime.types; | |
default_type application/octet-stream; # Undefined mime types are downloaded instead of rendered. | |
#### | |
# Compress contents in order of preference. | |
# Brotli is preffered over Gzip. | |
# Files that match the mime types are compressed. | |
# 1 is low compression using low CPU. | |
# 9 (gzip) or 11 (brotli) is high compression using high CPU. | |
# Gzip defaults: Nginx uses 1, Apache uses 6. | |
# Do not uncomment Brotli lines unless you have built NGINX with the module. | |
#### | |
#brotli on; | |
#brotli_static on; | |
#brotli_comp_level 7; | |
#brotli_types application/atom+xml application/javascript application/json application/rss+xml application/vnd.ms-fontobject application/x-font-opentype application/x-font-truetype application/x-font-ttf application/x-javascript application/xhtml+xml application/xml font/eot font/opentype font/otf font/truetype font/woff font/woff2 image/svg+xml image/vnd.microsoft.icon image/x-icon image/x-win-bitmap text/css text/javascript text/plain text/xml; | |
gzip on; | |
gzip_vary on; | |
gzip_min_length 100; | |
gzip_comp_level 4; | |
gzip_proxied any; | |
gzip_types application/atom+xml application/javascript application/json application/rss+xml application/vnd.ms-fontobject application/x-font-opentype application/x-font-truetype application/x-font-ttf application/x-javascript application/xhtml+xml application/xml font/eot font/opentype font/otf font/truetype image/svg+xml image/vnd.microsoft.icon image/x-icon image/x-win-bitmap text/css text/javascript text/plain text/xml; | |
#### | |
# Logging | |
#### | |
log_format main [$time_local] ' $status' ' $request_time' ' $remote_user' ' $remote_addr' ' "$request" $body_bytes_sent' ' "$http_referer" "$http_user_agent"'; | |
access_log /var/log/nginx/access.log main buffer=32k flush=5m; | |
error_log /var/log/nginx/error.log; # Add 'info' if you need a more detailed breakdown. 'debug' is excessive. | |
#### | |
# This is for Jellyfin only. | |
# Because Jellyfin web resources are unlikely to change, longer caches times are OK. | |
#### | |
proxy_cache_path /var/cache/nginx/jellyfin levels=1:2 keys_zone=jellyfin:256m max_size=15g inactive=30d use_temp_path=off; | |
map $sent_http_content_type $jfcache { | |
# All undefined contents | |
default off; | |
# Defined content | |
text/html off; | |
text/css 7d; | |
text/javascript 7d; | |
# Fonts | |
application/vnd.ms-fontobject 1y; | |
application/font-woff 1y; | |
application/x-font-truetype 1y; | |
application/x-font-opentype 1y; | |
~font/ 1y; | |
# Media | |
~image/ 6M; | |
} | |
include block.conf; # Drops direct IP access. | |
include jellyfin.vhost; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment