First off, the apache2 conf file is to have a web interface that is exactly like being on localhost:32400. You still must have port forwarding set up and wherever you are trying to access plex from must have that port open in order for everything to work properly. If you are slightly paranoid about security, you can use this (my) apache2 conf file:
EDIT 5/9/18: Added blob: to media-src in Content-Security-Policy to allow trailers to play.
EDIT: Update as of 4/17/2019.
You will need to be using Apache2 >= 2.4.17 to use this and several mods (proxy, ssl, proxy_wstunnel, http, dir, env, headers, proxy_balancer, proxy_http, rewrite I think is all of them).
EDIT: Update as of 2/18/2021.
- Updated Content Security Policy
- Added Permissions Policy
- Removed Rewrite
- Added
<Location>
sections - gzip defalte for common files (javascript, xml, etc.)
- Updated Ciphers
New config (February 2021)
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 URL subpath?
Change to this:
<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>
Old config (April 2019)
<IfModule mod_ssl.c>
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
ModPagespeed Off
<VirtualHost *:80>
ServerName ${serv_name}
DocumentRoot /var/www/html
ServerAdmin aw@hell.no
RewriteEngine On
RewriteCond %{SERVER_NAME} =${serv_name}
RewriteCond %{HTTPS} Off
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
ErrorLog ${APACHE_LOG_DIR}/${serv_name}.error.log
CustomLog ${APACHE_LOG_DIR}/${serv_name}.access.log combined
</VirtualHost>
<VirtualHost *:443>
ServerName ${serv_name}
DocumentRoot /var/www/html
ServerAdmin aw@hell.no
ErrorLog ${APACHE_LOG_DIR}/${serv_name}.error.log
CustomLog ${APACHE_LOG_DIR}/${serv_name}.access.log combined
### Let's Encrypt Section ###
SSLCertificateFile /etc/letsencrypt/live/${serv_name}/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/${serv_name}/privkey.pem
#Include /etc/letsencrypt/options-ssl-apache.conf
Options -Includes -ExecCGI
### Deny http1.0 requests ###
RewriteEngine On
RewriteCond %{SERVER_PROTOCOL} ^HTTP/1\.0$
#RewriteCond %{REQUEST_URI} !^/404/$
RewriteRule ^ - [F]
### Harden Security ###
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 "same-origin"
Header always set Feature-Policy "accelerometer 'none'; camera 'none'; geolocation 'none'; gyroscope 'none'; magnetometer 'none'; microphone 'none'; payment 'none'; usb 'none';"
Header always set Content-Security-Policy "default-src 'self' https:; font-src 'self' data: ${plex_url} ${serv_name}; media-src 'self' blob: ${plex_url} ${serv_name}; script-src 'self' 'unsafe-inline' ${plex_url} ${serv_name} plex.tv www.gstatic.com; style-src 'self' ${plex_url} ${serv_name}; img-src 'self' data: blob: ${plex_url} ${serv_name} plex.tv *.plex.tv; worker-src *; frame-src 'none'; connect-src 'self' wss: https: ${plex_url} ${serv_name} plex.tv *.plex.direct *.plex.tv;"
SSLCipherSuite ECDHE+RSA+AES256+GCM+SHA512:DHE+RSA+AES256+GCM+SHA512:ECDHE+RSA+AES256+GCM+SHA384:DHE+RSA+AES256+GCM+SHA384:ECDHE+RSA+AES256+SHA384:EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4
SSLProtocol All -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
SSLHonorCipherOrder On
### Plex Specific Section ###
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>
</IfModule>