You open your browser console and see this:
WebSocket connection to 'ws://localhost:25580/' failed: Error during WebSocket handshake: net::ERR_CONNECTION_CLOSEDYou check your uWSGI logs and find:
you need to build uWSGI with SSL support to use the websocket handshake api function !!!Two errors, one root cause. Here's what's happening and how to fix it.
The Problem
When using flask-socketio with async_mode='gevent_uwsgi', the WebSocket handshake is handled natively by uWSGI. However, if your client connects via wss:// (WebSocket Secure), uWSGI needs SSL support compiled in to perform the handshake.
The browser sees ERR_CONNECTION_CLOSED because uWSGI aborts the upgrade mid-handshake. The connection closes before it's established, which is why the error looks like a network problem rather than a configuration one.
This commonly occurs when:
- Your application is behind a reverse proxy (nginx) that terminates SSL
- You're using Docker with slim Python images that don't include
libssl-dev - You recently upgraded your base image or rebuilt without SSL development headers
The Stack
- Python 3.14 (slim Docker image)
- Flask + Flask-SocketIO
- uWSGI with gevent
- nginx as reverse proxy
- Docker deployment
The Fix
Install libssl-dev before compiling uWSGI so it builds with SSL support.
Dockerfile
FROM python:3.14-slim
WORKDIR /app
RUN apt-get update && apt-get install -y --no-install-recommends \
nginx gcc libffi-dev libwebp-dev zlib1g-dev libjpeg-dev libssl-dev curl \
&& rm -rf /var/lib/apt/lists/* \
&& mkdir -p /run/nginx
COPY requirements.txt .
RUN pip3 install --no-cache-dir -r requirements.txt \
&& apt-get purge -y --auto-remove gcc libffi-dev libssl-dev
# ... rest of your DockerfileKey points:
libssl-devmust be present beforepip install uwsgiruns- You can safely remove
libssl-devafter compilation — the runtime library (libssl3) is already part of the base image gccis also needed for uWSGI compilation
uWSGI Configuration
uwsgi --http :5000 --gevent 1000 --http-websockets --master --wsgi-file run.py --callable appThe --http-websockets flag enables WebSocket support in uWSGI's HTTP router.
Flask-SocketIO Configuration
socketio.init_app(app,
cors_allowed_origins="*",
message_queue=app.config['REDIS_URL'],
async_mode='gevent_uwsgi'
)The async_mode='gevent_uwsgi' tells Flask-SocketIO to use uWSGI's native WebSocket handler instead of trying to use gevent-websocket (which requires gunicorn).
nginx Configuration
location / {
proxy_pass http://127.0.0.1:5000;
proxy_set_header Connection 'Upgrade';
proxy_set_header Upgrade $http_upgrade;
proxy_http_version 1.1;
proxy_buffering off;
proxy_cache off;
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-Proto $http_x_forwarded_proto;
}The critical headers are Connection: Upgrade and Upgrade: $http_upgrade — without these, nginx won't forward the WebSocket upgrade request to uWSGI.
Common Pitfalls
1. Using async_mode='gevent' with uWSGI
If you set async_mode='gevent', Flask-SocketIO will try to use gevent-websocket which requires a gunicorn GeventWebSocketWorker. With uWSGI, you'll get:
RuntimeError: The gevent-websocket server is not configured appropriately.Fix: Use async_mode='gevent_uwsgi' with uWSGI.
2. Segfaults with Python 3.13+ on aarch64
If you see segfaults in gevent_callback_fork on Python 3.13 with ARM64 architecture:
!!! uWSGI process got Segmentation Fault !!!
gevent/libev/corecext.cpython-313-aarch64-linux-gnu.so(gevent_callback_fork+0x20)Fix: Upgrade to Python 3.14 where this is resolved, or downgrade to Python 3.12.
3. Missing --http-websockets flag
Without this flag, uWSGI won't attempt WebSocket upgrades and connections will fail silently or return 500.
Verifying the Fix
After deploying, the browser console error should be gone. In uWSGI logs you should see:
Received request to upgrade to websocket
Upgrade to websocket successfulSummary
| Component | Configuration |
|---|---|
| Dockerfile | Install libssl-dev before pip install |
| uWSGI | --http-websockets --gevent 1000 |
| Flask-SocketIO | async_mode='gevent_uwsgi' |
| nginx | proxy_set_header Upgrade $http_upgrade |
The root cause is simple — uWSGI needs SSL libraries at compile time to support secure WebSocket handshakes. Slim Docker images don't include these by default, so you need to explicitly add them before building.

Member discussion