Help set up a reverse proxy

I asked a question on GitHub View submitted traffic · Issue #3617 · Chocobozzz/PeerTube · GitHub

@Alterak I think there is an issue in your reverse proxy config: the web browser cannot connect to the tracker. Ensure you have the appropriate nginx config: PeerTube/support/nginx/peertube at develop · Chocobozzz/PeerTube · GitHub

I figured out the reason, but I can’t connect the socket with the reverse proxy. I will be very grateful to you if you can help.
My scheme of work is as follows:

WEB-PROXY (ip 192.168.2.20)

server {
  server_name www.peervideo.ru peervideo.ru;

  error_log /var/log/nginx/peervideo.ru_error.log crit;

  location / {
        proxy_pass http://192.168.2.27/;
        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_send_timeout 15m;
        proxy_read_timeout 15m;
    }
######## Connect socket #######################


########################

  location ^~ /.well-known/acme-challenge/ {
   default_type "text/plain";
   root /var/www/letsencrypt;
  }
  location = /.well-known/acme-challenge/ {
   return 404;
  }

    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/peervideo.ru/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/peervideo.ru/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
    if ($host = www.peervideo.ru) {
        return 301 https://$host$request_uri;
    } # managed by Certbot

  server_name www.peervideo.ru peervideo.ru;
    listen 80;
    return 404; # managed by Certbot
}

PEERTUBE Server (ip 192.168.2.27)

upstream backend {
  server 127.0.0.1:9000;
}

#server {
#  listen 443 ssl http2;
#  listen [::]:443 ssl http2;
#  server_name peervideo.ru;

server {
  listen 80;
  listen [::]:80;
  server_name peervideo.ru;

  access_log /var/log/nginx/peertube.access.log; # reduce I/0 with buffer=10m flush=5m
  error_log  /var/log/nginx/peertube.error.log;

  set_real_ip_from 192.168.2.20;
  real_ip_header X-Real-IP;

  ##
  # Certificates
  # you need a certificate to run in production. see https://letsencrypt.org/
  ##
#  ssl_certificate     /etc/letsencrypt/live/peervideo.ru/fullchain.pem;
#  ssl_certificate_key /etc/letsencrypt/live/peervideo.ru/privkey.pem;

  location ^~ '/.well-known/acme-challenge' {
    default_type "text/plain";
    root /var/www/certbot;
  }

  ##
  # Security hardening (as of Nov 15, 2020)
  # based on Mozilla Guideline v5.6
  ##

#  ssl_protocols             TLSv1.2 TLSv1.3;
#  ssl_prefer_server_ciphers on;
#  ssl_ciphers               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; # add ECDHE-RSA-AES256-SHA if you want compatibility with Android 4
#  ssl_session_timeout       1d; # defaults to 5m
#  ssl_session_cache         shared:SSL:10m; # estimated to 40k sessions
#  ssl_session_tickets       off;
#  ssl_stapling              on;
#  ssl_stapling_verify       on;
  # HSTS (https://hstspreload.org), requires to be copied in 'location' sections that have add_header directives
  #add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";

  ##
  # Application
  ##

  location @api {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host            $host;
    proxy_set_header X-Real-IP       $remote_addr;

    client_max_body_size  100k; # default is 1M

    proxy_connect_timeout 10m;
    proxy_send_timeout    10m;
    proxy_read_timeout    10m;
    send_timeout          10m;

    proxy_pass http://backend;
  }

  location / {
    try_files /dev/null @api;
  }

  location = /api/v1/users/me/avatar/pick {
    limit_except POST HEAD { deny all; }

    client_max_body_size                      2M; # default is 1M
    add_header            X-File-Maximum-Size 2M always; # inform backend of the set value in bytes

    try_files /dev/null @api;
  }

  location = /api/v1/videos/upload {
    limit_except POST HEAD { deny all; }

    # This is the maximum upload size, which roughly matches the maximum size of a video file.
    # Note that temporary space is needed equal to the total size of all concurrent uploads.
    # This data gets stored in /var/lib/nginx by default, so you may want to put this directory
    # on a dedicated filesystem.
    client_max_body_size                      8G; # default is 1M
    add_header            X-File-Maximum-Size 8G always; # inform backend of the set value in bytes

    try_files /dev/null @api;
  }

  ##
  # Websocket
  ##

  location @api_websocket {
    proxy_http_version 1.1;
    proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header   Host            $host;
    proxy_set_header   X-Real-IP       $remote_addr;
    proxy_set_header   Upgrade         $http_upgrade;
    proxy_set_header   Connection      "upgrade";

    proxy_pass http://backend;
  }

  location /socket.io {
    try_files /dev/null @api_websocket;
  }

  location /tracker/socket {
    # Peers send a message to the tracker every 15 minutes
    # Don't close the websocket before then
    proxy_read_timeout 15m; # default is 60s

    try_files /dev/null @api_websocket;
  }

  ##
  # Performance optimizations
  # For extra performance please refer to https://github.com/denji/nginx-tuning
  ##

  root /var/www/peertube/storage-fs;

  # Enable compression for JS/CSS/HTML, for improved client load times.
  # It might be nice to compress JSON/XML as returned by the API, but
  # leaving that out to protect against potential BREACH attack.
  gzip              on;
  gzip_vary         on;
  gzip_types        # text/html is always compressed by HttpGzipModule
                    text/css
                    application/javascript
                    font/truetype
                    font/opentype
                    application/vnd.ms-fontobject
                    image/svg+xml;
  gzip_min_length   1000; # default is 20 bytes
  gzip_buffers      16 8k;
  gzip_comp_level   2; # default is 1

  client_body_timeout       30s; # default is 60
  client_header_timeout     10s; # default is 60
  send_timeout              10s; # default is 60
  keepalive_timeout         10s; # default is 75
  resolver_timeout          10s; # default is 30
  reset_timedout_connection on;

  tcp_nopush                on; # send headers in one piece
  tcp_nodelay               on; # don't buffer data sent, good for small data bursts in real time

  # If you have a small /var/lib partition, it could be interesting to store temp nginx uploads in a different place
  # See https://nginx.org/en/docs/http/ngx_http_core_module.html#client_body_temp_path
  client_body_temp_path /var/www/peertube/storage-fs/nginx/;

  # Bypass PeerTube for performance reasons. Optional.
  # Should be consistent with client-overrides assets list in /server/controllers/client.ts
  location ~ ^/client/(assets/images/(icons/icon-36x36\.png|icons/icon-48x48\.png|icons/icon-72x72\.png|icons/icon-96x96\.png|icons/icon-144x144\.png|icons/icon-192x192\.png|icons/icon-512x512\.png|logo\.svg|favicon\.png))$ {
    add_header Cache-Control "public, max-age=31536000, immutable"; # Cache 1 year

    root /var/www/peertube;

    try_files /storage-fs/client-overrides/$1 /peertube-latest/client/dist/$1 @api;
  }

  # Bypass PeerTube for performance reasons. Optional.
  location ~ ^/client/(.*\.(js|css|png|svg|woff2|otf|ttf|woff|eot))$ {
    add_header Cache-Control "public, max-age=31536000, immutable"; # Cache 1 year

    alias /var/www/peertube/peertube-latest/client/dist/$1;
  }

  # Bypass PeerTube for performance reasons. Optional.
  location ~ ^/static/(thumbnails|avatars)/ {
    if ($request_method = 'OPTIONS') {
      add_header Access-Control-Allow-Origin  '*';
      add_header Access-Control-Allow-Methods 'GET, OPTIONS';
      add_header Access-Control-Allow-Headers 'Range,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
      add_header Access-Control-Max-Age       1728000; # Preflight request can be cached 20 days
      add_header Content-Type                 'text/plain charset=UTF-8';
      add_header Content-Length               0;
      return 204;
    }

    add_header Access-Control-Allow-Origin    '*';
    add_header Access-Control-Allow-Methods   'GET, OPTIONS';
    add_header Access-Control-Allow-Headers   'Range,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
    add_header Cache-Control                  "public, max-age=7200"; # Cache response 2 hours

    rewrite ^/static/(.*)$ /$1 break;

    try_files $uri @api;
  }

  # Bypass PeerTube for performance reasons. Optional.
  location ~ ^/static/(webseed|redundancy|streaming-playlists)/ {
    limit_rate_after            5M;

    # Clients usually have 4 simultaneous webseed connections, so the real limit is 3MB/s per client
    set $peertube_limit_rate    800k;

    # Increase rate limit in HLS mode, because we don't have multiple simultaneous connections
    if ($request_uri ~ -fragmented.mp4$) {
      set $peertube_limit_rate  5M;
    }

    # Use this line with nginx >= 1.17.0
    #limit_rate $peertube_limit_rate;
    # Or this line if your nginx < 1.17.0
    set $limit_rate $peertube_limit_rate;

    if ($request_method = 'OPTIONS') {
      add_header Access-Control-Allow-Origin  '*';
      add_header Access-Control-Allow-Methods 'GET, OPTIONS';
      add_header Access-Control-Allow-Headers 'Range,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
      add_header Access-Control-Max-Age       1728000; # Preflight request can be cached 20 days
      add_header Content-Type                 'text/plain charset=UTF-8';
      add_header Content-Length               0;
      return 204;
    }

    if ($request_method = 'GET') {
      add_header Access-Control-Allow-Origin  '*';
      add_header Access-Control-Allow-Methods 'GET, OPTIONS';
      add_header Access-Control-Allow-Headers 'Range,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';

      # Don't spam access log file with byte range requests
      access_log off;
    }

    # Enabling the sendfile directive eliminates the step of copying the data into the buffer
    # and enables direct copying data from one file descriptor to another.
    sendfile on;
    sendfile_max_chunk 1M; # prevent one fast connection from entirely occupying the worker process. should be > 800k.
    aio threads;

    # Use this in tandem with fuse-mounting i.e. https://docs.joinpeertube.org/#/admin-remote-storage
    # to serve files directly from a public bucket without proxying.
    # Assumes you have buckets named after the storage subdirectories, i.e. 'videos', 'redundancy', etc.
    #set $cdn <your S3-compatiable bucket public url mounted via fuse>;
    #rewrite ^/static/webseed/(.*)$ $cdn/videos/$1 redirect;
    #rewrite ^/static/(.*)$         $cdn/$1        redirect;
    rewrite ^/static/webseed/(.*)$ /videos/$1 break;
    rewrite ^/static/(.*)$         /$1        break;

    try_files $uri @api;
  }

  location ~* .(txt|html|zip|png|torrent)$ {
    root /var/www/peertube/static;
    error_page 404 = 404;
  }
}

Hello,

I think the line proxy_pass http://192.168.2.27/; should use https instead of http because of listen 443 ssl;

Unfortunately if you use https it doesn’t work at all. The certificate is installed on the proxy server

OK. I better see what you tend to do : proxying https to http backend (what I had in mind was https ⬄ https )

What’s your problem exactly ?

Any message in logs ?

502 Bad Gateway

proxy_pass will not work over HTTPS. I’m sure this is not related to the socket error.

Hi alterak,

Here is my nginx’es setup (1 public reverse-proxy passing requests to an nginx-powered peertube server:

The public proxy (192.168.10.2) :

server {
# Frontal Web HTTPS Liberta Vidéo (Peertube) :
server_name video.liberta.vip;
listen 443 ssl http2;
ssl_certificate /etc/letsencrypt/live/liberta.vip/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/liberta.vip/privkey.pem;

location / {
	proxy_pass https://192.168.10.8/;
            proxy_http_version                 1.1;
            proxy_cache_bypass                 $http_upgrade;
            proxy_set_header Upgrade           $http_upgrade;
            proxy_set_header Connection        "upgrade";
            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-Forwarded-Port  $server_port;
    }
}

The Peertube server (192.168.10.8):

# Liberta Vidéo (Peertube)
server {
    server_name video.liberta.vip;
    listen 443 ssl http2;
    ssl_certificate /etc/letsencrypt/live/liberta.vip/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/liberta.vip/privkey.pem;

client_max_body_size 0;

access_log /var/log/nginx/video_access.log;
error_log /var/log/nginx/video_error.log warn;

    location / {
            proxy_pass https://192.168.10.8/;
            proxy_set_header Host $host;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto https;
            proxy_set_header X-Forwarded-Port 443;
            set_real_ip_from 192.168.10.2;
            real_ip_header X-Forwarded-For;
            real_ip_recursive on;
    }
}

It seems to me that you made a mistake with the configuration … Does your Peertube server proxy itself?

The Peertube server (192.168.10.8):

location / {
        proxy_pass https://192.168.10.8/;

And a certificate is installed on both servers? -) I don’t understand anything at all …

And where have you added all the basic configuration for Peertube (socket, directories, caching)?

Sorry I was mistaken, I will edit my answer, I have a public proxy -> web server nginx -> peertube server setup.

If it doesn’t bother you, show all configurations.

OK, cannot edit posts. Here it is:

Hi alterak,

Here is my nginx’es setup (1 public reverse-proxy (192.168.10.2) passing requests to an nginx-powered web server (192.168.10.5), proxying to another nginx-powered peertube server (192.168.10.8):

The public proxy (192.168.10.2) :

server {
# Frontal Web HTTPS Liberta Vidéo (Peertube) :
server_name video.liberta.vip;
listen 443 ssl http2;
ssl_certificate /etc/letsencrypt/live/liberta.vip/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/liberta.vip/privkey.pem;

location / {
	proxy_pass https://192.168.10.8/;
            proxy_http_version                 1.1;
            proxy_cache_bypass                 $http_upgrade;
            proxy_set_header Upgrade           $http_upgrade;
            proxy_set_header Connection        "upgrade";
            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-Forwarded-Port  $server_port;
    }
}

The general purpose Web server (192.168.10.5):

# Liberta Vidéo (Peertube)
server {
    server_name video.liberta.vip;
    listen 443 ssl http2;
    ssl_certificate /etc/letsencrypt/live/liberta.vip/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/liberta.vip/privkey.pem;

client_max_body_size 0;

access_log /var/log/nginx/video_access.log;
error_log /var/log/nginx/video_error.log warn;

    location / {
            proxy_pass https://192.168.10.8/;
            proxy_set_header Host $host;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto https;
            proxy_set_header X-Forwarded-Port 443;
            set_real_ip_from 192.168.10.2;
            real_ip_header X-Forwarded-For;
            real_ip_recursive on;
    }
}

And finally, the Peertube server:

upstream backend {
	server 127.0.0.1:9000;
}

server {
	listen 80;
	listen [::]:80;
	server_name video.liberta.vip;
	
	access_log /var/log/nginx/video.liberta.vip.access.log;
	error_log /var/log/nginx/video.liberta.vip.error.log;
	
	location / {
		return 301 https://$host$request_uri;
	}
}

server {
	listen 443 ssl http2;
	listen [::]:443 ssl http2;
	server_name video.liberta.vip;
	
	access_log /var/log/nginx/video.liberta.vip.access.log;
	error_log /var/log/nginx/video.liberta.vip.error.log;
	
	ssl_certificate      /etc/letsencrypt/live/liberta.vip/fullchain.pem;
	ssl_certificate_key  /etc/letsencrypt/live/liberta.vip/privkey.pem;
	ssl_protocols TLSv1.2 TLSv1.3;
	ssl_prefer_server_ciphers on;
	ssl_ciphers 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:ECDHE-RSA-AES256-SHA;
	ssl_session_timeout 1d; # defaults to 5m
	ssl_session_cache shared:SSL:10m; # estimated to 40k sessions
	ssl_session_tickets off;
	ssl_stapling on;
	ssl_stapling_verify on;
	
	# Enable HSTS
	# Tells browsers to stick with HTTPS and never visit the insecure HTTP
	# version. Once a browser sees this header, it will only visit the site over
	# HTTPS for the next 2 years: (read more on hstspreload.org)
	add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
	
	location @api {
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
		proxy_set_header Host            $host;
		proxy_set_header X-Real-IP       $remote_addr;
		client_max_body_size  100k; # default is 1M
		
		proxy_connect_timeout 10m;
		proxy_send_timeout    10m;
		proxy_read_timeout    10m;
		send_timeout          10m;
		
		proxy_pass http://backend;
	}
	
	location / {
		proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header Host $host;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

                client_max_body_size 8G;

                proxy_connect_timeout       600;
                # derrière un reverse-proxy, on commente : #proxy_send_timeout          600;
                proxy_read_timeout          600;
                send_timeout                600;
		
		try_files /dev/null @api;
	}
	
	location = /api/v1/users/me/avatar/pick {
		limit_except POST HEAD { deny all; }
		client_max_body_size 2M; # default is 1M
		add_header X-File-Maximum-Size 2M always; # inform backend of the set value in bytes
		
		try_files /dev/null @api;
	}
	
	location = /api/v1/videos/upload {
		limit_except POST HEAD { deny all; }
		
		# This is the maximum upload size, which roughly matches the maximum size of a video file.
		# Note that temporary space is needed equal to the total size of all concurrent uploads.
		# This data gets stored in /var/lib/nginx by default, so you may want to put this directory
		# on a dedicated filesystem.
		client_max_body_size 8G; # default is 1M
		add_header X-File-Maximum-Size 8G always; # inform backend of the set value in bytes
		
		try_files /dev/null @api;
	}
	
	##
	# Websocket
	##
	location @api_websocket {
		proxy_http_version 1.1;
		proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
		proxy_set_header   Host            $host;
		proxy_set_header   X-Real-IP       $remote_addr;
		proxy_set_header   Upgrade         $http_upgrade;
		proxy_set_header   Connection      "upgrade";
		
		proxy_pass http://backend;
	}
	
	location /tracker/socket {
		proxy_http_version 1.1;
		proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
		proxy_set_header   Host            $host;
		proxy_set_header   X-Real-IP       $remote_addr;
		proxy_set_header   Upgrade         $http_upgrade;
		proxy_set_header   Connection      "upgrade";
		
		# Peers send a message to the tracker every 15 minutes
		# Don't close the websocket before then
		proxy_read_timeout 15m; # default is 60s
		
		try_files /dev/null @api_websocket;
	}
	
	location /socket.io {
		proxy_http_version 1.1;
		proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
		proxy_set_header   Host            $host;
		proxy_set_header   X-Real-IP       $remote_addr;
		proxy_set_header   Upgrade         $http_upgrade;
		proxy_set_header   Connection      "upgrade";
		
		try_files /dev/null @api_websocket;
	}
	
	root /var/www/peertube/storage;
	
	# Enable compression for JS/CSS/HTML, for improved client load times.
	# It might be nice to compress JSON/XML as returned by the API, but
	# leaving that out to protect against potential BREACH attack.
	gzip              on;
	gzip_vary         on;
	gzip_types        # text/html is always compressed by HttpGzipModule
	                  text/css
	                  application/javascript
	                  font/truetype
	                  font/opentype
	                  application/vnd.ms-fontobject
	                  image/svg+xml;
	gzip_min_length   1000; # default is 20 bytes
	gzip_buffers      16 8k;
	gzip_comp_level   2; # default is 1
	
	client_body_timeout       30s; # default is 60
	client_header_timeout     10s; # default is 60
	send_timeout              10s; # default is 60
	keepalive_timeout         10s; # default is 75
	resolver_timeout          10s; # default is 30
	reset_timedout_connection on;
	
	tcp_nopush                on; # send headers in one piece
	tcp_nodelay               on; # don't buffer data sent, good for small data bursts in real time
	
	open_file_cache           max=2000 inactive=5m; # default is no cache
	open_file_cache_valid     2m; # default is 60s
	open_file_cache_min_uses  2; # default is 1
	open_file_cache_errors    on;
	
	# If you have a small /var/lib partition, it could be interesting to store temp nginx uploads in a different place
	# See https://nginx.org/en/docs/http/ngx_http_core_module.html#client_body_temp_path
	#client_body_temp_path /var/www/peertube/storage/nginx/;
	
	# Bypass PeerTube for performance reasons. Could be removed
	# Should be consistent with client-overrides assets list in /server/controllers/client.ts
	location ~ ^/client/(assets/images/(icons/icon-36x36\.png|icons/icon-48x48\.png|icons/icon-72x72\.png|icons/icon-96x96\.png|icons/icon-144x144\.png|icons/icon-192x192\.png|icons/icon-512x512\.png|logo\.svg|favicon\.png))$ {
		add_header Cache-Control "public, max-age=31536000, immutable"; # Cache 1 year
		
		root /var/www/peertube;
			
		try_files /storage/client-overrides/$1 /peertube-latest/client/dist/$1 @api;	
	}
	
	# Bypass PeerTube for performance reasons. Optional.
	location ~ ^/client/(.*\.(js|css|png|svg|woff2|otf|ttf|woff|eot))$ {
		add_header Cache-Control "public, max-age=31536000, immutable"; # Cache 1 year
		
		alias /var/www/peertube/peertube-latest/client/dist/$1;
	}
	
	# Bypass PeerTube for performance reasons. Optional.
	location ~ ^/static/(thumbnails|avatars)/ {
		if ($request_method = 'OPTIONS') {
			add_header 'Access-Control-Allow-Origin'  '*';
			add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS';
			add_header 'Access-Control-Allow-Headers' 'Range,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
			add_header 'Access-Control-Max-Age'       1728000; # Preflight request can be cached 20 days
			add_header 'Content-Type'                 'text/plain charset=UTF-8';
			add_header 'Content-Length'               0;
			return 204;
		}
		add_header 'Access-Control-Allow-Origin'    '*';
		add_header 'Access-Control-Allow-Methods'   'GET, OPTIONS';
		add_header 'Access-Control-Allow-Headers'   'Range,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
		add_header Cache-Control                    "public, max-age=7200"; # Cache response 2 hours
		
		rewrite ^/static/(.*)$ /$1 break;
		try_files $uri @api;
	}
	
	# Bypass PeerTube for performance reasons. Optional.
	location ~ ^/static/(webseed|redundancy|streaming-playlists)/ {
		limit_rate_after            5M;
		
		# Clients usually have 4 simultaneous webseed connections, so the real limit is 3MB/s per client
		set $peertube_limit_rate    800k;
		
		# Increase rate limit in HLS mode, because we don't have multiple simultaneous connections
		if ($request_uri ~ -fragmented.mp4$) {
			set $peertube_limit_rate  5M;
		}
		
		# Use this line with nginx >= 1.17.0
		#limit_rate $peertube_limit_rate;
		# Or this line if your nginx < 1.17.0
		set $limit_rate $peertube_limit_rate;
		
		if ($request_method = 'OPTIONS') {
			add_header 'Access-Control-Allow-Origin'  '*';
			add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS';
			add_header 'Access-Control-Allow-Headers' 'Range,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
			add_header 'Access-Control-Max-Age'       1728000; # Preflight request can be cached 20 days
			add_header 'Content-Type'                 'text/plain charset=UTF-8';
			add_header 'Content-Length'               0;
			return 204;
		}
		
		if ($request_method = 'GET') {
			add_header 'Access-Control-Allow-Origin'  '*';
			add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS';
			add_header 'Access-Control-Allow-Headers' 'Range,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
			
			# Don't spam access log file with byte range requests
			access_log off;
		}
		
		# Enabling the sendfile directive eliminates the step of copying the data into the buffer
		# and enables direct copying data from one file descriptor to another.
		sendfile on;
		sendfile_max_chunk 1M; # prevent one fast connection from entirely occupying the worker process. should be > 800k.
		aio threads;
		
		# Use this in tandem with fuse-mounting i.e. https://docs.joinpeertube.org/#/admin-remote-storage
		# to serve files directly from a public bucket without proxying.
		# Assumes you have buckets named after the storage subdirectories, i.e. 'videos', 'redundancy', etc.
		#set $cdn <your S3-compatiable bucket public url mounted via fuse>;
		#rewrite ^/static/webseed/(.*)$ $cdn/videos/$1 redirect;
		#rewrite ^/static/(.*)$         $cdn/$1        redirect;
		
		rewrite ^/static/webseed/(.*)$ /videos/$1 break;
		rewrite ^/static/(.*)$         /$1        break;
	
		 try_files $uri @api;	
	}

}

Pay attention that I had to comment out the proxy_send_timeout line;

I see logic like this:
192.168.10.2 → 192.168.10.8
192.168.10.5 → 192.168.10.8

As I understand it, 192.168.10.8 is a Peertube server.
But I can’t figure out why you have both the main reverse proxy and the second general purpose server referring to the Peertube server?

Because you are absolutely right!

Just forget about the web server 192.168.10.5; it does not pass requests to peertube anymore but it used to. So some code just stayed in the web server’s nginx config (harmless). I’ve just kinda forgot about my latest changes as I had to struggle a bit with the chained-proxies-problematic

So logic is simply as follows : 192.168.10.2 (public proxy) -> 192.168.10.8 (peertube).
Sorry about the misleadings!

Sorry, but your configuration probably only works on the local network-) but in general your configuration does not work …

The official documentation says that each upstream server must be configured for SSL, and only HTTP can transmit it.

I see your point.

I actually use NAT and prerouting rules with iptables in order to translate public requests towards the proxy on the LAN by bridging virtual network interfaces.

Hello
I’m not sure I have understood the problem, but if I’m not mistaken, everything rise from the fact that the proxy connects to port 80 of the peertube server and in nginx config there is a rule that redirects all requests to port 443 where it is sent ssl certificates to the proxy in response … and of course the proxy goes crazy.

location / { return 301 https://$host$request_uri; }

I am using a similar configuration with Trafik 1.7, and to make it work, I had to modify the section of port 80 … and fundamental: delete the default site:

rm /etc/nginx/sites-enabled/default

here my nginx config of port 80 section. Please edit the server_name

root@peertube:~/PeerTube# cat /etc/nginx/sites-enabled/peertube 
# Minimum Nginx version required:  1.13.0 (released Apr 25, 2017)
# Please check your Nginx installation features the following modules via 'nginx -V':
# STANDARD HTTP MODULES: Core, Proxy, Rewrite.
# OPTIONAL HTTP MODULES: Gzip, Headers, HTTP/2, Log, Real IP, SSL, Thread Pool, Upstream.
# THIRD PARTY MODULES:   None.

server {
  listen 80;
  listen [::]:80;
  server_name [videos.tormentasolar.win];

  location /.well-known/acme-challenge/ {
    default_type "text/plain";
    root /var/www/certbot;
  }

#  location / { return 301 https://$host$request_uri; }

  ##
  # Application
  ##

  location @api {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host            $host;
    proxy_set_header X-Real-IP       $remote_addr;

    client_max_body_size  100k; # default is 1M

    proxy_connect_timeout 10m;
    proxy_send_timeout    10m;
    proxy_read_timeout    10m;
    send_timeout          10m;

    proxy_pass http://backend;
  }

  location / {
    try_files /dev/null @api;
  }

  location = /api/v1/users/me/avatar/pick {
    limit_except POST HEAD { deny all; }

    client_max_body_size                      2M; # default is 1M
    add_header            X-File-Maximum-Size 2M always; # inform backend of the set value in bytes

    try_files /dev/null @api;
  }

  location = /api/v1/videos/upload {
    limit_except POST HEAD { deny all; }

    # This is the maximum upload size, which roughly matches the maximum size of a video file.
    # Note that temporary space is needed equal to the total size of all concurrent uploads.
    # This data gets stored in /var/lib/nginx by default, so you may want to put this directory
    # on a dedicated filesystem.
    client_max_body_size                      8G; # default is 1M
    add_header            X-File-Maximum-Size 8G always; # inform backend of the set value in bytes

    try_files /dev/null @api;
  }

  ##
  # Websocket
  ##

  location @api_websocket {
    proxy_http_version 1.1;
    proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header   Host            $host;
    proxy_set_header   X-Real-IP       $remote_addr;
    proxy_set_header   Upgrade         $http_upgrade;
    proxy_set_header   Connection      "upgrade";

    proxy_pass http://backend;
  }

  location /socket.io {
    try_files /dev/null @api_websocket;
  }

  location /tracker/socket {
    # Peers send a message to the tracker every 15 minutes
    # Don't close the websocket before then
    proxy_read_timeout 15m; # default is 60s

    try_files /dev/null @api_websocket;
  }

  ##
  # Performance optimizations
  # For extra performance please refer to https://github.com/denji/nginx-tuning
  ##

  #root /var/www/peertube/storage;
  root /mnt/Disk/SRV/www/peertube/storage;
  # Enable compression for JS/CSS/HTML, for improved client load times.
  # It might be nice to compress JSON/XML as returned by the API, but
  # leaving that out to protect against potential BREACH attack.
  gzip              on;
  gzip_vary         on;
  gzip_types        # text/html is always compressed by HttpGzipModule
                    text/css
                    application/javascript
                    font/truetype
                    font/opentype
                    application/vnd.ms-fontobject
                    image/svg+xml;
  gzip_min_length   1000; # default is 20 bytes
  gzip_buffers      16 8k;
  gzip_comp_level   2; # default is 1

  client_body_timeout       30s; # default is 60
  client_header_timeout     10s; # default is 60
  send_timeout              10s; # default is 60
  keepalive_timeout         10s; # default is 75
  resolver_timeout          10s; # default is 30
  reset_timedout_connection on;

  tcp_nopush                on; # send headers in one piece
  tcp_nodelay               on; # don't buffer data sent, good for small data bursts in real time

  # If you have a small /var/lib partition, it could be interesting to store temp nginx uploads in a different place
  # See https://nginx.org/en/docs/http/ngx_http_core_module.html#client_body_temp_path
  #client_body_temp_path /var/www/peertube/storage/nginx/;

  # Bypass PeerTube for performance reasons. Optional.
  # Should be consistent with client-overrides assets list in /server/controllers/client.ts
  location ~ ^/client/(assets/images/(icons/icon-36x36\.png|icons/icon-48x48\.png|icons/icon-72x72\.png|icons/icon-96x96\.png|icons/icon-144x144\.png|icons/icon-192x192\.png|icons/icon-512x512\.png|logo\.svg|favicon\.png))$ {
    add_header Cache-Control "public, max-age=31536000, immutable"; # Cache 1 year

    #root /var/www/peertube;
    root /mnt/Disk/SRV/www/peertube;
    try_files /storage/client-overrides/$1 /peertube-latest/client/dist/$1 @api;
  }

  # Bypass PeerTube for performance reasons. Optional.
  location ~ ^/client/(.*\.(js|css|png|svg|woff2|otf|ttf|woff|eot))$ {
    add_header Cache-Control "public, max-age=31536000, immutable"; # Cache 1 year

#    alias /var/www/peertube/peertube-latest/client/dist/$1;
    alias /var/www/peertube/peertube-latest/client/dist/$1;
#          /var/www/peertube/peertube-latest/client/dist/
  }

  # Bypass PeerTube for performance reasons. Optional.
  location ~ ^/static/(thumbnails|avatars)/ {
    if ($request_method = 'OPTIONS') {
      add_header Access-Control-Allow-Origin  '*';
      add_header Access-Control-Allow-Methods 'GET, OPTIONS';
      add_header Access-Control-Allow-Headers 'Range,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
      add_header Access-Control-Max-Age       1728000; # Preflight request can be cached 20 days
      add_header Content-Type                 'text/plain charset=UTF-8';
      add_header Content-Length               0;
      return 204;
    }

    add_header Access-Control-Allow-Origin    '*';
    add_header Access-Control-Allow-Methods   'GET, OPTIONS';
    add_header Access-Control-Allow-Headers   'Range,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
    add_header Cache-Control                  "public, max-age=7200"; # Cache response 2 hours

    rewrite ^/static/(.*)$ /$1 break;

    try_files $uri @api;
  }

  # Bypass PeerTube for performance reasons. Optional.
  location ~ ^/static/(webseed|redundancy|streaming-playlists)/ {
    limit_rate_after            5M;

    # Clients usually have 4 simultaneous webseed connections, so the real limit is 3MB/s per client
    set $peertube_limit_rate    800k;

    # Increase rate limit in HLS mode, because we don't have multiple simultaneous connections
    if ($request_uri ~ -fragmented.mp4$) {
      set $peertube_limit_rate  5M;
    }

    # Use this line with nginx >= 1.17.0
    #limit_rate $peertube_limit_rate;
    # Or this line if your nginx < 1.17.0
    set $limit_rate $peertube_limit_rate;

    if ($request_method = 'OPTIONS') {
      add_header Access-Control-Allow-Origin  '*';
      add_header Access-Control-Allow-Methods 'GET, OPTIONS';
      add_header Access-Control-Allow-Headers 'Range,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
      add_header Access-Control-Max-Age       1728000; # Preflight request can be cached 20 days
      add_header Content-Type                 'text/plain charset=UTF-8';
      add_header Content-Length               0;
      return 204;
    }

    if ($request_method = 'GET') {
      add_header Access-Control-Allow-Origin  '*';
      add_header Access-Control-Allow-Methods 'GET, OPTIONS';
      add_header Access-Control-Allow-Headers 'Range,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';

      # Don't spam access log file with byte range requests
      access_log off;
    }

    # Enabling the sendfile directive eliminates the step of copying the data into the buffer
    # and enables direct copying data from one file descriptor to another.
    sendfile on;
    sendfile_max_chunk 1M; # prevent one fast connection from entirely occupying the worker process. should be > 800k.
    aio threads;

    # Use this in tandem with fuse-mounting i.e. https://docs.joinpeertube.org/#/admin-remote-storage
    # to serve files directly from a public bucket without proxying.
    # Assumes you have buckets named after the storage subdirectories, i.e. 'videos', 'redundancy', etc.
    #set $cdn <your S3-compatiable bucket public url mounted via fuse>;
    #rewrite ^/static/webseed/(.*)$ $cdn/videos/$1 redirect;
    #rewrite ^/static/(.*)$         $cdn/$1        redirect;
    rewrite ^/static/webseed/(.*)$ /videos/$1 break;
    rewrite ^/static/(.*)$         /$1        break;

    try_files $uri @api;
  }
}

##############################################################

below the 443 port section from your original config file

I’ve done it … PeerTube works well, but the problem with the tracker-) I already talked about this.

Here is the reverse proxy config (192.168.2.20)

Config Proxy
server {
  server_name www.video.ustim.ru video.ustim.ru;

  error_log /var/log/nginx/video.ustim.ru_error.log crit;

location / {
        proxy_pass http://192.168.2.28/;
            proxy_http_version                 1.1;
            proxy_cache_bypass                 $http_upgrade;
            proxy_set_header Upgrade           $http_upgrade;
            proxy_set_header Connection        "upgrade";
            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-Forwarded-Port  $server_port;
    }

  location ^~ /.well-known/acme-challenge/ {
   default_type "text/plain";
   root /var/www/letsencrypt;
  }
  location = /.well-known/acme-challenge/ {
   return 404;
  }

    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/video.ustim.ru/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/video.ustim.ru/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

}
server {
    if ($host = www.video.ustim.ru) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    if ($host = video.ustim.ru) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


  server_name www.video.ustim.ru video.ustim.ru;
    listen 80;
    return 404; # managed by Certbot

}

This is the PeerTube server config (192.168.2.28)

Config PeerTube
upstream backend {
  server 127.0.0.1:9000;
}

server {
  listen 80;
  listen [::]:80;
  server_name video.ustim.ru;

  access_log /var/log/nginx/peertube.access.log; # reduce I/0 with buffer=10m flush=5m
  error_log  /var/log/nginx/peertube.error.log;

  location ^~ '/.well-known/acme-challenge' {
    default_type "text/plain";
    root /var/www/certbot;
  }

  #add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";

  location @api {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host            $host;
    proxy_set_header X-Real-IP       $remote_addr;

    client_max_body_size  100k; # default is 1M

    proxy_connect_timeout 10m;
    proxy_send_timeout    10m;
    proxy_read_timeout    10m;
    send_timeout          10m;

    proxy_pass http://backend;
  }

  location / {
    try_files /dev/null @api;
  }

  location ~ ^/api/v1/(videos|video-playlists|users/me/avatar/pick)$ {
    client_max_body_size                      3M; # default is 1M
    add_header            X-File-Maximum-Size 2M always; # inform backend of the set value in bytes before mime-encoding (x * 1.4 >= client_max_body_size)

    try_files /dev/null @api;
  }

  location = /api/v1/videos/upload {
    limit_except POST HEAD { deny all; }

    # This is the maximum upload size, which roughly matches the maximum size of a video file.
    # Note that temporary space is needed equal to the total size of all concurrent uploads.
    # This data gets stored in /var/lib/nginx by default, so you may want to put this directory
    # on a dedicated filesystem.
    client_max_body_size                      12G; # default is 1M
    add_header            X-File-Maximum-Size 8G always; # inform backend of the set value in bytes before mime-encoding (x * 1.4 >= client_max_body_size)

    try_files /dev/null @api;
  }

  ##
  # Websocket
  ##

  location @api_websocket {
    proxy_http_version 1.1;
    proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header   Host            $host;
    proxy_set_header   X-Real-IP       $remote_addr;
    proxy_set_header   Upgrade         $http_upgrade;
    proxy_set_header   Connection      "upgrade";

    proxy_pass http://backend;
  }

  location /socket.io {
    try_files /dev/null @api_websocket;
  }

  location /tracker/socket {
    # Peers send a message to the tracker every 15 minutes
    # Don't close the websocket before then
    proxy_read_timeout 15m; # default is 60s

    try_files /dev/null @api_websocket;
  }

  ##
  # Performance optimizations
  # For extra performance please refer to https://github.com/denji/nginx-tuning
  ##

  root /var/www/peertube/storage;

  # Enable compression for JS/CSS/HTML, for improved client load times.
  # It might be nice to compress JSON/XML as returned by the API, but
  # leaving that out to protect against potential BREACH attack.
  gzip              on;
  gzip_vary         on;
  gzip_types        # text/html is always compressed by HttpGzipModule
                    text/css
                    application/javascript
                    font/truetype
                    font/opentype
                    application/vnd.ms-fontobject
                    image/svg+xml;
  gzip_min_length   1000; # default is 20 bytes
  gzip_buffers      16 8k;
  gzip_comp_level   2; # default is 1

  client_body_timeout       30s; # default is 60
  client_header_timeout     10s; # default is 60
  send_timeout              10s; # default is 60
  keepalive_timeout         10s; # default is 75
  resolver_timeout          10s; # default is 30
  reset_timedout_connection on;
  proxy_ignore_client_abort on;

  tcp_nopush                on; # send headers in one piece
  tcp_nodelay               on; # don't buffer data sent, good for small data bursts in real time

  # If you have a small /var/lib partition, it could be interesting to store temp nginx uploads in a different place
  # See https://nginx.org/en/docs/http/ngx_http_core_module.html#client_body_temp_path
  #client_body_temp_path /var/www/peertube/storage/nginx/;

  # Bypass PeerTube for performance reasons. Optional.
  # Should be consistent with client-overrides assets list in /server/controllers/client.ts
  location ~ ^/client/(assets/images/(icons/icon-36x36\.png|icons/icon-48x48\.png|icons/icon-72x72\.png|icons/icon-96x96\.png|icons/icon-144x144\.png|icons/icon-192x192\.png|icons/icon-512x512\.png|logo\.svg|favicon\.png))$ {
    add_header Cache-Control "public, max-age=31536000, immutable"; # Cache 1 year

    root /var/www/peertube;

    try_files /storage/client-overrides/$1 /peertube-latest/client/dist/$1 @api;
  }

  # Bypass PeerTube for performance reasons. Optional.
  location ~ ^/client/(.*\.(js|css|png|svg|woff2|otf|ttf|woff|eot))$ {
    add_header Cache-Control "public, max-age=31536000, immutable"; # Cache 1 year

    alias /var/www/peertube/peertube-latest/client/dist/$1;
  }

  # Bypass PeerTube for performance reasons. Optional.
  location ~ ^/static/(thumbnails|avatars)/ {
    if ($request_method = 'OPTIONS') {
      add_header Access-Control-Allow-Origin  '*';
      add_header Access-Control-Allow-Methods 'GET, OPTIONS';
      add_header Access-Control-Allow-Headers 'Range,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
      add_header Access-Control-Max-Age       1728000; # Preflight request can be cached 20 days
      add_header Content-Type                 'text/plain charset=UTF-8';
      add_header Content-Length               0;
      return 204;
    }

    add_header Access-Control-Allow-Origin    '*';
    add_header Access-Control-Allow-Methods   'GET, OPTIONS';
    add_header Access-Control-Allow-Headers   'Range,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
    add_header Cache-Control                  "public, max-age=7200"; # Cache response 2 hours

    rewrite ^/static/(.*)$ /$1 break;

    try_files $uri @api;
  }

  # Bypass PeerTube for performance reasons. Optional.
  location ~ ^/static/(webseed|redundancy|streaming-playlists)/ {
    limit_rate_after            5M;

    # Clients usually have 4 simultaneous webseed connections, so the real limit is 3MB/s per client
    set $peertube_limit_rate    800k;

    # Increase rate limit in HLS mode, because we don't have multiple simultaneous connections
    if ($request_uri ~ -fragmented.mp4$) {
      set $peertube_limit_rate  5M;
    }

    # Use this line with nginx >= 1.17.0
    #limit_rate $peertube_limit_rate;
    # Or this line if your nginx < 1.17.0
    set $limit_rate $peertube_limit_rate;

    if ($request_method = 'OPTIONS') {
      add_header Access-Control-Allow-Origin  '*';
      add_header Access-Control-Allow-Methods 'GET, OPTIONS';
      add_header Access-Control-Allow-Headers 'Range,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
      add_header Access-Control-Max-Age       1728000; # Preflight request can be cached 20 days
      add_header Content-Type                 'text/plain charset=UTF-8';
      add_header Content-Length               0;
      return 204;
    }

    if ($request_method = 'GET') {
      add_header Access-Control-Allow-Origin  '*';
      add_header Access-Control-Allow-Methods 'GET, OPTIONS';
      add_header Access-Control-Allow-Headers 'Range,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';

      # Don't spam access log file with byte range requests
      access_log off;
    }

    # Enabling the sendfile directive eliminates the step of copying the data into the buffer
    # and enables direct copying data from one file descriptor to another.
    sendfile on;
    sendfile_max_chunk 1M; # prevent one fast connection from entirely occupying the worker process. should be > 800k.
    aio threads;

    # Use this in tandem with fuse-mounting i.e. https://docs.joinpeertube.org/#/admin-remote-storage
    # to serve files directly from a public bucket without proxying.
    # Assumes you have buckets named after the storage subdirectories, i.e. 'videos', 'redundancy', etc.
    #set $cdn <your S3-compatiable bucket public url mounted via fuse>;
    #rewrite ^/static/webseed/(.*)$ $cdn/videos/$1 redirect;
    #rewrite ^/static/(.*)$         $cdn/$1        redirect;
    rewrite ^/static/webseed/(.*)$ /videos/$1 break;
    rewrite ^/static/(.*)$         /$1        break;

    try_files $uri @api;
  }
}

With such settings, everything works, but when watching the video, the tracker does not work and does not distribute.
I made a test server on purpose, you can check
https://video.ustim.ru/videos/watch/652aa647-26db-4178-8628-deead7c1968f