Unable to use collabora online from nextcloud with Nginx

I have a debian (12) server with nextcloud (27.1.5) and collabora online (CODE) installed from deb packages.

I try to swith from apache to nginx server. With apache, all was working well, but not from nginx.

From nginx, in nextcloud settings, I have the green check so config seems to be OK, but when I try to open a document I have this error:

Failed to establish socket connection or socket connection closed unexpectedely. The reverse proxy might be misconfigured, please contact the administrator. For more infor on proxy configuration please check out …

This is my nextcloud nextcloud config on nginx:

server {
    listen 80;
    server_name cloud.mydomain.fr;

    #Redirige toutes les requêtes http vers https
    return 301 https://$host$request_uri;
}

upstream php-handler {
    #server 127.0.0.1:9000;
    server unix:/var/run/nextcloud.sock;
    #server unix:/run/php/php8.2-fpm.sock;
}

# Set the `immutable` cache control options only for assets with a cache busting `v` argument
map $arg_v $asset_immutable {
    "" "";
    default "immutable";
}

server {
    listen 443 ssl;
    server_name cloud.mydomain.fr;

    # SSL certificate configuration
    ssl_certificate /etc/letsencrypt/live/cloud.mydomain.fr/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/cloud.mydomain.fr/privkey.pem;

    root /var/www/vhosts/mydomain.fr/subdomains/cloud/httpdocs;

    # set max upload size and increase upload timeout:
    client_max_body_size 512M;
    client_body_timeout 300s;
    fastcgi_buffers 64 4K;

    # Enable gzip but do not remove ETag headers
    gzip on;
    gzip_vary on;
    gzip_comp_level 4;
    gzip_min_length 256;
    gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
    gzip_types application/atom+xml text/javascript application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/wasm application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;

    # Pagespeed is not supported by Nextcloud, so if your server is built
    # with the `ngx_pagespeed` module, uncomment this line to disable it.
    #pagespeed off;

    # The settings allows you to optimize the HTTP2 bandwidth.
    # See https://blog.cloudflare.com/delivering-http-2-upload-speed-improvements/
    # for tuning hints
    client_body_buffer_size 512k;

    # HTTP response headers borrowed from Nextcloud `.htaccess`
    add_header Referrer-Policy                   "no-referrer"       always;
    add_header X-Content-Type-Options            "nosniff"           always;
    add_header X-Frame-Options                   "SAMEORIGIN"        always;
    add_header X-Permitted-Cross-Domain-Policies "none"              always;
    add_header X-Robots-Tag                      "noindex, nofollow" always;
    add_header X-XSS-Protection                  "1; mode=block"     always;

    # Remove X-Powered-By, which is an information leak
    fastcgi_hide_header X-Powered-By;

    # Add .mjs as a file extension for javascript
    # Either include it in the default mime.types list
    # or include you can include that list explicitly and add the file extension
    # only for Nextcloud like below:
    include mime.types;
    types {
        text/javascript js mjs;
    }

    # Specify how to handle directories -- specifying `/index.php$request_uri`
    # here as the fallback means that Nginx always exhibits the desired behaviour
    # when a client requests a path that corresponds to a directory that exists
    # on the server. In particular, if that directory contains an index.php file,
    # that file is correctly served; if it doesn't, then the request is passed to
    # the front-end controller. This consistent behaviour means that we don't need
    # to specify custom rules for certain paths (e.g. images and other assets,
    # `/updater`, `/ocs-provider`), and thus
    # `try_files $uri $uri/ /index.php$request_uri`
    # always provides the desired behaviour.
    index index.php index.html /index.php$request_uri;

    # Rule borrowed from `.htaccess` to handle Microsoft DAV clients
    location = / {
        if ( $http_user_agent ~ ^DavClnt ) {
            return 302 /remote.php/webdav/$is_args$args;
        }
    }

    location = /robots.txt {
        allow all;
        log_not_found off;
        access_log off;
    }

    # Make a regex exception for `/.well-known` so that clients can still
    # access it despite the existence of the regex rule
    # `location ~ /(\.|autotest|...)` which would otherwise handle requests
    # for `/.well-known`.
    location ^~ /.well-known {
        # The rules in this block are an adaptation of the rules
        # in `.htaccess` that concern `/.well-known`.

        location = /.well-known/carddav { return 301 /remote.php/dav/; }
        location = /.well-known/caldav  { return 301 /remote.php/dav/; }

        location /.well-known/acme-challenge    { try_files $uri $uri/ =404; }
        location /.well-known/pki-validation    { try_files $uri $uri/ =404; }

        # Let Nextcloud's API for `/.well-known` URIs handle all other
        # requests by passing them to the front-end controller.
        return 301 /index.php$request_uri;
    }

    # Rules borrowed from `.htaccess` to hide certain paths from clients
    location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)(?:$|/)  { return 404; }
    location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console)                { return 404; }

    # Ensure this block, which passes PHP files to the PHP process, is above the blocks
    # which handle static assets (as seen below). If this block is not declared first,
    # then Nginx will encounter an infinite rewriting loop when it prepends `/index.php`
    # to the URI, resulting in a HTTP 500 error response.
    location ~ \.php(?:$|/) {
        # Required for legacy support
        rewrite ^/(?!index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|ocs-provider\/.+|.+\/richdocumentscode\/proxy) /index.php$request_uri;

        fastcgi_split_path_info ^(.+?\.php)(/.*)$;
        set $path_info $fastcgi_path_info;

        try_files $fastcgi_script_name =404;

        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $path_info;
        fastcgi_param HTTPS on;

        fastcgi_param modHeadersAvailable true;         # Avoid sending the security headers twice
        fastcgi_param front_controller_active true;     # Enable pretty urls
        fastcgi_pass php-handler;

        fastcgi_intercept_errors on;
        fastcgi_request_buffering off;

        fastcgi_max_temp_file_size 0;
    }

    # Serve static files
    location ~ \.(?:css|js|mjs|svg|gif|png|jpg|ico|wasm|tflite|map|ogg|flac)$ {
        try_files $uri /index.php$request_uri;
        add_header Cache-Control "public, max-age=15778463, $asset_immutable";
        access_log off;     # Optional: Don't log access to assets

        location ~ \.wasm$ {
            default_type application/wasm;
        }
    }

    location ~ \.woff2?$ {
        try_files $uri /index.php$request_uri;
        expires 7d;         # Cache-Control policy borrowed from `.htaccess`
        access_log off;     # Optional: Don't log access to assets
    }

    # Rule borrowed from `.htaccess`
    location /remote {
        return 301 /remote.php$request_uri;
    }

    location / {
        try_files $uri $uri/ /index.php$request_uri;
    }
}

(with cloud.mydomain.fr as my nextcloud public address)

And this is my nginx config for collabora online:

server {
    listen 80;
    server_name office.mydomain.fr;

    #Redirige toutes les requêtes http vers https
    return 301 https://$host$request_uri;
}

server {
    listen       443 ssl;
    server_name  office.mydomain.fr;


    ssl_certificate /etc/letsencrypt/live/office.mydomain.fr/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/office.mydomain.fr/privkey.pem;

    # static files
    location ^~ /browser {
        proxy_pass https://24.24.12.15:9980;
        proxy_set_header Host $host;
    }


    # WOPI discovery URL
    location ^~ /hosting/discovery {
        proxy_pass https://24.24.12.15:9980;
        proxy_set_header Host $host;
    }


    # Capabilities
    location ^~ /hosting/capabilities {
        proxy_pass https://24.24.12.15:9980;
        proxy_set_header Host $host;
    }


    # main websocket
    location ~ ^/cool/(.*)/ws$ {
        proxy_pass https://24.24.12.15:9980;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
        proxy_set_header Host $host;
        proxy_read_timeout 36000s;
    }


    # download, presentation and image upload
    location ~ ^/(c|l)ool {
        proxy_pass https://24.24.12.15:9980;
        proxy_set_header Host $host;
    }


    # Admin Console websocket
    location ^~ /cool/adminws {
        proxy_pass https://24.24.12.15:9980;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
        proxy_set_header Host $host;
        proxy_read_timeout 36000s;
    }

}

(with 24.24.12.15 is my server public IP and office.mydomain.fr code public url).

I tried also with 127.0.0.1 like in doc for IP, same error.

For information, with apache, this config worked for collabora online:

<IfModule mod_ssl.c>
<VirtualHost *:443>
        ServerName                      office.mydomain.fr
        Options -Indexes

AllowEncodedSlashes NoDecode
 SSLProxyEngine On
 ProxyPreserveHost On

 # static html, js, images, etc. served from coolwsd
 # browser is the client part of Collabora Online
 ProxyPass           /browser https://24.24.12.15:9980/browser retry=0
 ProxyPassReverse    /browser https://24.24.12.15:9980/browser

 # WOPI discovery URL
 ProxyPass           /hosting/discovery https://24.24.12.15:9980/hosting/discovery retry=0
 ProxyPassReverse    /hosting/discovery https://24.24.12.15:9980/hosting/discovery

 # Capabilities
 ProxyPass           /hosting/capabilities https://24.24.12.15:9980/hosting/capabilities retry=0
 ProxyPassReverse    /hosting/capabilities https://24.24.12.15:9980/hosting/capabilities

 # Main websocket
 ProxyPassMatch      "/cool/(.*)/ws$"      wss://24.24.12.15:9980/cool/$1/ws nocanon

 # Admin Console websocket
 ProxyPass           /cool/adminws wss://24.24.12.15:9980/cool/adminws

 # Download as, Fullscreen presentation and Image upload operations
 ProxyPass           /cool https://24.24.12.15:9980/cool
 ProxyPassReverse    /cool https://24.24.12.15:9980/cool
 # Compatibility with integrations that use the /lool/convert-to endpoint
 ProxyPass           /lool https://24.24.12.15:9980/cool
 ProxyPassReverse    /lool https://24.24.12.15:9980/cool

SSLCertificateFile /etc/letsencrypt/live/office.mydomain.fr/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/office.mydomain.fr/privkey.pem
Include /etc/letsencrypt/options-ssl-apache.conf
</VirtualHost>
</IfModule>

With nginx, when I try to open a document, I have this logs for coolwsd service:

anv. 07 19:57:29 srv coolwsd[14758]: kit-14758-13748 2024-01-07 19:57:29.156354 +0100 [ kit_spare_004 ] WRN  chmod("/opt/cool/child-roots/13732-8c285983/evEkRPaqF6oSIPDJ/lo/") failed: Read-only file system| common/JailUtil.cpp:282
janv. 07 19:57:29 srv coolwsd[13732]: wsd-13732-14757 2024-01-07 19:57:29.162602 +0100 [ docbroker_003 ] ERR  WOPI::CheckFileInfo failed for URI [https://cloud.mydomain.fr/index.php/apps/richdocuments/wopi/files/14516_ocdxk959hxs7?access_token=yfZNuqv17VzPpHtKxNFnx5W2UHJkKAuM&access_token_ttl=0]: 0 (Unknown) . Headers:         Body: []| wsd/Storage.cpp:712
janv. 07 19:57:29 srv coolwsd[13732]: wsd-13732-14757 2024-01-07 19:57:29.162675 +0100 [ docbroker_003 ] ERR  loading document exception: WOPI::CheckFileInfo failed: | wsd/DocumentBroker.cpp:2640
janv. 07 19:57:29 srv coolwsd[13732]: wsd-13732-14757 2024-01-07 19:57:29.162697 +0100 [ docbroker_003 ] ERR  Failed to add session to [https%3A%2F%2Fcloud.mydomain.fr%3A443%2Findex.php%2Fapps%2Frichdocuments%2Fwopi%2Ffiles%2F14516_ocdxk959hxs7] with URI [https://cloud.mydomain.fr/index.php/apps/richdocuments/wopi/files/14516_ocdxk959hxs7?access_token=yfZNuqv17VzPpHtKxNFnx5W2UHJkKAuM&access_token_ttl=0]: WOPI::CheckFileInfo failed: | wsd/DocumentBroker.cpp:2602
janv. 07 19:57:29 srv coolwsd[13732]: wsd-13732-14757 2024-01-07 19:57:29.162718 +0100 [ docbroker_003 ] ERR  Storage error while starting session on https%3A%2F%2Fcloud.mydomain.fr%3A443%2Findex.php%2Fapps%2Frichdocuments%2Fwopi%2Ffiles%2F14516_ocdxk959hxs7 for socket #24. Terminating connection. Error: WOPI::CheckFileInfo failed: | wsd/COOLWSD.cpp:5343
janv. 07 19:57:29 srv coolwsd[13732]: wsd-13732-14757 2024-01-07 19:57:29.166127 +0100 [ docbroker_003 ] ERR  #18: Read failed, have 0 buffered bytes (ECONNRESET: Connection reset by peer)| net/Socket.hpp:1139
janv. 07 19:57:29 srv coolwsd[13732]: wsd-13732-14757 2024-01-07 19:57:29.166175 +0100 [ docbroker_003 ] WRN  #18: Unassociated Kit (14102) disconnected unexpectedly| wsd/COOLWSD.cpp:3776
janv. 07 19:57:29 srv coolwsd[14776]: kit-14776-13748 2024-01-07 19:57:29.302041 +0100 [ kit_spare_005 ] WRN  chmod("/opt/cool/child-roots/13732-8c285983/JWklR77eEFl7hr0p/lo/") failed: Read-only file system| common/JailUtil.cpp:282
janv. 07 19:57:29 srv coolwsd[13732]: wsd-13732-14769 2024-01-07 19:57:29.305030 +0100 [ docbroker_004 ] ERR  WOPI::CheckFileInfo failed for URI [https://cloud.mydomain.fr/index.php/apps/richdocuments/wopi/files/14516_ocdxk959hxs7?access_token=yfZNuqv17VzPpHtKxNFnx5W2UHJkKAuM&access_token_ttl=0&permission=edit]: 0 (Unknown) . Headers:         Body: []| wsd/Storage.cpp:712
janv. 07 19:57:29 srv coolwsd[13732]: wsd-13732-14769 2024-01-07 19:57:29.305100 +0100 [ docbroker_004 ] ERR  loading document exception: WOPI::CheckFileInfo failed: | wsd/DocumentBroker.cpp:2640
janv. 07 19:57:29 srv coolwsd[13732]: wsd-13732-14769 2024-01-07 19:57:29.305123 +0100 [ docbroker_004 ] ERR  Failed to add session to [https%3A%2F%2Fcloud.mydomain.fr%3A443%2Findex.php%2Fapps%2Frichdocuments%2Fwopi%2Ffiles%2F14516_ocdxk959hxs7] with URI [https://cloud.mydomain.fr/index.php/apps/richdocuments/wopi/files/14516_ocdxk959hxs7?access_token=yfZNuqv17VzPpHtKxNFnx5W2UHJkKAuM&access_token_ttl=0&permission=edit]: WOPI::CheckFileInfo failed: | wsd/DocumentBroker.cpp:2602
janv. 07 19:57:29 srv coolwsd[13732]: wsd-13732-14769 2024-01-07 19:57:29.305144 +0100 [ docbroker_004 ] ERR  Storage error while starting session on https%3A%2F%2Fcloud.mydomain.fr%3A443%2Findex.php%2Fapps%2Frichdocuments%2Fwopi%2Ffiles%2F14516_ocdxk959hxs7 for socket #24. Terminating connection. Error: WOPI::CheckFileInfo failed: | wsd/COOLWSD.cpp:5343
janv. 07 19:57:29 srv coolwsd[13732]: wsd-13732-14769 2024-01-07 19:57:29.308728 +0100 [ docbroker_004 ] ERR  #18: Read failed, have 0 buffered bytes (ECONNRESET: Connection reset by peer)| net/Socket.hpp:1139
janv. 07 19:57:29 srv coolwsd[13732]: wsd-13732-14769 2024-01-07 19:57:29.308771 +0100 [ docbroker_004 ] WRN  #18: Unassociated Kit (14758) disconnected unexpectedly| wsd/COOLWSD.cpp:3776

With apache, only this appear (and it work):

janv. 07 21:19:08 srv coolwsd[28509]: kit-28509-13748 2024-01-07 21:19:08.113014 +0100 [ kit_spare_008 ] WRN  chmod("/opt/cool/child-roots/13732-8c285983/J4plBo05wZlZrbfX/lo/") failed: Read-only file system| common/JailUtil.cpp:282
janv. 07 21:19:09 srv coolwsd[27878]: kit-27878-13748 2024-01-07 21:19:09.041266 +0100 [ kitbroker_007 ] ERR  Session is not found. Maybe exited after rendering request.| kit/Kit.cpp:890
janv. 07 21:19:09 srv coolwsd[27878]: kit-27878-13748 2024-01-07 21:19:09.041300 +0100 [ kitbroker_007 ] ERR  Session is not found. Maybe exited after rendering request.| kit/Kit.cpp:890
janv. 07 21:19:09 srv coolwsd[13732]: wsd-13732-13762 2024-01-07 21:19:09.064734 +0100 [ websrv_poll ] ERR  FileServerRequestHandler: File not found: Invalid URI request: [/browser/84551c8/images/dark/lc_exportdirectpdf.svg].| wsd/FileServer.cpp:713

Could you help me to find what is the problem with nginx please?

Thank you in advance for the time you give me…

Floreal

Nobody to help me please?

Sorry about that; it looks like the WOPI URL that is given to coolwsd cannot be resolved from coolwsd’s perspective. I assume that you can access the URL above with a CURL command and get some details back from the WOPI server(?)

If so, then it is interesting to work out why coolwsd cannot; very curious. I would dig down that path - why would coolwsd be able to access it under NGINX but not Apache - but best to start on the command-line. Ultimately coolwsd expects to get a URL for a WOPISRC that it can resolve =)

HTH.