Possible nginx reverse-proxy issue: Bad URI syntax| wsd/ClientRequestDispatcher.cpp

Hi everyone,

My company is considering a possible integration of Collabora Online within its application and, accordingly, I’m building a small proof-of-concept.

My current POC is very simple, I’ve been following the “Step by step tutorial” and I’ve used Flask to build my WOPI host with a simple “Hello world” example.

I’m running Collabora on a docker container with the following compose.yml:

services:
  collabora-app:
    image: collabora/code
    container_name: collabora-app
    ports:
      - "127.0.0.1:8085:9980"
    environment:
      - domain=mydomain.com
      - extra_params=--o:ssl.enable=false --o:hostname=mydomain.com --o:ssl.termination=true
      - aliasgroup1=https://mydomain.com
    extra_hosts:
      - "mydomain.com:host-gateway"

My Flask application will be running locally on port 5000.

My host is a VM with hostname mydomain.com (where its DNS have also the same name - mydomain.com). Since I’m running my VM on the internet I’m only exposing port 443 and I’m using Nginx as a reverse proxy.

Also locally, mydomain.com is mapped to 127.0.0.1, i.e., in my VM /etc/hosts I have:
127.0.0.1 mydomain.com

My correspondent Nginx conf is:

    location /flask/ {
      proxy_set_header X-Forwarded-Host $host:$server_port;
      proxy_set_header X-Forwarded-Server $host;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_pass http://127.0.0.1:5000/;
    }
 

 location ^~ /browser {
   proxy_pass http://127.0.0.1:8085;
   proxy_set_header Host $host;
 }

 location ^~ /hosting/discovery {
   proxy_pass http://127.0.0.1:8085;
   proxy_set_header Host $host;
 }

 location ^~ /hosting/capabilities {
   proxy_pass http://127.0.0.1:8085;
   proxy_set_header Host $host;
 }

 location ~ ^/cool/(.*)/ws$ {
   proxy_pass http://127.0.0.1:8085;
   proxy_set_header Upgrade $http_upgrade;
   proxy_set_header Connection "Upgrade";
   proxy_set_header Host $host;
   proxy_read_timeout 36000s;
 }

 location ~ ^/(c|l)ool {
   proxy_pass http://127.0.0.1:8085;
   proxy_set_header Host $host;
 }

 location ^~ /cool/adminws {
   proxy_pass http://127.0.0.1:8085;
   proxy_set_header Upgrade $http_upgrade;
   proxy_set_header Connection "Upgrade";
   proxy_set_header Host $host;
   proxy_read_timeout 36000s;
 }

    location / {
        proxy_pass http://127.0.0.1:8085/;
        proxy_set_header Host $host;
        proxy_buffering off;
    }

And with this setup everything works just fine!

Now here’s where my problem starts. In a real production VM I will have my main web-application (which will replace my Flask test-bench of this test) being server to the root URL. This will makes me the need to use a “suffix” in the URL for Collabora access.

Accordingly, I decided to use “/collabora/” as suffix and changed my Nginx conf:

# static files
 location ^~ /collabora/browser {
   proxy_pass http://127.0.0.1:8085/browser;
   proxy_set_header Host $host;
 }

 # WOPI discovery URL
 location ^~ /collabora/hosting/discovery {
   proxy_pass http://127.0.0.1:8085/hosting/discovery;
   proxy_set_header Host $host;
 }


 # Capabilities
 location ^~ /collabora/hosting/capabilities {
   proxy_pass http://127.0.0.1:8085/hosting/capabilities;
   proxy_set_header Host $host;
 }

# main websocket
 location ~ ^/collabora/cool/(.*)/ws$ {
   proxy_pass http://127.0.0.1:8085/cool/$1/ws;
   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 ~ ^/collabora/(c|l)ool {
   proxy_pass http://127.0.0.1:8085/$1ool;
   proxy_set_header Host $host;
 }

 # Admin Console websocket
 location ^~ /collabora/cool/adminws {
   proxy_pass http://127.0.0.1:8085/cool/adminws;
   proxy_set_header Upgrade $http_upgrade;
   proxy_set_header Connection "Upgrade";
   proxy_set_header Host $host;
   proxy_read_timeout 36000s;
 }

    location /collabora/ {
        proxy_pass http://127.0.0.1:8085/;
        proxy_set_header Host $host;
        proxy_buffering off;
    }

With this change I quickly realize that the “urlsrc” links wouldn’t reflect my change, since they were still pointing to the standard source:
https://mydomain.com/browser/d5ebff5/cool.html?

instead of:
https://mydomain.com/collabora/browser/d5ebff5/cool.html?

Which leads my to several 404 not found errors.

After some searching i’ve found out the need to include --o:net.service_root=/collabora on my docker setup in order to reflect the change of having the suffix. Accordingly I changed my compose.yml to:

services:
  collabora-app:
    image: collabora/code
    container_name: collabora-app
    ports:
      - "127.0.0.1:8085:9980"
    environment:
      - domain=mydomain.com
      - extra_params=--o:ssl.enable=false --o:hostname=mydomain.com --o:ssl.termination=true --o:net.service_root=/collabora --o:server_name=mydomain.com
      - aliasgroup1=https://mydomain.com
    extra_hosts:
      - "mydomain.com:host-gateway"

This change makes Collabora accessible (locally in the host) through 127.0.0.1:8085/collabora/

Accordingly, I changed Nginx conf file as well:

    location ^~ /collabora/browser {
        proxy_pass http://127.0.0.1:8085/collabora/browser;
        proxy_set_header Host $host;
    }

    location ^~ /collabora/hosting/discovery {
        proxy_pass http://127.0.0.1:8085/collabora/hosting/discovery;
        proxy_set_header Host $host;
    }

    location ^~ /collabora/hosting/capabilities {
        proxy_pass http://127.0.0.1:8085/collabora/hosting/capabilities;
        proxy_set_header Host $host;
    }

    location ~ ^/collabora/cool/(.*)/ws$ {
        proxy_pass http://127.0.0.1:8085/collabora/cool/$1/ws; 
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
        proxy_set_header Host $host;
        proxy_read_timeout 36000s;
    }

    location ~ ^/collabora/(c|l)ool {
        proxy_pass http://127.0.0.1:8085/collabora/$1ool;
        proxy_set_header Host $host;
    }

    location ^~ /collabora/cool/adminws {
        proxy_pass http://127.0.0.1:8085/collabora/cool/adminws; 
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
        proxy_set_header Host $host;
        proxy_read_timeout 36000s;
    }

    location /collabora/ {
        proxy_pass http://127.0.0.1:8085/collabora/;
        proxy_set_header Host $host;
        proxy_buffering off;
    }

With this change, all the urls seem to have the “right link”, i.e, in https://mydomain.com/collabora/hosting/discovery we can see now the “correct” urlsrc values.

Now, when I try to run the very same example I end up with:

WebSocket connection to 'wss://mydomain.com/collabora/cool/https%3A%2F%2Fmydomain.com%2Fflask%2Fwopi%2Ffiles%2F1233%3Faccess_token%3Dtest%26access_token_ttl%3D0/ws?WOPISrc=https%3A%2F%2Fmydomain.com%2Fflask%2Fwopi%2Ffiles%2F1233&compat=/ws' failed: Error during WebSocket handshake: Unexpected response code: 400

Checking/inspecting the websocket call the request URL I have:

wss://mydomain.com/collabora/cool/https%3A%2F%2Fmydomain.com%2Fflask%2Fwopi%2Ffiles%2F1233%3Faccess_token%3Dtest%26access_token_ttl%3D0/ws?WOPISrc=https%3A%2F%2Fmydomain.com%2Fflask%2Fwopi%2Ffiles%2F1233&compat=/ws

From Collabora docker logs I can see:

collabora-app | wsd-00001-00036 2024-10-08 15:05:51.056804 +0000 [ websrv_poll ] ERR #31: #31 Exception while processing incoming request: [GET /collabora/cool/https:/mydomain.com/flask/wopi/files/1233%3Faccess_token=test&access_token_ttl=0/ws?WOPISrc=https%3A%2F%2Fmydomain.com%2Fflask%2Fwopi%2Ffiles%2F12...]: Bad URI syntax| wsd/ClientRequestDispatcher.cpp:915

I’m not sure what the Bad URI means, but I guess something is happening between Nginx and Collabora.

I’ve been trying different configs with Nginx back and forth and trying to learn as much as I can by searching around on the web but, unfortunately, I haven’t made any progress by a week now.

Am I missing any config from Collabora to handle prefixes correctly? Or am I missing something from Nginx side to handle websockets properly?

Any help would be deeply appreciated!

Thank you!

In the “Step by Step Tutorial,” there might be an encoding issue when calling the URL via JavaScript without a form POST element. The URL needs to be properly URL-encoded. The form of the URL should be:

https://<WOPI client URL>:<port>/browser/<hash>/cool.html?WOPISrc=https://<WOPI host URL>/<...>/wopi/files/<id>

It seems that the WOPISrc parameter is missing in your URL, which could be causing the issue.

Please re iterate through step 4 in Step-by-step guide

Thanks,
Darshan

Hi Darshan,

Thank you so much for your feedback and for taking the time to check on my case.

I believe the URL was already correct, I mean, it have worked with my first test where I didn’t use a suffix, i.e., Collabora was accessible through the root of the URL (https://mydomain.com/).

Now, by using a suffix (/collabora/) in the URL and by making the necessary changes on my test application, I cannot mange to make Collabora working. Collabora is accessible now by https://mydomain.com/collabora/

Double checking the URL that is now being used in the POST request, directly from my test application:

https://mydomain.com/collabora/browser/d5ebff5/cool.html?WOPISrc=https%3A%2F%2Fmydomain.com%2Fflask%2Fwopi%2Ffiles%2F123

Where we can access to both:
https://mydomain.com/collabora/browser/d5ebff5/cool.html?
and
https://mydomain.com/flask/wopi/files/123

Checking Nginx logs we can confirm the the URL POST request:

xxx.xx.xxx.xxx - - [09/Oct/2024:05:30:24 -0400] "POST /collabora/browser/d5ebff5/cool.html?WOPISrc=https%3A%2F%2Fmydomain.com%2Fflask%2Fwopi%2Ffiles%2F123
 HTTP/2.0" 200 3695 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36"

Then we can see the following GET requests (to the POST) in Nginx logs:

 xxx.xx.xxx.xxx - - [09/Oct/2024:05:30:24 -0400] "GET /collabora/browser/d5ebff5/branding.js HTTP/2.0" 200 1089 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36"                                                                                                  
 xxx.xx.xxx.xxx - - [09/Oct/2024:05:30:24 -0400] "GET /collabora/browser/d5ebff5/branding.css HTTP/2.0" 200 6699 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36"                                                                                                 
 xxx.xx.xxx.xxx - - [09/Oct/2024:05:30:24 -0400] "GET /collabora/browser/d5ebff5/global.js HTTP/2.0" 200 10121 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36"                                                                                                   
 xxx.xx.xxx.xxx - - [09/Oct/2024:05:30:24 -0400] "GET /collabora/browser/d5ebff5/bundle.css HTTP/2.0" 200 114261 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36"                                                                                                 
 xxx.xx.xxx.xxx - - [09/Oct/2024:05:30:24 -0400] "GET /collabora/browser/d5ebff5/bundle.js HTTP/2.0" 200 782266 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36"                                                                                                  
 xxx.xx.xxx.xxx - - [09/Oct/2024:05:30:24 -0400] "GET /collabora/browser/d5ebff5/device-desktop.css HTTP/2.0" 200 302 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36"                                                                                            
 xxx.xx.xxx.xxx - - [09/Oct/2024:05:30:24 -0400] "GET /collabora/browser/d5ebff5/branding-desktop.css HTTP/2.0" 200 0 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36"                                                                                            
 xxx.xx.xxx.xxx - - [09/Oct/2024:05:30:24 -0400] "GET /collabora/browser/d5ebff5/images/closedoc.svg HTTP/2.0" 200 310 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36"
 xxx.xx.xxx.xxx - - [09/Oct/2024:05:30:24 -0400] "GET /collabora/cool/https%3A%2F%2Fmydomain.com%2Fflask%2Fwopi%2Ffiles%2F123%3Faccess_token%3Dtest%26access_token_ttl%3D0/ws?WOPISrc=https%3A%2F%2Fmydomain.com%2Fflask%2Fwopi%2Ffiles%2F123&compat=/ws HTTP/1.1" 400 0 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36

Where this last GET request, with the 400 errror, corresponds to what we then see on Collabora logs:

collabora-app | wsd-00001-00036 2024-10-09 09:30:24.907091 +0000 [ websrv_poll ] ERR #31: #31 Exception while processing incoming request: [GET /collabora/cool/https:/mydomain.com/flask/wopi/files/123%3Faccess_token=test&access_token_ttl=0/ws?WOPISrc=https%3A%2F%2Fmydomain.com%2Fflask%2Fwopi%2Ffiles%2F123...]: Bad URI syntax| wsd/ClientRequestDispatcher.cpp:915

My intuition is that I may be missing something on my compose.yml for collabora handle the suffix part correctly or I’m still missing some config in my Nginx, which I still didn’t manage to find out.

I would like to add here some findings.

I noticed when I use Collabora withing the root URL path (https://mydomain.com/) - where I manage to have it working - I can see from my Nginx logs the following calls:

xxx.xx.xxx.xxx - - [09/Oct/2024:12:28:18 -0400] "POST /browser/d5ebff5/cool.html?WOPISrc=https%3A%2F%2Fmydomain.com%2Fflask%2Fwopi%2Ffiles%2F123 HTTP/2.0"
 200 3680 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:131.0) Gecko/20100101 Firefox/131.0"
172.19.0.2 - - [09/Oct/2024:12:28:18 -0400] "GET /flask/wopi/files/123?access_token=test&access_token_ttl=0 HTTP/1.1" 200 222 "-" "COOLWSD HTTP Agent 24.04.7.2"
xxx.xx.xxx.xxx - - [09/Oct/2024:12:28:18 -0400] "GET /browser/d5ebff5/branding.css HTTP/2.0" 200 6699 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:131.0) Gecko/201
00101 Firefox/131.0"
xxx.xx.xxx.xxx - - [09/Oct/2024:12:28:18 -0400] "GET /browser/d5ebff5/bundle.css HTTP/2.0" 200 114261 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:131.0) Gecko/201
00101 Firefox/131.0"
...
...

We can see the POST request from my application and right after we can see a GET request coming from docker (172.19.0.2) to my application WOPI interface.

Now when we use the prefix on the URL for collabora (https://mydomain.com/collabora/) I noticed that only the POST is made on Nginx but we dont get the GET request from docker:

xxx.xx.xxx.xxx - - [09/Oct/2024:12:35:58 -0400] "POST /collabora/browser/d5ebff5/cool.html?WOPISrc=https%3A%2F%2Fmydomain.com%2Fflask%2Fwopi%2Ffiles%2F123 HTTP/2.0" 200 3695 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36"
xxx.xx.xxx.xxx - - [09/Oct/2024:12:35:58 -0400] "GET /collabora/browser/d5ebff5/branding.css HTTP/2.0" 200 6699 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36"
xxx.xx.xxx.xxx - - [09/Oct/2024:12:35:58 -0400] "GET /collabora/browser/d5ebff5/branding.js HTTP/2.0" 200 1089 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36"
...
...

After some more playing with Nginx conf file I believe the issue may be on this rule?

location ~ ^/collabora/cool/(.*)/ws$ {
    proxy_pass http://127.0.0.1:8085/collabora/cool/$1/ws;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
    proxy_set_header Host $host;
    proxy_read_timeout 36000s;
}

With this rule in place, we cannot observe the call:

172.19.0.2 - - [09/Oct/2024:12:28:18 -0400] "GET /flask/wopi/files/123?access_token=test&access_token_ttl=0 HTTP/1.1" 200 222 "-" "COOLWSD HTTP Agent 24.04.7.2"

Making the change to (removing /collabora/ from the proxy_pass parameter):

location ~ ^/collabora/cool/(.*)/ws$ {
    rewrite ^/collabora/cool/(.*)/ws$ /$1/ws break;
    proxy_pass http://127.0.0.1:8085/cool/$1/ws;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
    proxy_set_header Host $host;
    proxy_read_timeout 36000s;
}

we can see the GET call being made! Although, as expected, collabora logs complain about:

collabora-app | wsd-00001-00036 2024-10-10 08:23:10.825828 +0000 [ websrv_poll ] ERR #33: #33 bad request: [GET /cool/https:/mydomain.com/flask/wopi/fil...]: The request does not start with prefix: /collabora| wsd/ClientRequestDispatcher.cpp:905

All my tries to work around this have failed so far.

My question is, did anyone have been able to setup collabora with a prefix on the URL with a docker container? If so, can you share your toughs on your approach?

Hey @n36ul4, since you’ve removed /collabora/, you should also remove the --o:net.service_root=/collabora form coolwsd. Have you done that? If not, please remove it and try again, and let me know how it goes.

If it still doesn’t work, could you share your Nginx logs here? That way, I can analyze it better.

Thanks,
Darshan

Hi @darshan

Thank you so much once again!

By removing the --o:net.service_root=/collabora from coolwsd we will end in one of my very first tests, where the “urlsrc” links (shown at /hosting/discovery) would stay with its default state:

https://mydomain.com/browser/d5ebff5/cool.html?

which means we would not be able access to any of its content. It was base on this that I was lead to introduce the --o:net.service_root=/collabora change so the “urlsrc” links from /hosting/discovery could be accessible:

https://mydomain.com/collabora/browser/d5ebff5/cool.html?

Considering then this scenario again:
my docker compose.yml

services:
  collabora-app:
    image: collabora/code
    container_name: collabora-app
    ports:
      - "127.0.0.1:8085:9980"
    environment:
      - domain=mydomain.eknow.com
      - extra_params=--o:ssl.enable=false --o:hostname=mydomain.eknow.com --o:ssl.termination=true 
      - aliasgroup1=https://mydomain.com:443
    extra_hosts:
      - "mydomain.com:host-gateway"

Having my nginx with:

 location ^~ /collabora/browser {
   proxy_pass http://127.0.0.1:8085/browser;
   proxy_set_header Host $host;
 }

 location ^~ /collabora/hosting/discovery {
   proxy_pass http://127.0.0.1:8085/hosting/discovery;
   proxy_set_header Host $host;
 }

 location ^~ /collabora/hosting/capabilities {
   proxy_pass http://127.0.0.1:8085/hosting/capabilities;
   proxy_set_header Host $host;
 }


 location ~ ^/collabora/cool/(.*)/ws$ {
   rewrite ^/collabora/cool/(.*)/ws$ /$1/ws break;
   proxy_pass http://127.0.0.1:8085/cool/$1/ws;
   proxy_set_header Upgrade $http_upgrade;
   proxy_set_header Connection "Upgrade";
   proxy_set_header Host $host;
   proxy_read_timeout 36000s;
 }

 location ~ ^/collabora/(c|l)ool {
   proxy_pass http://127.0.0.1:8085/$1ool;
   proxy_set_header Host $host;
 }

 location ^~ /collabora/cool/adminws {
   proxy_pass http://127.0.0.1:8085/cool/adminws;
   proxy_set_header Upgrade $http_upgrade;
   proxy_set_header Connection "Upgrade";
   proxy_set_header Host $host;
   proxy_read_timeout 36000s;
 }

  location /collabora/ {
    proxy_pass http://127.0.0.1:8085/;
    proxy_set_header Host $host;
    proxy_buffering off;
  }

As soon as I try to make the call to collabora I get the following POST request log from Nginx, where it shows that it cannot find the https://mydomain.com/browser/d5ebff5/cool.html?:


xxx.xx.xxx.xxx - - [10/Oct/2024:11:52:46 -0400] "POST /browser/d5ebff5/cool.html?WOPISrc=https%3A%2F%2Fmydomain.com%2Fflask%2Fwopi%2Ffiles%2F123 HTTP/2.0" 404 281 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36"

Thank you once again!

This is a timely thread - I am experiencing exactly the same issue, although my set up is a little different essentially I am attempting to achieve the same (use a /collabora suffix):

  • nginx reverse proxy with /collabora with proxy pass to:
  • nginx ingress on k8s, configured with /collabora but rewriting with /$2, so the request is forwarded with no suffix to the collabora container
  • collabora container (with no suffix configured in the start up params) receives the request with:
wsd-00001-00032 2024-10-13 07:45:59.007001 +0000 [ websrv_poll ] ERR  #36: #36 Exception while processing incoming request: [GET /cool/https:/domain.com/nextcloud/index.php/apps/richdocuments/wopi/files/166183_oc12zdgi5841%3Faccess_token=vVNkokdLiIoOtWrbc9xONiwOv3LuKeap&access_token_ttl=0&permission=edit/ws?WOPISrc=https%3A%2F%2Fdomain.com%2Fnextcloud%2Findex.php%2Fapps%2Frichdocuments%2Fwopi%2Ffiles%2F166183_...]: Bad URI syntax| wsd/ClientRequestDispatcher.cpp:919

I have tried various rewrites, forwarding /browser /cool, /hosting and I believe I am experiencing the same as OP, so I’m following with interest

I’ve been back and forth with different combinations with the nginx setup but unfortunately I couldn’t get any development from my previous state.

My current workaround is to have a new CNAME so nginx can route the requests to this “new server name” and so Collabora will be available under the root URL. This is not desired at all but was the only way I could think to overcome this problem for now.