"Load doc content error"

Hi all,
I got Seafile 12.0.11 Pro up and running yesterday. Seafile is running at seafile.example.com. It works perfectly fine, I can create all files. I can edit Markdown files in the browser, and I can download files.

SeaDoc is running at sdoc.example.com and is running “Welcome to sdoc-server. The current version is 1.0.5”.

However, Seadoc isnt doing much. I cannot open .sdoc files, and office (e.g., .docx) files will not preview.

Download URLs do work, e.g.:

https://drive.example.com/seafhttp/repos/d33781e5-d965-464d-a392-05662fe0a315/files//Test.sdoc/?op=download

But the request as formulated by Seafile and sent to SeaDoc which does not work, e.g.:

curl -i -X GET "https://sdoc.example.com/sdoc-server/api/v1/docs/a9be4f40-8395-46a3-af87-f1a91e8a907c/" \
  -H "Accept: application/json, text/plain, */*" \
  -H "Authorization: Token eyJ...VQ"

This request returns this 404:

{
    "message": "Request failed with status code 404",
    "name": "AxiosError",
    "stack": "AxiosError: Request failed with status code 404\n    at Te (https://drive.example.com/media/assets/frontend/static/js/viewFileSdoc.db4ce4b9.js:2:4353767)\n    at XMLHttpRequest.v (https://drive.example.com/media/assets/frontend/static/js/viewFileSdoc.db4ce4b9.js:2:4358073)\n    at it.request (https://drive.example.com/media/assets/frontend/static/js/viewFileSdoc.db4ce4b9.js:2:4366345)",
    "config": {
        "xsrfCookieName": "XSRF-TOKEN",
        "xsrfHeaderName": "X-XSRF-TOKEN",
        ...
        "headers": {
            "Accept": "application/json, text/plain, */*",
            "Authorization": "Token eyJh...VQ"
        },
        "method": "get",
        "url": "https://sdoc.example.com/sdoc-server/api/v1/docs/a9be4f40-8395-46a3-af87-f1a91e8a907c/",
        "allowAbsoluteUrls": true
    },
    "code": "ERR_BAD_REQUEST",
    "status": 404
}

Build

I followed the seadoc setup for v12.

My docker compose is a combination of seafile-server.yml and seadoc.yml with one difference:

The SeaDoc hostname is not the same as the Seafile hostname:

SEAFILE_SERVER_HOSTNAME=${SEAFILE_SERVER_HOSTNAME}
SEADOC_SERVER_URL=${SERVER_PROTOCOL:-http}://${SEADOC_SERVER_HOSTNAME}/sdoc-server

With

SEAFILE_SERVER_HOSTNAME=seafile.example.com
SEADOC_SERVER_HOSTNAME=sdoc.example.com

Of course ENABLE_SEADOC=true.

The ports of the seafile and seadoc services are respectively:

ports:
      - "8012:80"
ports:
    - "7070:80"

Disabling SSL

Ive tried disabling SSL of sdoc.example.com and changing the protocol to http from https, and disabled mixed-content, but that didnt resolve anything.

How can I configure a file request that my seadoc-server responds to with OK?

From there I might be able to find the difference with how Seafile currently constructs the file request.

Hi.

If you added the URLs directly, instead of getting them from .env, maybe you should also add the https://, since the first part of those lines is the SERVER_PROTOCOL, something like:

SEAFILE_SERVER_HOSTNAME=https://seafile.example.com
SEADOC_SERVER_HOSTNAME=https://sdoc.example.com

By any chance, are you using Caddy as your proxy?
If you are, you might want to do something like this for both the seafile and seadoc Caddy config to make it accept self-signed certificates:

https://seafile.example.com:443 {
    reverse_proxy https://your.ip:443 {
          transport http {
               tls
               tls_insecure_skip_verify
        }
    }
}

Did you ever get to resolve this? I’m at the same part where everything else goes but sdoc doesn’t

Nope. I’ve been going at this for a month now and still haven’t been able to figure out how to get Seafile + Seadoc to work over HTTPS.

What I can say is that the Caddy-based installation is easier to get fully functional over HTTP, but I haven’t seen any support for API-based DNS challanges, so it hasn’t been able to locally sign my *.example.com domains.

For other proxy managers the instructions can be found here
https://manual.seafile.com/12.0/setup/use_other_reverse_proxy/
not that loopback IPs (127.0.0.1) are scoped within docker container (networks), ao you may have to use your local IP (192...*)

Tell me - where exactly should this be done?

Yep, can’t get Seadoc to work either

I am not sure if this will help or even if it’s relevant, but I had similar sounding issues with seadoc, wikis, as well as the notification-server.

In my case I have my own NGINX in front of everything, and no matter what I did with my config, those services it would not proxy properly.

What I ended up doing to get it working work was edit seafile’s nginx config instead, inside the seafile-data mount (seafile-data/nginx/conf/seafile.nginx.conf).

Mine currently looks like this:

# -*- mode: nginx -*-
# Auto generated at 04/19/2025 03:08:42
server {
listen 80;
server_name seafile.xxxxx.com;

    client_max_body_size 10m;

    location / {
        proxy_pass http://127.0.0.1:8000/;
        proxy_read_timeout 310s;
        proxy_set_header Host $http_host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Host $server_name;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Connection "";
        proxy_http_version 1.1;

        add_header Access-Control-Allow-Origin *;
        client_max_body_size 0;
        access_log      /var/log/nginx/seahub.access.log seafileformat;
        error_log       /var/log/nginx/seahub.error.log;
    }

    location /seafhttp {
        rewrite ^/seafhttp(.*)$ $1 break;
        proxy_pass http://127.0.0.1:8082;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        client_max_body_size 0;
        proxy_read_timeout  36000s;

        access_log      /var/log/nginx/seafhttp.access.log seafileformat;
        error_log       /var/log/nginx/seafhttp.error.log;
    }

    location /notification/ping {
        proxy_pass http://notification_server:8083/ping;
        access_log      /var/log/nginx/notification.access.log seafileformat;
        error_log       /var/log/nginx/notification.error.log;
    }

    location /notification {
        proxy_pass http://notification_server:8083/;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        access_log      /var/log/nginx/notification.access.log seafileformat;
        error_log       /var/log/nginx/notification.error.log;
    }

    location /seafdav {
        rewrite ^/seafdav$ /seafdav/ permanent;
    }

    location /seafdav/ {
        proxy_pass         http://127.0.0.1:8080/seafdav/;
        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-Host $server_name;
        proxy_read_timeout  1200s;
        client_max_body_size 0;

        access_log      /var/log/nginx/seafdav.access.log seafileformat;
        error_log       /var/log/nginx/seafdav.error.log;
    }

    location /:dir_browser {
        # Logo of WebDAV
        proxy_pass         http://127.0.0.1:8080/:dir_browser;
    }

    location /media {
        root /opt/seafile/seafile-server-latest/seahub;
    }

   location /sdoc-server/ {
        proxy_pass         http://seadoc:80/;
        proxy_redirect     off;
        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-Host  $server_name;
        proxy_set_header   X-Forwarded-Proto $scheme;

        client_max_body_size 100m;
    }

    location /socket.io {
        proxy_pass http://seadoc:80;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_redirect off;

        proxy_buffers 8 32k;
        proxy_buffer_size 64k;

        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_set_header X-NginX-Proxy true;
    }
}

I really dislike that I had to edit that file instead of my own that is in front, which looks like this FYI:

server {
    server_name seafile.xxxxx.com;

    listen 80;
    listen 443 ssl http2;

    ssl_certificate /letsencrypt/live/xxxxx.com/fullchain.pem;
    ssl_certificate_key /letsencrypt/live/xxxxx.com/privkey.pem;

    # Redirect non-https traffic to https
    if ($scheme != "https") {
        return 301 https://$host$request_uri;
    }

    resolver 127.0.0.11 valid=30s;

    server_tokens off;

    proxy_set_header X-Forwarded-For $remote_addr;

    location / {
        set $upstream seafile;
        proxy_pass http://$upstream;
        proxy_read_timeout 310s;
        proxy_set_header Host $host;
        proxy_set_header Scheme $scheme;
        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 Upgrade $http_upgrade;
        # proxy_set_header Connection '';
        proxy_set_header Connection 'upgrade';
        proxy_redirect off;

        proxy_http_version 1.1;

        client_max_body_size 0;
    }

    ### moved the below stuff into seafile's own nginx file :(

    # location /notification/ping {
    #     set $upstream notification_server;
    #     proxy_pass http://$upstream:8083/ping;
    # }

    # location /notification {
    #     set $upstream notification_server;
    #     proxy_pass http://$upstream:8083/;
    #     proxy_http_version 1.1;
    #     proxy_set_header Upgrade $http_upgrade;
    #     # proxy_set_header Upgrade 'websocket';
    #     proxy_set_header Connection 'upgrade';
    # }

    # location /sdoc-server/ {
    #     set $upstream seadoc;
    #     proxy_pass http://$upstream/;
    #     proxy_redirect     off;
    #     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-Host  $server_name;

    #     client_max_body_size 100m;
    # }

    # location /socket.io {
    #     set $upstream seadoc;
    #     proxy_pass http://$upstream;
    #     proxy_http_version 1.1;
    #     proxy_set_header Upgrade $http_upgrade;
    #     proxy_set_header Connection 'upgrade';

    #     proxy_redirect off;

    #     proxy_buffers 8 32k;
    #     proxy_buffer_size 64k;

    #     proxy_set_header X-Real-IP $remote_addr;
    #     proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    #     proxy_set_header Host $http_host;
    #     proxy_set_header X-NginX-Proxy true;
    # }
}
2 Likes

Thanks, that looks promising.

I guess I don’t need seadoc badly enough to try all this though…

Thanks! this helped me to get Seadoc finally up and running.

1 Like

Okay so it’s not just me with the issue.

Are you using your own instance of NGINX in front of Seafile’s?

Actually, I just pulled the 2.0-latest image instead of the documented 1.0-latest and now seadoc works for me!

I am running Seafile behind Nginx Proxy Manager without Caddy. What helped me solve it was adding this to NPM:

location /sdoc-server/ {
    proxy_pass         http://xxx:852/;
    proxy_redirect     off;
    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-Host  $server_name;
    client_max_body_size 100m;
}

location /socket.io/ {
    proxy_pass http://xxx:852;
    proxy_http_version 1.1;
    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-Host $server_name;

    proxy_redirect off;
    client_max_body_size 100m;
}

Thanks to https:// forum. seafile. com/t/seafile-12-wiki-404/23870/2 for getting me most of the way there. :slight_smile:

What ip address do you use behind “seadoc”?

As seadoc is the service name within the docker compose file, other containers on the same docker network can reach it using the hostname seadoc instead of needing to know it’s IP address. Same with notification_service etc.

When I configured it this way, I couldn’t access https://my-seafserver/sdoc-server, which is why I asked.
In the meantime, I’ve created a configuration that completely bypasses the Nginx reverse proxy within the Seafile container. If you’re interested, I’d be happy to show you my web server’s Nginx configuration.

Can you share how you are doing this please?

It’s essentially the same as TechnicallyReal did, but not in Nginx within the Seafile container, but in the “external” Nginx.

I had to change the media path information. For this configuration to work, Seafile’s Gunicorn web server must be configured to allow connections from more than just 127.0.0.1.
Instead of bind = “127.0.0.1:8000” it must be bind = “0.0.0.0:8000”.

Here are the locations from my nginx configuration. 172.17.0.1 is the docker ip address. Of course, the specified ports must be published in Docker.I haven’t tested Seafdav.

    location / {
            proxy_pass http://172.17.0.1:8000/;
            proxy_read_timeout 310s;
            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-Host $server_name;
            proxy_set_header Connection "";
            proxy_http_version 1.1;

            client_max_body_size 0;

            # CORS headers
            add_header Access-Control-Allow-Origin *;

            access_log      /var/log/nginx/seahub_access.log seafileformat;
            error_log       /var/log/nginx/seahub_error.log;
    }

    location /seafhttp {
            rewrite ^/seafhttp(.*)$ $1 break;
            proxy_pass http://172.17.0.1:8082;
            client_max_body_size 0;
            proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;

            proxy_connect_timeout  36000s;
            proxy_read_timeout  36000s;
            proxy_send_timeout  36000s;
            proxy_http_version 1.1;

            # Large file uploads
            proxy_request_buffering off;

            access_log      /var/log/nginx/seafhttp.access.log seafileformat;
            error_log       /var/log/nginx/seafhttp.error.log;
    }

    location /media {
            proxy_pass http://172.17.0.1:8000/media;
            proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_http_version 1.1;
            client_max_body_size 0;

            access_log      /var/log/nginx/media.access.log seafileformat;
            error_log       /var/log/nginx/media.error.log;
    }


    location /notification/ping {
            proxy_pass http://172.17.0.1:8083/ping;

            access_log      /var/log/nginx/notification-server_access.log seafileformat;
            error_log       /var/log/nginx/notification-server_error.log;
    }

    location /notification {
            proxy_pass http://172.17.0.1:8083/;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";

            access_log      /var/log/nginx/notification-server_access.log seafileformat;
            error_log       /var/log/nginx/notification-server_error.log;
    }

    location /seafdav {
            rewrite ^/seafdav$ /seafdav/ permanent;
    }

    location /seafdav/ {
            proxy_pass http://127.0.0.1:8080/seafdav/;
            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-Host $server_name;
            proxy_read_timeout  1200s;
            client_max_body_size 0;

            access_log      /var/log/nginx/seafdav.access.log seafileformat;
            error_log       /var/log/nginx/seafdav.error.log;
        location /:dir_browser {
                # Logo of WebDAV
                proxy_pass http://127.0.0.1:8080/:dir_browser;
        }

        location /sdoc-server/ {
                proxy_pass http://172.17.0.1:8888/;
                proxy_redirect off;
                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-Host $server_name;

                # in der Dokumentation unter Extensions, SeaDoc Integration gefunden, jedoch nicht bei Setup, Use other reverse proxy
                proxy_set_header X-Forwarded-Proto $scheme;

                client_max_body_size 100m;
                access_log      /var/log/nginx/sdoc-server_access.log seafileformat;
                error_log       /var/log/nginx/sdoc-server_error.log;
        }

        location /socket.io {
                proxy_pass http://172.17.0.1:8888;
                proxy_http_version 1.1;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection 'upgrade';
                proxy_redirect off;

                proxy_buffers 8 32k;
                proxy_buffer_size 64k;

                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header Host $http_host;
                proxy_set_header X-NginX-Proxy true;

                access_log      /var/log/nginx/sdoc-server_access.log seafileformat;
                error_log       /var/log/nginx/sdoc-server_error.log;
        }

Hi. Good news to share; at least for me.
In my case, simple one line addition to .env did the trick.

SEAFILE_SERVICE_URL=https://seafile.example.com

I noticed that this environment variable is referred by SEAHUB_SERVICE_URL in seadoc.yml, which defaults with internal hostname (http://seafile). Simply adding the URL of my public-accessible URL made it work.

My environment

  • Fully docker hosted.
  • No Caddy.
  • Behind a proxy, Nginx. It is outside of the Seafile docker network.
  • Nginx configuration is based on the official documentation. Just passing to the exposed ports of Seafile.

I’m really not sure why this works, as the communication between Seafile and SeaDoc should be possible by its default hostname. My best guess is that this variable actually handles both public and internal communication, therefore it must be publicly accessible, too.
Hope this helps.