flutternginxdocker-composesocket.ioflask-socketio

Flutter web + flask_socketio: flutter app can't connect to flask socketio


I am trying to run a Flutter web frontend and a python flask + flask_socketio api/socketio backend in a docker compose file. The frontend image uses nginx as a reverse proxy, and its configuration contains the location for both the api and the websocket. The backend SocketIO runs in async_mode='threading'. I have no trouble communicating with the api, but I just can't connect to the socketio endpoint.

The frontend uses the socket_io_client package (version 3.1.2) to connect to the websocket. This is the connect method I created:

Future<void> connectAndWait() {
  final completer = Completer<void>();

  // url = "/socket.io"
  socket = socket_io.io(url, <String, dynamic>{
    'transports': ['websocket'],
    'autoConnect': false,
  });

  socket.connect();

  return completer.future;
}

Here is the current nginx.conf file:

worker_processes 1;

events { worker_connections 1024; }

http {
    include       mime.types;
    default_type  application/octet-stream;

    sendfile        on;
    keepalive_timeout  65;

    upstream backend_server {
        # "backend" is the service name in the docker compose file 
        server backend:5300;
    }

    server {
        listen 80;

        location / {
            root /usr/share/nginx/html;
            index index.html;
            try_files $uri $uri/ /index.html;
        }

        # Proxy REST API
        location /api/ {
            proxy_pass http://backend_server/;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }

        location /socket.io {
            # This is the problem part
        }
    }
}

What I've noticed

When I debug the application and run the 2 services separately (i.e. starting the frontend and then starting the backend in another VSCode window), I've noticed that I can easily connect to the websocket through http://localhost:5300 (the backend). This method works both in postman and in the frontend. Since this method seemed to work, I've thought about "translating" this endpoint to a location that I can put in the nginx.conf file.

However, when running the docker compose file, nothing really happens: no websocket connections started in the Network tab, and no 'connect' or 'connect-error' event thrown.

I'm pretty sure this isn't even a CORS error since no such thing showed up in the DevTools' console. Also, I have the CORS(app, ...) (from flask_cors) instruction in the backend file.

What I've tried

Here is a list of what I've tried (note: in this section I'm running the services in the docker compose file):

  1. I've copy-pasted this config location from the documentation and modified like so: I removed the include proxy_params; line and changed http://127.0.0.1:5000/socket.io to http://backend:5300/socket.io.

  2. Tried using the api location ("/api") as the url for the websocket.

  3. Tried running the socketio app with async_mode set to 'eventlet' instead of threading. Also, while debugging this method the frontend was connecting but not receiving any messages.

  4. As I've mentioned earlier, I tried to "translate" the basic endpoint into a valid nginx location like this:

    location /socket.io/ {
        proxy_pass http://backend_server/;
        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;
    }
    
  5. (edit 9 june 2025) Did the same thing as described in the first point of this list, but insted of removing include proxy_params I replaced it with these lines found on a related GitHub issue. Even though this solution doesn't work through the frontend (i.e. using connectAndWait() method shown above), it (kind of) does in postman: I can connect to the websocket through http://localhost:5200 (the frontend port) but not through http://localhost:5200/socket.io. Also, whenever I disconnect I get this message in the frontend logs:

    172.19.0.1 - - [09/Jun/2025:10:28:24 +0000] "GET /socket.io/?EIO=4&transport=websocket HTTP/1.1" 101 128 "-" "-"
    

Solution

  • I finally managed to solve the problem. I haven't mentioned it in the question since I thought it wasn't relevant, but this whole project was "wrapped" inside another nginx to make it public on the internet; basically, the nginx.conf of the outer reverse proxy wasn't handling the socket.io connection correctly, even though everything I had written was ok (as I said, local testing was working fine).

    So, here is the correct location for the socket.io in the nginx.conf of the frontend image:

    # Sources: see 5th point of the list under "What I've tried" section in the question
    location /socket.io {
        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_http_version 1.1;
        proxy_buffering off;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
        proxy_pass http://backend_server/socket.io;
    }
    

    And here is the location that was missing in the outer nginx.conf:

    server {
        listen 443 ssl;
        server_name example.com;
    
        ssl_certificate ...;
        ssl_certificate_key ...;
    
        # I was missing this location
        location /socket.io/ {
            proxy_pass http://localhost:5200/socket.io/;
            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-Proto $scheme;
            proxy_read_timeout 86400;
        }
    
        # ...
    }