# Nginx Reverse Proxy for Subdomain

**URL:** <https://forums.plex.tv/t/nginx-reverse-proxy-for-subdomain/692172>\
**Category:** General Discussions\
**Created:** [February 18, 2021, 8:23pm UTC](https://forums.plex.tv/t/nginx-reverse-proxy-for-subdomain/692172 "2021-02-18T20:23:38Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![jugzadmin](https://sea1.discourse-cdn.com/plex/user_avatar/forums.plex.tv/jugzadmin/32/145893_2.png) [@jugzadmin](https://forums.plex.tv/u/jugzadmin)\
**Post date:** [February 18, 2021, 8:23pm UTC](https://forums.plex.tv/t/nginx-reverse-proxy-for-subdomain/692172/1 "2021-02-18T20:23:39Z")

</div>

## My setup

- nginx 1.19.7
- Plex server versions 1.21.3.4046 and 1.21.3.4021 (or higher, maybe lower)
- Ubuntu 20.04 LTS

### Requirements

- nginx \>1.17.0
- nginx modules: `--with-debug --with-cc-opt=-Wimplicit-fallthrough=0 --with-pcre=objs/lib/pcre-8.44 --with-zlib=objs/lib/zlib-1.2.11 --with-openssl-opt='no-asm no-tests' --with-file-aio --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_degradation_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_image_filter_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-http_xslt_module --with-mail --with-mail_ssl_module --with-poll_module --with-select_module --with-stream --with-stream_ssl_module --with-stream_ssl_preread_module`

### A couple things worth mentioning:

- Only use TLSv1.2 and TLSv1.3
- Change ciphers accordingly
- You don’t need the `error_page` section if you don’t want (but will tell you if Plex server isn’t running for some reason or another)
- Add some [security headers](https://securityheaders.com/)
- Turn on SSL Session Cache
- SSL Stapling (if don’t have a self-signed certificate)
- Use upstream if you want (required [nginx upstream module](https://nginx.org/en/docs/http/ngx_http_upstream_module.html))

### What this config enables on Plex

- Can watch Live TV, Movies & Shows on Plex
- Plays TV Show theme songs
- Plays Movie trailers, extras, and featurettes

### Finally

If you run into problems with images not displaying, media not playing, or something else that worked before, comment out the `add_header Content-Security-Policy` line and reload nginx and see if that helps.

## plex.conf

```nginx
upstream plex {
    server localhost:32400;
}
server {
    listen 443 ssl http2;
    server_name plex.domain.com;
    ssl_certificate /etc/nginx/ssl/cert.pem;
    ssl_certificate_key /etc/nginx/ssl/key.key;
    client_max_body_size 500M;
    send_timeout 100m;
    
    ssl_session_cache builtin:1000 shared:SSL:10m;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers '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';
    ssl_stapling on;
    ssl_stapling_verify on;
    # For LetsEncrypt/Certbot, you can get your chain like this: https://esham.io/2016/01/ocsp-stapling
    ssl_trusted_certificate /path/to/intermediate/ocsp/cert-r3.pem;
    
    add_header Strict-Transport-Security max-age=15768000;
    add_header Referrer-Policy strict-origin-when-cross-origin;
    add_header X-Frame-Options deny;
    add_header X-Content-Type-Options nosniff;
    add_header X-XSS-Protection "1; mode=block";
    add_header Permissions-Policy "geolocation=(self), midi=(self), sync-xhr=(self), microphone=(self), camera=(self), magnetometer=(self), gyroscope=(self), fullscreen=(self), payment=(self)";
    # Pay attention to how many domains we need to allow
    add_header Content-Security-Policy "default-src 'none'; base-uri 'self' plex.domain.com; font-src 'self' data: plex.domain.com; media-src 'self' data: blob: plex.domain.com https://*.plex.direct:32400 https://video.internetvideoarchive.net https://*.cloudfront.net; script-src 'self' 'unsafe-inline' 'unsafe-eval' domain.com plex.domain.com; style-src 'self' 'unsafe-inline' plex.domain.com; img-src 'self' data: blob: https: plex.domain.com; worker-src * blob:; frame-src 'self'; connect-src 'self' https: domain.com plex.domain.com wss://*.plex.direct:32400 wss://pubsub.plex.tv; object-src 'self' plex.domain.com; frame-ancestors 'self' domain.com plex.domain.com; form-action 'self' plex.domain.com; manifest-src 'self' plex.domain.com; script-src-elem 'self' 'unsafe-inline' domain.com plex.domain.com www.gstatic.com";

    error_page 500 502 503 504 /50x.html;
        location = /50x.html {
        root /path/to/503;
    }
    
    # gzip source: https://github.com/toomuchio/plex-nginx-reverseproxy/blob/master/nginx.conf
    # Plex has A LOT of javascript, xml and html. This helps a lot, but if it causes playback issues with devices, disable this section
    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]\.";

    # Forward real ip and host to Plex
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
	
    # If not using ngx_http_realip_module change '$http_x_forwarded_for,$realip_remote_addr' to $proxy_add_x_forwarded_for
    proxy_set_header X-Forwarded-For '$proxy_add_x_forwarded_for,$realip_remote_addr';
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header Sec-WebSocket-Extensions $http_sec_websocket_extensions;
    proxy_set_header Sec-WebSocket-Key $http_sec_websocket_key;
    proxy_set_header Sec-WebSocket-Version $http_sec_websocket_version;

    # 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 / {
        proxy_pass http://plex/;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_ssl_verify off;
        proxy_http_version 1.1;
        proxy_set_header Host $http_host;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_read_timeout 86400;
    }
}

```

## Need Plex for a URL subpath?

Change to this:

```apache
location /plex/ {
        proxy_pass http://plex/;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_ssl_verify off;
        proxy_http_version 1.1;
        proxy_set_header Host $http_host;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_read_timeout 86400;
    }

```

## Other notes

- This reverse proxy, but for apache [is here](https://forums.plex.tv/t/apache2-reverse-proxy-for-subdomain/447162).
- Using nginx with Plex is **lightening** fast.
- Have more than one Plex server? Try this for some load balancing:

```apache
upstream plex {
    server localhost:32400 weight=5;
    server 192.168.1.22:32400 max_conns=2;
}

```

---

<div class="post-metadata">

**Author:** ![Volts](https://avatars.discourse-cdn.com/v4/letter/v/a5b964/32.png) [@Volts](https://forums.plex.tv/u/Volts)\
**Post date:** [February 18, 2021, 11:12pm UTC](https://forums.plex.tv/t/nginx-reverse-proxy-for-subdomain/692172/2 "2021-02-18T23:12:16Z")

</div>

Neat. Does this work with most clients, do you know?

> [@jugzadmin](#):
>
> `client_max_body_size 500M;`

Why do you set this? Image/video uploads, I’m guessing?

> [@jugzadmin](#):
>
> `send_timeout 100m`

And that? That’s so long.

> [@jugzadmin](#):
>
> `proxy_buffering off;`

And this one. I’d have guessed this would be harmful.

---

<div class="post-metadata">

**Author:** ![jugzadmin](https://sea1.discourse-cdn.com/plex/user_avatar/forums.plex.tv/jugzadmin/32/145893_2.png) [@jugzadmin](https://forums.plex.tv/u/jugzadmin)\
**Post date:** [February 22, 2021, 8:10pm UTC](https://forums.plex.tv/t/nginx-reverse-proxy-for-subdomain/692172/3 "2021-02-22T20:10:46Z")

</div>

As far as I know, this should work with lot’s of _modern_ clients because of the newer security protocols.

I _do_ user Plex for video uploads from my iPhone. A 60 second 1080p 60fps video is approximately 100MB. This is the reason my `client_max_body_size` is set so high. You may adjust this to something way more reasonable if you do not use that functionality for Plex.

“Some players don’t reopen a socket and playback stops totally instead of resuming after an extended pause i.e. Chrome”. This was taken directly from [this source](https://github.com/toomuchio/plex-nginx-reverseproxy/blob/master/nginx.conf). So, if I interpret that correctly, if you watch a two hour movie, in theory, it might stop playing.

The `proxy_buffering` set to off tells nginx to immediately send the data to and from Plex rather than adding overhead between the client and the server.

---

<div class="post-metadata">

**Author:** ![system](https://global.discourse-cdn.com/plex/original/3X/2/a/2acb9765406f63293d357b4ec509ec39aa28f2ad.png) [@system](https://forums.plex.tv/u/system)\
**Post date:** [May 23, 2021, 8:10pm UTC](https://forums.plex.tv/t/nginx-reverse-proxy-for-subdomain/692172/4 "2021-05-23T20:10:53Z")

</div>

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