Skip to content

Instantly share code, notes, and snippets.

@rcland12
Created January 13, 2026 04:34
Show Gist options
  • Select an option

  • Save rcland12/2a41c741a4e298257eb6ae58be0aef22 to your computer and use it in GitHub Desktop.

Select an option

Save rcland12/2a41c741a4e298257eb6ae58be0aef22 to your computer and use it in GitHub Desktop.
Tautulli-OAuth-Keycloak-Nginx-Proxy
upstream tautulli_backend {
server tautulli:8181;
}
upstream tautulli_oauth2_proxy {
server tautulli-proxy:4180;
}
map $http_upgrade $connection_upgrade {
default upgrade;
"" close;
}
server {
listen 80;
listen [::]:80;
server_name website.com;
location ^~ /.well-known/acme-challenge/ {
root /var/www/certbot;
try_files $uri =404;
}
location / { return 301 https://$host$request_uri; }
}
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name website.com;
access_log /var/log/nginx/website.com.access.log;
error_log /var/log/nginx/website.com.error.log warn;
ssl_certificate /etc/letsencrypt/live/website.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/website.com/privkey.pem;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header X-Robots-Tag "noindex, nofollow" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
limit_req zone=general burst=500;
limit_conn addr 200;
client_max_body_size 100M;
client_body_timeout 120s;
send_timeout 120s;
location /oauth2/ {
proxy_pass http://tautulli_oauth2_proxy;
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-Host $host;
proxy_set_header X-Auth-Request-Redirect $request_uri;
}
location = /oauth2/auth {
internal;
proxy_pass http://tautulli_oauth2_proxy;
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 Content-Length "";
proxy_pass_request_body off;
}
location ^~ /pms_image_proxy {
auth_request /oauth2/auth;
error_page 401 = /oauth2/sign_in;
proxy_pass http://tautulli_backend;
proxy_http_version 1.1;
proxy_set_header Connection "close";
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_connect_timeout 30s;
proxy_send_timeout 120s;
proxy_read_timeout 120s;
proxy_next_upstream error timeout invalid_header http_502 http_503 http_504;
proxy_next_upstream_tries 3;
proxy_buffering off;
add_header Cache-Control "private, max-age=300" always;
}
location ~* \.(css|js|jpg|jpeg|png|gif|ico|woff|woff2|ttf|svg|eot)$ {
auth_request /oauth2/auth;
error_page 401 = /oauth2/sign_in;
auth_request_set $auth_cookie $upstream_http_set_cookie;
add_header Set-Cookie $auth_cookie always;
proxy_pass http://tautulli_backend;
proxy_http_version 1.1;
proxy_set_header Connection "close";
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_connect_timeout 30s;
proxy_send_timeout 120s;
proxy_read_timeout 120s;
proxy_next_upstream error timeout invalid_header http_502 http_503 http_504;
proxy_next_upstream_tries 3;
proxy_buffering off;
add_header Cache-Control "public, max-age=2592000" always;
access_log off;
}
location / {
auth_request /oauth2/auth;
error_page 401 = /oauth2/sign_in;
auth_request_set $user $upstream_http_x_auth_request_user;
auth_request_set $email $upstream_http_x_auth_request_email;
auth_request_set $auth_cookie $upstream_http_set_cookie;
add_header Set-Cookie $auth_cookie always;
proxy_set_header X-User $user;
proxy_set_header X-Email $email;
proxy_pass http://tautulli_backend;
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-Host $server_name;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_connect_timeout 30s;
proxy_send_timeout 120s;
proxy_read_timeout 120s;
proxy_next_upstream error timeout invalid_header http_502 http_503 http_504;
proxy_next_upstream_tries 3;
proxy_buffering off;
proxy_redirect off;
}
}
@rcland12
Copy link
Author

rcland12 commented Jan 13, 2026

I spent days dealing with 429 and 502 errors on my public facing Tautulli instance until I finally fine-tuned this file enough. If you're running other nginx.conf files in your Nginx instance make sure none of the other server block are rate-limiting your Tautulli block.

I hope this can help other people if they are running a similar stack. I am running Plex and Tautulli but have Tautulli proxied via Nginx with auth using Keycloak and OAuth2. If you're interested in this stack, let me know and I'd be happy to post my docker-compose.yaml as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment