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
}
}
}
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.
Here is a list of what I've tried (note: in this section I'm running the services in the docker compose file):
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
.
Tried using the api location ("/api") as the url for the websocket.
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.
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;
}
(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 "-" "-"
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;
}
# ...
}