You open your browser console and see this:

WebSocket connection to 'ws://localhost:25580/' failed: Error during WebSocket handshake: net::ERR_CONNECTION_CLOSED

You 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
working Browser wss:// connection Upgrade: websocket nginx reverse proxy proxy_pass uWSGI gevent + websockets Flask SocketIO gevent_uwsgi without libssl-dev broken Browser wss:// connection nginx forwards upgrade uWSGI no SSL support aborts handshake ERR_CONNECTION_CLOSED browser console uWSGI log: needs SSL support to handshake fixed — add libssl-dev before pip install uwsgi fixed Browser wss:// connection nginx Connection: Upgrade Upgrade: $http_upgrade uWSGI libssl-dev compiled in --http-websockets Flask SocketIO connected ✓ WebSocket connection established

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 Dockerfile

Key points:

  • libssl-dev must be present before pip install uwsgi runs
  • You can safely remove libssl-dev after compilation — the runtime library (libssl3) is already part of the base image
  • gcc is also needed for uWSGI compilation

uWSGI Configuration

uwsgi --http :5000 --gevent 1000 --http-websockets --master --wsgi-file run.py --callable app

The --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 successful

Summary

ComponentConfiguration
DockerfileInstall libssl-dev before pip install
uWSGI--http-websockets --gevent 1000
Flask-SocketIOasync_mode='gevent_uwsgi'
nginxproxy_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.