I've been using nginx for more than a decade now, before that I used lighttpd. Both work well for the purpose of reverse proxying services like Jellyfin. I do not use Caddy because I prefer to keep certificate management centralised in its own (Proxmox-managed) container. I have about 50 services running in Proxmox-managed containers (some containers run more than a single service, others are service-specific) proxied through a single nginx instance, the setup is reliable and performs well. Since nginx is widely used there is usually a config file available which can be used to base your own installation on. Here's what it looks like in my case:
# kijkbuis.example.org
server {
listen 80;
listen [::]:80;
server_name kijkbuis.example.org;
include /etc/nginx/snippets/enforcehttps.conf;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name kijkbuis.example.org;
ssl_certificate /etc/letsencrypt/live/kijkbuis.example.org/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/kijkbuis.example.org/privkey.pem;
set $jellyfin 192.168.1.51;
resolver 192.168.1.1 valid=30;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options "nosniff";
# the google-related domains are there to enable chromecast support
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/cv/js/sender/v1/cast_sender.js https://www.gstatic.com/eureka/clank/108/cast_sender.js https://www.gstatic.com/eureka/clank/107/cast_sender.js https://www.gstatic.com/eureka/clank/cast_sender.js https://www.youtube.com blob:; worker-src 'self' blob:; connect-src 'self'; object-src 'none'; frame-ancestors 'self'";
location = / {
return 302 https://$host/web/;
}
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;
}
}