Apache2 Reverse Proxy for Subdomain

I need to update my above Plex configuration. This current configuration is based of Server Version 1.16.5.1488 and Web Version: 3.108.2. This updated config file allows the playing of trailers and TV Show theme music where as the previous one did not. I hope this helps somebody from searching everywhere.

Requirements

  1. Apache version > 2.4
  2. A bunch of mod’s enabled (proxy, ssl, proxy_wstunnel, http, dir, defalte, env, headers, proxy_balancer, proxy_http, rewrite I think is all of them)
  3. Protocols h2 http/1.1 needs apachectl -V 2.4.17 and higher…
  4. Tested with server versions 1.21.3.4046 and 1.21.3.4021 with web version 4.51.1

Edit as of February 2021

  • Updated Content Security Policy
  • Added Permissions Policy
  • Removed Rewrite
  • Added <Location> sections
  • gzip defalte for common files (javascript, xml, etc.)
  • Updated Ciphers
    DEFINE plex_url 192.168.1.22
    DEFINE plex_port 32400
    DEFINE serv_name plex.domain.com
    ServerTokens Prod
    SSLStaplingCache "shmcb:${APACHE_LOG_DIR}/stapling-cache(150000)"
    SSLSessionCache "shmcb:${APACHE_LOG_DIR}/ssl_scache(512000)"
    SSLSessionCacheTimeout 300
### If you have Google's Mod PageSpeed, disable it ###
    <IfModule mod_pagespeed_ap24.c>
        ModPagespeed Off
    </IfModule>
<VirtualHost *:80>
    ServerName ${serv_name}
    Redirect / https://plex.domain.com
    ErrorLog ${APACHE_LOG_DIR}/plex.error.log
    CustomLog ${APACHE_LOG_DIR}/plex.access.log combined
</VirtualHost>
<VirtualHost *:443>
    ServerName ${serv_name}
    DocumentRoot /var/www/html
    ErrorLog ${APACHE_LOG_DIR}/plex.error.log
    CustomLog ${APACHE_LOG_DIR}/plex.access.log combined
### Let's Encrypt Section ###
    SSLCertificateFile /etc/letsencrypt/live/domain.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/domain.com/privkey.pem
### Deny http1.0 requests ###
    Protocols h2 http/1.1
### Harden Security ###
    ProxyRequests Off
    ProxyPreserveHost On
    ProxyTimeout 600
    SSLProxyEngine On
    RequestHeader set Front-End-Https "On"
    ServerSignature Off
    SSLCompression Off
    SSLUseStapling On
    SSLStaplingResponderTimeout 20
    SSLStaplingReturnResponderErrors Off
    SSLSessionTickets Off
### Add headers ###
    RequestHeader set X-Forwarded-Proto 'https' env=HTTPS
    Header always set Strict-Transport-Security "max-age=15552000;"
    Header always set X-Content-Type-Options nosniff
    Header always set X-Robots-Tag none
    Header always set X-XSS-Protection "1; mode=block"
    Header always set X-Frame-Options "SAMEORIGIN"
    Header always set Referrer-Policy "same-origin"
    Header always set Permissions-Policy "geolocation=(self), midi=(self), sync-xhr=(self), microphone=(self), camera=(self), magnetometer=(self), gyroscope=(self), fullscreen=(self), payment=(self)"
### Content Security Policy for the overly paranoid - may break with Plex server updates (works with server version 1.21.3.4046 and 1.21.3.4021) ###
    Header always set Content-Security-Policy "default-src 'none'; base-uri 'self' ${serv_name}; font-src 'self' data: ${serv_name}; media-src 'self' data: blob: ${serv_name} https://*.plex.direct:32400 https://video.internetvideoarchive.net https://*.cloudfront.net; script-src 'self' 'unsafe-inline' 'unsafe-eval' domain.com ${serv_name}; style-src 'self' 'unsafe-inline' ${serv_name}; img-src 'self' data: blob: https: ${serv_name}; worker-src * blob:; frame-src 'self'; connect-src 'self' https: domain.com ${serv_name} wss://*.plex.direct:32400 wss://pubsub.plex.tv; object-src 'self' ${serv_name}; frame-ancestors 'self' domain.com ${serv_name}; form-action 'self' ${serv_name}; manifest-src 'self' ${serv_name}; script-src-elem 'self' 'unsafe-inline' domain.com ${serv_name} www.gstatic.com"
### Add secure ciphers ###
    SSLCipherSuite TLS-CHACHA20-POLY1305-SHA256:TLS-AES-256-GCM-SHA384:TLS-AES-128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256
### Only enable TLSv1.2 and TLSv1.3 ###
    SSLProtocol All -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
### Tell clients to use server defined ciphers and orders ###
    SSLHonorCipherOrder On
### If path is for Let's Encrypt, don't proxy ###
    ProxyPassMatch ^/.well-known !
### Plex Specific Section ###
 ## Plex has A LOT of javascript, xml and html. This helps a lot, but if it causes playback issues with devices, disable this section
    <IfModule mod_deflate.c>
        AddOutputFilterByType DEFLATE text/html
        AddOutputFilterByType DEFLATE text/plain
        AddOutputFilterByType DEFLATE text/css
        AddOutputFilterByType DEFLATE application/javascript
        AddOutputFilterByType DEFLATE text/javascript
        AddOutputFilterByType DEFLATE application/x-javascript
        AddOutputFilterByType DEFLATE image/svg+xml
        AddOutputFilterByType DEFLATE image/x-icon
        AddOutputFilterByType DEFLATE application/vnd.ms-fontobject
        AddOutputFilterByType DEFLATE application/x-font
        AddOutputFilterByType DEFLATE application/x-font-opentype
        AddOutputFilterByType DEFLATE application/x-font-otf
        AddOutputFilterByType DEFLATE application/x-font-truetype
        AddOutputFilterByType DEFLATE application/x-font-ttf
        AddOutputFilterByType DEFLATE font/opentype
        AddOutputFilterByType DEFLATE font/otf
        AddOutputFilterByType DEFLATE font/ttf
        AddOutputFilterByType DEFLATE application/rss+xml
        AddOutputFilterByType DEFLATE application/xhtml+xml
        AddOutputFilterByType DEFLATE application/xml
        AddOutputFilterByType DEFLATE text/xml
        BrowserMatch ^Mozilla/4 gzip-only-text/html
        BrowserMatch ^Mozilla/4\.0[678] no-gzip
        BrowserMatch \bMSIE !no-gzip !gzip-only-text/html
        Header append Vary User-Agent
    </IfModule>
 ## Proxy all web traffic here ## 
    <Location />
        ProxyPass http://${plex_url}:${plex_port}/
        ProxyPassReverse http://${plex_url}:${plex_port}/
    </Location>
 ## Proxy all websocket requests here ## 
    <Location /:/>
        ProxyPass wss://${plex_url}:${plex_port}/:/
        ProxyPassReverse wss://${plex_url}:${plex_port}/:/
    </Location>
### Don't know if we still need this ###
    <Proxy *>
        Order deny,allow
        Allow from all
    </Proxy>
    #RewriteEngine on
    #RewriteCond %{REQUEST_URI} !^/web
    #RewriteCond %{HTTP:X-Plex-Device} ^$
    #RewriteCond %{REQUEST_METHOD} !^(OPTIONS)$
    #RewriteCond %{QUERY_STRING} (^|&)X-Plex-Device=(&|$) [OR]
    #RewriteCond %{QUERY_STRING} !(^|&)X-Plex-Device=
    #RewriteRule ^/$ /web/$1 [R,L]
</VirtualHost>

Need Plex URL subpath?

Change to this (doesn’t actually work :/):

 ## Proxy all web traffic here ## 
    <Location /plex/>
        ProxyPass http://${plex_url}:${plex_port}/
        ProxyPassReverse http://${plex_url}:${plex_port}/
    </Location>
 ## Proxy all websocket requests here ## 
    <Location /plex/:/>
        ProxyPass wss://${plex_url}:${plex_port}/:/
        ProxyPassReverse wss://${plex_url}:${plex_port}/:/
    </Location>

Apache .conf file (old August 2019)

    DEFINE plex_url 127.0.0.1
    DEFINE plex_port 32400
    DEFINE public_url subdomain.plex.tv
    DEFINE email admin@subdomain.plex.tv
    ServerTokens Prod
    SSLStaplingCache "shmcb:${APACHE_LOG_DIR}/stapling-cache(150000)"
    SSLSessionCache "shmcb:${APACHE_LOG_DIR}/ssl_scache(512000)"
    SSLSessionCacheTimeout 300
### If you have Google's Mod PageSpeed, disable it
    #ModPagespeed Off
<VirtualHost *:80>
    ServerName ${public_url}
    DocumentRoot /var/www/offline
    ServerAdmin ${email}
    RewriteEngine on
    RewriteCond %{SERVER_NAME} =${public_url}
    RewriteCond %{HTTPS} off
    RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
</VirtualHost>
<VirtualHost *:443>
    ServerName ${public_url}
    DocumentRoot /var/www/offline
    ServerAdmin ${email}
    ErrorLog ${APACHE_LOG_DIR}/${public_url}.error.log
    CustomLog ${APACHE_LOG_DIR}/${public_url}.access.log combined
    SSLEngine On
    SSLCertificateFile /etc/letsencrypt/live/${public_url}/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/${public_url}/privkey.pem
    #Include /etc/letsencrypt/options-ssl-apache.conf
### Forbid the http1.0 protocol ###
    Protocols h2 http/1.1
    #Options -Includes -ExecCGI
    #LimitRequestBody 512000
    #FileETag None
    #TraceEnable off
    Timeout 360
    ProxyRequests Off
    ProxyPreserveHost On
    ProxyTimeout 600
    ProxyReceiveBufferSize 4096
    SSLProxyEngine On
    RequestHeader set Front-End-Https "On"
    ServerSignature Off
    SSLCompression Off
    SSLUseStapling On
    SSLStaplingResponderTimeout 5
    SSLStaplingReturnResponderErrors Off
    SSLSessionTickets Off
    RequestHeader set X-Forwarded-Proto 'https' env=HTTPS
    Header always set Strict-Transport-Security "max-age=15552000; preload"
    Header always set X-Content-Type-Options nosniff
    Header always set X-Robots-Tag none
    Header always set X-XSS-Protection "1; mode=block"
    Header always set X-Frame-Options "SAMEORIGIN"
    Header always set Referrer-Policy "strict-origin-when-cross-origin"
    Header always set Content-Security-Policy "default-src 'self' https:; font-src 'self' data: ${plex_url} ${public_url}; media-src 'self' blob: data: https: ${plex_url} ${public_url} *.plex.direct *.plex.tv plex.tv; script-src 'self' 'unsafe-inline' 'unsafe-eval' ${plex_url} ${public_url} plex.tv *.plex.tv gstatic.com *.gstatic.com *.plex.direct; style-src 'self' ${plex_url} ${public_url} *.plex.direct; img-src 'self' data: blob: ${plex_url} ${public_url} plex.tv *.plex.tv *.plex.direct; worker-src *; frame-src 'none'; connect-src 'self' wss: https: ${plex_url} ${public_url} plex.tv *.plex.direct *.plex.tv;"
 ## If you want to be safer, remove the 'unsafe-inline' 'unsafe-eval' from above and use Chrome to get the sha-256 sums and input below (below was for Server version: 1.16.5.1488; Web version: 3.108.2)
	#Header always set Content-Security-Policy "default-src 'self' https:; font-src 'self' data: ${plex_url} ${public_url}; media-src 'self' blob: data: https: ${plex_url} ${public_url} *.plex.direct *.plex.tv plex.tv; script-src 'self' 'sha256-8yKKbip2qr14RHV8H1qDEbRAm9Mmf5ePeQh+wB5pMCw=' 'sha256-pKO/nNgeauDINvYfxdygP3mGssdVQRpRNxaF7uPRoGM=' 'sha256-mrLkgfrqAhdxc2TvIODT0I7QtvuQLMS9AgtfLL9eMXo=' ${plex_url} ${public_url} plex.tv *.plex.tv gstatic.com *.gstatic.com *.plex.direct; style-src 'self' ${plex_url} ${public_url} *.plex.direct; img-src 'self' data: blob: ${plex_url} ${public_url} plex.tv *.plex.tv *.plex.direct; worker-src *; frame-src 'none'; connect-src 'self' wss: https: ${plex_url} ${public_url} plex.tv *.plex.direct *.plex.tv;"
    Header always set Feature-Policy "geolocation 'self'; midi 'self'; sync-xhr 'self'; microphone 'self'; camera 'self'; magnetometer 'self'; gyroscope 'self'; speaker 'self'; fullscreen 'self'; payment 'self'"
### Use next two for very secure connections ###
    SSLHonorCipherOrder On
    SSLCipherSuite EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH
    SSLProtocol All -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
### Use next two for secure connections and supports more endpoints ###
    #SSLCipherSuite EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH:ECDHE-RSA-AES128-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA128:DHE-RSA-AES128-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA128:ECDHE-RSA-AES128-SHA384:ECDHE-RSA-AES128-SHA128:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA128:DHE-RSA-AES128-SHA128:DHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA384:AES128-GCM-SHA128:AES128-SHA128:AES128-SHA128:AES128-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4
    #SSLProtocol All -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
### Actually proxy the traffic and really the only important part ###
	ProxyPass / http://${plex_url}:${plex_port}/
	ProxyPassReverse / http://${plex_url}:${plex_port}/
	ProxyPass /:/ ws://${plex_url}:${plex_port}/:/
	ProxyPassReverse /:/ ws://${plex_url}:${plex_port}/:/
	ProxyPass /:/ wss://${plex_url}:${plex_port}/:/
	ProxyPassReverse /:/ wss://${plex_url}:${plex_port}/:/
	LimitRequestBody 512000
	FileETag None
	TraceEnable off
	#Header edit Set-Cookie ^(.*)$ ;HttpOnly;Secure
	Timeout 60
	<Location /:/websockets/notifications>
		ProxyPass wss://${plex_url}:${plex_port}/:/websockets/notifications
		ProxyPassReverse wss://${plex_url}:${plex_port}/:/websockets/notifications
	</Location>
	<Proxy *>
		Order deny,allow
		Allow from all
	</Proxy>
	RewriteEngine on
	RewriteCond %{REQUEST_URI} !^/web
	RewriteCond %{HTTP:X-Plex-Device} ^$
	RewriteCond %{REQUEST_METHOD} !^(OPTIONS)$
	RewriteCond %{QUERY_STRING} (^|&)X-Plex-Device=(&|$) [OR]
	RewriteCond %{QUERY_STRING} !(^|&)X-Plex-Device=
	RewriteRule ^/$ /web/$1 [R,L]
</VirtualHost>
1 Like

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