immich-app / immich

High performance self-hosted photo and video management solution.
https://immich.app
GNU Affero General Public License v3.0
44.66k stars 2.17k forks source link

IOS client cannot play video when connect through https domain #10777

Closed MFYDev closed 2 months ago

MFYDev commented 2 months ago

The bug

I reported this issue long time ago and also reported this in the discord help, however it has been already a long time and many users met the same issue as mine, no solution or fix are provided.

The issue is when I connect to my immich which is running behind Nginx Reverse Proxy, I use let's encrypt SSL, the video will become blank. It cannot be played. However if I use the ip to connect to it, which means bypass nginx reverse proxy, the video can play. This issue is only happening on IOS client.

My full nginx config is attached in this reply below

I am a software engineer and I am fully aware what I am talking. There is no issue on Android client, or web, no error at all. And this has significantly influence my using experience now

The OS that Immich Server is running on

Ubuntu 22.04 LTS

Version of Immich Server

v1.107.1

Version of Immich Mobile App

v1.107.1

Platform with the issue

Your docker-compose.yml content

version: "3.8"

name: immich

services:
  immich-server:
    container_name: immich_server
    image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}
    ports:
      - 2283:3001
    volumes:
      - ${UPLOAD_LOCATION}:/usr/src/app/upload
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro
    env_file:
      - .env
    depends_on:
      - redis
      - database
    restart: always

  immich-machine-learning:
    container_name: immich_machine_learning
    image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}
    volumes:
      - model-cache:/cache
    env_file:
      - .env
    restart: always

  redis:
    container_name: immich_redis
    image: redis:6.2-alpine
    restart: always

  database:
    container_name: immich_postgres
    image: tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:90724186f0a3517cf6914295b5ab410db9ce23190a2d9d0b9dd6463e3fa298f0
    env_file:
      - .env
    environment:
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_USER: ${DB_USERNAME}
      POSTGRES_DB: ${DB_DATABASE_NAME}
    volumes:
      - pgdata:/var/lib/postgresql/data
    restart: always

  backup:
    container_name: immich_db_dumper
    image: prodrigestivill/postgres-backup-local:14
    env_file:
      - .env
    environment:
      POSTGRES_HOST: database
      POSTGRES_CLUSTER: 'TRUE'
      POSTGRES_USER: ${DB_USERNAME}
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_DB: ${DB_DATABASE_NAME}
      SCHEDULE: "0 */4 * * *"
      POSTGRES_EXTRA_OPTS: '--clean --if-exists'
      BACKUP_DIR: /db_dumps
      BACKUP_KEEP_DAYS: 30
    volumes:
      - ./db_dumps:/db_dumps
    depends_on:
      - database
    restart: always

volumes:
  pgdata:
  model-cache:

Your .env content

# You can find documentation for all the supported env variables at https://immich.app/docs/install/environment-variables

# The location where your uploaded files are stored
UPLOAD_LOCATION=**redacted**

# The Immich version to use. You can pin this to a specific version like "v1.71.0"
IMMICH_VERSION=release

# Connection secrets for postgres and typesense. You should change these to random passwords
TYPESENSE_API_KEY=**redacted**
DB_PASSWORD=**redacted**

# The values below this line do not need to be changed
###################################################################################
DB_HOSTNAME=immich_postgres
DB_USERNAME=postgres
DB_DATABASE_NAME=**redacted**

REDIS_HOSTNAME=**redacted**

PUBLIC_LOGIN_PAGE_MESSAGE=**redacted**

Reproduction steps

1. Use IOS client connect to the immich by using https domain behind nginx
2. play a video
3. fully blank and grey out
...

Relevant log output

Instance of 'FlutterErrorDetails'
Exception: PlatformException(VideoError, Failed to load video: Operation Stopped, null, null)
Library: widgets library
Context: Instance of 'ErrorDescription'

#1      AsyncError.value (package:riverpod/src/common.dart:494:0)
#2      VideoViewerPage.build (package:immich_mobile/pages/common/video_viewer.page.dart:36:0)
#3      _ConsumerState.build (package:flutter_riverpod/src/consumer.dart:476:0)
#4      StatefulElement.build (package:flutter/src/widgets/framework.dart:5599:0)
#5      ConsumerStatefulElement.build (package:flutter_riverpod/src/consumer.dart:539:0)
#6      HookElement.build (package:flutter_hooks/src/framework.dart:438:0)
#7      ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5487:0)
#8      StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5650:0)
#9      Element.rebuild (package:flutter/src/widgets/framework.dart:5203:0)
#10     StatefulElement.update (package:flutter/src/widgets/framework.dart:5673:0)
#11     HookElement.update (package:flutter_hooks/src/framework.dart:400:0)
#12     Element.updateChild (package:flutter/src/widgets/framework.dart:3827:0)
#13     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5512:0)
#14     Element.rebuild (package:flutter/src/widgets/framework.dart:5203:0)
#15     StatelessElement.update (package:flutter/src/widgets/framework.dart:5563:0)
#16     Element.updateChild (package:flutter/src/widgets/framework.dart:3827:0)
#17     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5512:0)
#18     Element.rebuild (package:flutter/src/widgets/framework.dart:5203:0)
#19     ProxyElement.update (package:flutter/src/widgets/framework.dart:5816:0)
#20     Element.updateChild (package:flutter/src/widgets/framework.dart:3827:0)
#21     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5512:0)
#22     StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5650:0)
#23     Element.rebuild (package:flutter/src/widgets/framework.dart:5203:0)
#24     StatefulElement.update (package:flutter/src/widgets/framework.dart:5673:0)
#25     Element.updateChild (package:flutter/src/widgets/framework.dart:3827:0)
#26     SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6776:0)
#27     Element.updateChild (package:flutter/src/widgets/framework.dart:3827:0)
#28     SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6776:0)
#29     Element.updateChild (package:flutter/src/widgets/framework.dart:3827:0)
#30     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5512:0)
#31     StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5650:0)
#32     Element.rebuild (package:flutter/src/widgets/framework.dart:5203:0)
#33     StatefulElement.update (package:flutter/src/widgets/framework.dart:5673:0)
#34     Element.updateChild (package:flutter/src/widgets/framework.dart:3827:0)
#35     SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6776:0)
#36     Element.updateChild (package:flutter/src/widgets/framework.dart:3827:0)
#37     SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6776:0)
#38     Element.updateChild (package:flutter/src/widgets/framework.dart:3827:0)
#39     SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6776:0)
#40     Element.updateChild (package:flutter/src/widgets/framework.dart:3827:0)
#41     SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6776:0)
#42     Element.updateChild (package:flutter/src/widgets/framework.dart:3827:0)
#43     SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6776:0)
#44     Element.updateChild (package:flutter/src/widgets/framework.dart:3827:0)
#45     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5512:0)
#46     Element.rebuild (package:flutter/src/widgets/framework.dart:5203:0)
#47     StatelessElement.update (package:flutter/src/widgets/framework.dart:5563:0)
#48     Element.updateChild (package:flutter/src/widgets/framework.dart:3827:0)
#49     SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6776:0)
#50     Element.updateChild (package:flutter/src/widgets/framework.dart:3827:0)
#51     SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6776:0)
#52     Element.updateChild (package:flutter/src/widgets/framework.dart:3827:0)
#53     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5512:0)
#54     StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5650:0)
#55     Element.rebuild (package:flutter/src/widgets/framework.dart:5203:0)
#56     StatefulElement.update (package:flutter/src/widgets/framework.dart:5673:0)
#57     Element.updateChild (package:flutter/src/widgets/framework.dart:3827:0)
#58     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5512:0)
#59     Element.rebuild (package:flutter/src/widgets/framework.dart:5203:0)
#60     StatelessElement.update (package:flutter/src/widgets/framework.dart:5563:0)
#61     Element.updateChild (package:flutter/src/widgets/framework.dart:3827:0)
#62     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5512:0)
#63     StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5650:0)
#64     Element.rebuild (package:flutter/src/widgets/framework.dart:5203:0)
#65     StatefulElement.update (package:flutter/src/widgets/framework.dart:5673:0)
#66     Element.updateChild (package:flutter/src/widgets/framework.dart:3827:0)
#67     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5512:0)
#68     StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5650:0)
#69     Element.rebuild (package:flutter/src/widgets/framework.dart:5203:0)
#70     StatefulElement.update (package:flutter/src/widgets/framework.dart:5673:0)
#71     Element.updateChild (package:flutter/src/widgets/framework.dart:3827:0)
#72     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5512:0)
#73     Element.rebuild (package:flutter/src/widgets/framework.dart:5203:0)
#74     StatelessElement.update (package:flutter/src/widgets/framework.dart:5563:0)
#75     Element.updateChild (package:flutter/src/widgets/framework.dart:3827:0)
#76     _LayoutBuilderElement._layout.layoutCallback (package:flutter/src/widgets/layout_builder.dart:155:0)
#77     BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2845:0)
#78     _LayoutBuilderElement._layout (package:flutter/src/widgets/layout_builder.dart:173:0)
#79     RenderObject.invokeLayoutCallback.<anonymous closure> (package:flutter/src/rendering/object.dart:2688:0)
#80     PipelineOwner._enableMutationsToDirtySubtrees (package:flutter/src/rendering/object.dart:1097:0)
#81     RenderObject.invokeLayoutCallback (package:flutter/src/rendering/object.dart:2688:0)
#82     RenderConstrainedLayoutBuilder.rebuildIfNecessary (package:flutter/src/widgets/layout_builder.dart:248:0)
#83     _RenderLayoutBuilder.performLayout (package:flutter/src/widgets/layout_builder.dart:331:0)
#84     RenderObject._layoutWithoutResize (package:flutter/src/rendering/object.dart:2416:0)
#85     PipelineOwner.flushLayout (package:flutter/src/rendering/object.dart:1051:0)
#86     PipelineOwner.flushLayout (package:flutter/src/rendering/object.dart:1064:0)
#87     RendererBinding.drawFrame (package:flutter/src/rendering/binding.dart:577:0)
#88     WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:1138:0)
#89     RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:443:0)
#90     SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1392:0)
#91     SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1313:0)
#92     SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:1171:0)
#93     _invoke (dart:ui/hooks.dart:312:0)
#94     PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:419:0)
#95     _drawFrame (dart:ui/hooks.dart:283:0)

Additional information

No response

mmomjian commented 2 months ago

Are you using a self-signed SSL cert?

MFYDev commented 2 months ago

Hi @mmomjian thank you for the fast response. I am using SSL Cert which I got from let's encrypt, so it is not a self-signed one

alextran1502 commented 2 months ago

Do you mind posting your reverse proxy config?

MFYDev commented 2 months ago

Hi @alextran1502 Thank you for the response, here is my reverse proxy config


#PROXY-START/

location ^~ /
{
    client_max_body_size 50000M;
    proxy_pass http://127.0.0.1:2283;
    proxy_set_header Host $http_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_cache_convert_head off;

    # enable websockets: http://nginx.org/en/docs/http/websocket.html
    proxy_http_version 1.1;
    proxy_set_header   Upgrade    $http_upgrade;
    proxy_set_header   Connection "upgrade";
    proxy_redirect     off;

    # set timeout
    proxy_connect_timeout 300;
    proxy_send_timeout 300;
    proxy_read_timeout 300;
    send_timeout 300;

}

#PROXY-END/
bo0tzz commented 2 months ago

Can you include the SSL configuration please?

alextran1502 commented 2 months ago

Can you try

client_max_body_size 0;

Restart your proxy and try again. Also, have you changed the default encoding settings? What are your video encoding options?

mmomjian commented 2 months ago

What is this setting for? proxy_cache_convert_head off;

mmomjian commented 2 months ago

Can you test using Safari on the same iOS device?

MFYDev commented 2 months ago

Hi @alextran1502 @mmomjian

Thanks again for the fast reply. I truly appreciate it.

I fistly set client_max_body_size 0;, still the same error in the IOS client, and I have never changed or even touched the settings related to video encoding.

And this below answer is from ChatGPT:

proxy_cache_convert_head off;: When this directive is set to off, responses to HEAD requests are cached separately from GET requests. This means that the HEAD and GET responses are stored independently in the cache, which can lead to slightly increased storage requirements but ensures that the cache contains accurate representations of both types of requests.

I tested on both Edge and Safari on my iPhone 14 Pro Max with the latest IOS 17, the browser can play the video without issues.

mmomjian commented 2 months ago

Yes, I looked up the meaning of the header, but I don't understand why you have set it that way. Are you caching replies? If so (I don't see this in your config), I would disable caching for testing. If you're not caching, you don't need the proxy_cache_convert_head and you should remove it for now since it's not part of our recommended config.

Also post the rest of your nginx config, specifically the listen, ssl_certificate, etc

MFYDev commented 2 months ago

Yes, I looked up the meaning of the header, but I don't understand why you have set it that way. Are you caching replies? If so (I don't see this in your config), I would disable caching for testing. If you're not caching, you don't need the proxy_cache_convert_head and you should remove it for now since it's not part of our recommended config.

@mmomjian soemtimes my nginx will cache a bit of the stuff from the website, so I added that, and sure I will remove that now.

Also post the rest of your nginx config, specifically the listen, ssl_certificate, etc

Here below is my full Nginx conf for the website

server
{
    listen 80;
        listen 443 ssl http2;
    server_name myimmichdomain;
    index index.php index.html index.htm default.php default.htm default.html;
    root /www/wwwroot/myimmichdomain;
    #CERT-APPLY-CHECK--START
    include /www/server/panel/vhost/nginx/well-known/myimmichdomain.conf;
    #CERT-APPLY-CHECK--END

    #error_page 404/404.html;
    #HTTP_TO_HTTPS_START
    if ($server_port !~ 443){
        rewrite ^(/.*)$ https://$host$1 permanent;
    }
    #HTTP_TO_HTTPS_END
    ssl_certificate    /www/server/panel/vhost/cert/myimmichdomain/fullchain.pem;
    ssl_certificate_key    /www/server/panel/vhost/cert/myimmichdomain/privkey.pem;
    ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
    ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
    add_header Strict-Transport-Security "max-age=31536000";
    error_page 497  https://$host$request_uri;

    #SSL-END

    #ERROR-PAGE-START
    #error_page 404 /404.html;
    #error_page 502 /502.html;
    #ERROR-PAGE-END

    #PHP-INFO-START

    #reverse proxy
    include /www/server/panel/vhost/nginx/proxy/myimmichdomain/*.conf;

    #REWRITE-START 
    include /www/server/panel/vhost/rewrite/myimmichdomain.conf;
    #REWRITE-END

    location ~ \.well-known{
        allow all;
    }

    if ( $uri ~ "^/\.well-known/.*\.(php|jsp|py|js|css|lua|ts|go|zip|tar\.gz|rar|7z|sql|bak)$" ) {
        return 403;
    }

    access_log  /www/wwwlogs/myimmichdomain.log;
    error_log  /www/wwwlogs/myimmichdomain.error.log;
}

#PROXY-START/

location ^~ /
{
    client_max_body_size 0;
    proxy_pass http://127.0.0.1:2283;
    proxy_set_header Host $http_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;

    # enable websockets: http://nginx.org/en/docs/http/websocket.html
    proxy_http_version 1.1;
    proxy_set_header   Upgrade    $http_upgrade;
    proxy_set_header   Connection "upgrade";
    proxy_redirect     off;

    # set timeout
    proxy_connect_timeout 300;
    proxy_send_timeout 300;
    proxy_read_timeout 300;
    send_timeout 300;

}

#PROXY-END/
MFYDev commented 2 months ago

Can you include the SSL configuration please?

Hi @bo0tzz thank you for the response, I just uploaded all my nginx config here

MFYDev commented 2 months ago

I just tested again on all my devices, I have all the platforms, Windows and Android and Linux they are all working fine. IOS Safari and Edge browser can play the video, but if you pause the video, click back and re-click the video again, it will stuck there. IOS client cannot play video at all with that error log.

MFYDev commented 2 months ago

Another weird issue is that if I add any description to any photos or videos, IOS client will not sync the description. But this is a bit unrelated to this important issue tho.

mmomjian commented 2 months ago

(Since this work by direct IP, should probably be moved to a discussion IMO)

MFYDev commented 2 months ago

IMHO I do not think this should be moved to a discussion as we can see from all the platforms I tested; this issue is only happening on IOS client with that weird error log. All other platforms are all normal without issues. Which makes me doubt this is actually a bug.

I searched a long time in the immich issue list and immich discord helpdesk, and unfortunately, I got nothing. The weird part is if this is happening to me, it should be a very common issue, surprisingly very few people posted here.

MFYDev commented 2 months ago

Some similar issues in the discussion:

IOS is always weird, I can see the error log is a bit different now tho. I remember long time ago I replied in a discord helpdesk post as well, however due to the discord auto-hide after inactivity for a period of time, I cannot revisit that one again.

MFYDev commented 2 months ago

Just checked the Nginx access log, IOS client's api request got a 200 response, which means it's fully correct.

172.70.114.242 - - [02/Jul/2024:16:27:55 -0400] "GET /api/assets/21ba507a-cf89-4fa0-b4e6-df50987194c8/video/playback HTTP/2.0" 200 8388608 "-" "AppleCoreMedia/1.0.0.21F90 (iPhone; U; CPU OS 17_5_1 like Mac OS X; en_us)"
172.70.230.4 - - [02/Jul/2024:16:42:19 -0400] "POST /api/auth/validateToken HTTP/2.0" 200 19 "-" "Dart/3.4 (dart:io)"

I also set up a API Key and tested with using Postman, the response is also correct. I think till this point I can confirm it is definitely IOS client issue.

image

raisinbear commented 2 months ago

That's interesting. I've been running behind nginx with Lets Encrypt since the very beginning. At the time, I just copied the relevant location parts from the by now obsolete reverse proxy container config, cf. below. Works flawlessly with (h264) videos in the iOS app. I can't see what else is going on in some of the included config files in your server block, but from what you posted, there is no drastic difference.

    location / {

      # Compression
      gzip_static on;
      gzip_min_length 1000;
      gzip_comp_level 2;

      proxy_buffering off;
      proxy_buffer_size 16k;
      proxy_busy_buffers_size 24k;
      proxy_buffers 64 4k;
      proxy_force_ranges on;

      proxy_http_version 1.1;
      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 Upgrade $http_upgrade;
      proxy_set_header Connection "upgrade";
      proxy_set_header Host $host;

      proxy_pass http://127.0.0.1:2283;

      proxy_send_timeout 86400s;
      proxy_read_timeout 86400s;

      client_body_timeout 120s;
      keepalive_timeout 150s;
    }
Str1atum commented 2 months ago

similar problem - I cannot play the videos on Safari Mac 17.5 since 1.107 update. Using traefik with Letsencrypt SSL. Worked fine previously and still works fine with Chrome. File is accessible as I can download the recoded and original video with right click "save as"

MFYDev commented 2 months ago

gzip_static on; gzip_min_length 1000; gzip_comp_level 2;

  proxy_buffering off;
  proxy_buffer_size 16k;
  proxy_busy_buffers_size 24k;
  proxy_buffers 64 4k;
  proxy_force_ranges on;

  proxy_http_version 1.1;
  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 Upgrade $http_upgrade;
  proxy_set_header Connection "upgrade";
  proxy_set_header Host $host;

  proxy_pass http://127.0.0.1:2283;

  proxy_send_timeout 86400s;
  proxy_read_timeout 86400s;

  client_body_timeout 120s;
  keepalive_timeout 150s;

HOLY JEEZ!!! THANK YOU SOOOOO MUCH!!!

I just tried your config for my proxy, basically just copy and paste and it is IMMEDIATELY WORKING!!!!

YOU ARE A LIFE SAVOR!!!

raisinbear commented 2 months ago

HOLY JEEZ!!! THANK YOU SOOOOO MUCH!!!

I just tried your config for my proxy, basically just copy and paste and it is IMMEDIATELY WORKING!!!!

YOU ARE A LIFE SAVOR!!!

Oh cool, very glad I could help! 😀 Still would be very interesting what went wrong before. Will do some experimentinf when I find time.

MFYDev commented 2 months ago

@raisinbear I did my research, and here is what I found

Buffering Settings:

I think disabling buffering make the file can be passed through Nginx directly to the client and for video this is important. I tried to create a pr add the necessary config to the nginx reverse proxy part, but seems like Immich's docusaurus wiki is not a public repo.

Anyway thank you so much, this issue has been bothered me for month and finally!

mmomjian commented 2 months ago

@raisinbear I did my research, and here is what I found

Buffering Settings:

  • Original Configuration: The original configuration did not specify any buffering settings. By default, Nginx enables proxy buffering, which can cause issues when streaming media content.
  • New Configuration: The new configuration explicitly disables proxy buffering with proxy_buffering off; and sets various buffer sizes (proxy_buffer_size, proxy_busy_buffers_size, proxy_buffers). Disabling buffering allows Nginx to pass the data directly to the client as it arrives, which is crucial for streaming videos.

I think disabling buffering make the file can be passed through Nginx directly to the client and for video this is important. I tried to create a pr add the necessary config to the nginx reverse proxy part, but seems like Immich's docusaurus wiki is not a public repo.

Anyway thank you so much, this issue has been bothered me for month and finally!

The docs are public. They’re found in the docs/docs folder.