Plex Server Behind NGiNX Reverse Proxy

Server Version#: 1.13.9.5439
Player Version#: (Latest Updated, Various Platforms)

So I have an NGiNX reverse proxy setup and a Plex Server that are both on the same local network. I want to pass all connections via the NGiNX reverse proxy that will perform SSL termination before passing off on the connection to the Plex server.

But using the NGiNX configuration below web browser access works but Plex apps do not load. If they connect they fail to load all the icons/album art and to play the actual content (music, tv shows, etc).

What am I doing wrong?

#Must be set in the global scope see: https://forum.nginx.org/read.php?2,152294,152294
#Why this is important especially with Plex as it makes a lot of requests http://vincent.bernat.im/en/blog/2011-ssl-session-reuse-rfc5077.html / https://www.peterbe.com/plog/ssl_session_cache-ab
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;

#Upstream to Plex
upstream plex_backend {
    server 192.168.4.102:32400;
    keepalive 32;
}

server {
    listen 80;
    server_name plex.server.com;
    return 301 https://plex.server.com:32400$request_uri;
}

server {
listen 443 ssl http2;
listen [::]:443 ssl http2;

server_name plex.server.com;
ssl_certificate /etc/letsencrypt/live/plex.server.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/plex.server.com/privkey.pem;

# Redirect the browser to our port 32400 config
return 301 $scheme://plex.server.com:32400$request_uri;

}

server {
	#listen 80;
  	listen 32400 ssl http2;
  	#error_page  497 https://plex.server.com:32400$request_uri;
	  error_page  497 https://192.168.7.102:32400$request_uri;
	#Enabling http2 can cause some issues with some devices, see #29 - Disable it if you experience issues
	# listen 443 ssl http2; #http2 can provide a substantial improvement for streaming: https://blog.cloudflare.com/introducing-http2/
	server_name plex.server.com;

	send_timeout 100m; #Some players don't reopen a socket and playback stops totally instead of resuming after an extended pause (e.g. Chrome)

	#Faster resolving, improves stapling time. Timeout and nameservers may need to be adjusted for your location Google's have been used here.
	resolver 8.8.4.4 8.8.8.8 valid=300s;
	resolver_timeout 10s;

	#Use letsencrypt.org to get a free and trusted ssl certificate
	ssl_certificate /etc/letsencrypt/live/plex.server.com/fullchain.pem;
	ssl_certificate_key /etc/letsencrypt/live/plex.server.com/privkey.pem;

	ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
	ssl_prefer_server_ciphers on;
	#Intentionally not hardened for security for player support and encryption video streams has a lot of overhead with something like AES-256-GCM-SHA384.
	ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';

	#Why this is important: https://blog.cloudflare.com/ocsp-stapling-how-cloudflare-just-made-ssl-30/
	ssl_stapling on;
	ssl_stapling_verify on;
	#For letsencrypt.org you can get your chain like this: https://esham.io/2016/01/ocsp-stapling
	ssl_trusted_certificate /etc/letsencrypt/live/plex.server.com/chain.pem;

	#Reuse ssl sessions, avoids unnecessary handshakes
	#Turning this on will increase performance, but at the cost of security. Read below before making a choice.
	#https://github.com/mozilla/server-side-tls/issues/135
	#https://wiki.mozilla.org/Security/Server_Side_TLS#TLS_tickets_.28RFC_5077.29
	#ssl_session_tickets on;
	ssl_session_tickets off;

	#Use: openssl dhparam -out dhparam.pem 2048 - 4096 is better but for overhead reasons 2048 is enough for Plex.
	ssl_dhparam /etc/ssl/certs/plex.pem;
	ssl_ecdh_curve secp384r1;

	#Will ensure https is always used by supported browsers which prevents any server-side http > https redirects, as the browser will internally correct any request to https.
	#Recommended to submit to your domain to https://hstspreload.org as well.
	#!WARNING! Only enable this if you intend to only serve Plex over https, until this rule expires in your browser it WONT BE POSSIBLE to access Plex via http, remove 'includeSubDomains;' if you only want it to effect your Plex (sub-)domain.
	#This is disabled by default as it could cause issues with some playback devices it's advisable to test it with a small max-age and only enable if you don't encounter issues. (Haven't encountered any yet)
	#add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

	#Plex has A LOT of javascript, xml and html. This helps a lot, but if it causes playback issues with devices turn it off. (Haven't encountered any yet)
	gzip on;
	gzip_vary on;
	gzip_min_length 1000;
	gzip_proxied any;
	gzip_types text/plain text/css text/xml application/xml text/javascript application/x-javascript image/svg+xml;
	gzip_disable "MSIE [1-6]\.";

	#Nginx default client_max_body_size is 1MB, which breaks Camera Upload feature from the phones.
	#Increasing the limit fixes the issue. Anyhow, if 4K videos are expected to be uploaded, the size might need to be increased even more
	client_max_body_size 100M;

	#Forward real ip and host to Plex
	proxy_set_header Host $host;
	proxy_set_header X-Real-IP $remote_addr;
	#When using ngx_http_realip_module change $proxy_add_x_forwarded_for to '$http_x_forwarded_for,$realip_remote_addr'
	proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
	proxy_set_header X-Forwarded-Proto $scheme;

	#Websockets
	proxy_http_version 1.1;
	proxy_set_header Upgrade $http_upgrade;
	proxy_set_header Connection "upgrade";

        #Disables compression between Plex and Nginx, required if using sub_filter below.
	#May also improve loading time by a very marginal amount, as nginx will compress anyway.
        #proxy_set_header Accept-Encoding "";

	#Buffering off send to the client as soon as the data is received from Plex.
	proxy_redirect off;
	proxy_buffering off;

	location / {
		#Example of using sub_filter to alter what Plex displays, this disables Plex News.
		#sub_filter ',news,' ',';
		#sub_filter_once on;
		#sub_filter_types text/xml;
		proxy_pass http://plex_backend;
	}

	#PlexPy forward example, works the same for other services.
	#location /plexpy {
	#	proxy_pass http://127.0.0.1:8181;
	#}
}

Couple of questions:

  1. Are you testing the connection with local or remote clients?
  2. Are you try to access Plex via plex.tv/web or directly via plex.server.com?
  3. Do the NGINX logs contain anything useful (either access or error log)?
  4. What do the clients show you? Do they get 404s? 502?
  5. Does your PMS require a secure connection?
  6. Which custom access domain do you have set up in your PMS?

And one remark: If I was to go through the effort of setting up a reverse proxy for Plex, I would not open 3 ports to the public. The great thing about a reverse proxy is that you can run several services behind one IP and one port. And unless you are planning to access Plex Web via your custom URL, there’s not even a reason to open more than one port. Plex will tell your clients that they can access your server via https://server.plex.com:[port] so there’s no need to open several ports and redirect between them.

To respond:

  1. Remote clients
  2. Web Browser is plex.server.com which works.
  3. Checking the logs
  4. Error 401
  5. Yes
  6. plex.server.com

One thing you can change already: Since your PMS requires a secure connection, you have to use proxy_pass https://plex_backend; instead of proxy_pass http://plex_backend;

Also, check which port clients are trying to use by default. If they use anything but 32400, make your custom access domain more specific: https://plex.server.com:32400

1 Like

The OP wantes SSL termination on the nginx reverse proxy.

This makes neither tcp passthrough an option, nor reverse proxying to a https target a valid solution.
If https termination can remain in plex, i would strongly suggest to use tcp passtrough, rather then SSL offloading.

For SSL offloading, it is required to set “Secure Connections” to disabled (or prefered). Also the URL will not match the certificates in PMS anymore. I assume you use the linuxserver/letsencrypt image to generate the certificates, don’t you?

In order to make the whole setup work, you need to set the “Customer Server Access URL” in PMS to https://plex.server.com:32400.

I am only seeing “Friendly name” which I already was plex.server.com

Just to be sure, you understand what the “Custom server access URLs” is and where it can be configured?

I see friendly name but nothing about “customer server access URLs”

You need to configure that unter Network in the PMS settings. Make sure you have Advanced Options enabled.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.