Skip to main content

ws-sse-proxy

PyPI version PyPI - Python Version conda-forge License: MIT

A drop-in reverse proxy that transparently translates WebSocket connections to SSE + HTTP POST, for environments where WebSocket is blocked.

The Problem

Some deployment environments block or drop WebSocket connections: AWS ALBs that strip Connection: Upgrade headers, corporate proxies, reverse proxies with misconfigured WebSocket support, or platforms like AWS SageMaker Studio Lab where the gateway kills WebSocket on certain paths.

Your web application works fine on localhost but shows "connecting..." or blank content when deployed behind one of these proxies — because WebSocket never completes.

How It Works

Browser → Broken Proxy → ws-sse-proxy (port 8081) → WebSocket → Your App (port 8080)
           (HTTP only)                                (localhost, works fine)

The proxy:

  1. Passes all HTTP through to your application unchanged
  2. Injects a tiny JavaScript shim into HTML responses that wraps window.WebSocket
  3. The shim tries real WebSocket first — if it works, there's zero overhead
  4. If WebSocket fails (code 1006 or connection stall), it falls back to SSE + POST
  5. The /__wss/events endpoint opens a real WebSocket to your app on localhost and streams messages back as Server-Sent Events
  6. The /__wss/send POST endpoint forwards client messages over the local WebSocket

Your application doesn't need any changes. The proxy handles the translation.

Installation

pip install ws-sse-proxy

Usage

# Your app is running on port 8080
ws-sse-proxy --target-port 8080 --listen-port 8081

Then point your users (or proxy/gateway) at port 8081 instead of 8080.

All Options

ws-sse-proxy --target-port PORT --listen-port PORT [OPTIONS]

Required:
  --target-port PORT     Port your application is listening on
  --listen-port PORT     Port for the proxy to listen on

Optional:
  --target-host HOST     Target host (default: localhost)
  --host HOST            Bind address (default: 0.0.0.0)
  --log-level LEVEL      DEBUG, INFO, WARNING, or ERROR (default: INFO)

As a Python module

python -m ws_sse_proxy --target-port 8080 --listen-port 8081

Programmatic

from ws_sse_proxy.proxy import create_proxy

app = create_proxy(target_port=8080)

import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8081)

Example: marimo on AWS SageMaker

marimo is a reactive Python notebook that requires WebSocket. On SageMaker Studio Lab, the gateway drops WebSocket on proxy paths. With ws-sse-proxy:

# Start marimo
marimo edit --host 0.0.0.0 --port 2718 --no-token --headless &

# Start the proxy in front of it
ws-sse-proxy --target-port 2718 --listen-port 2719

Access marimo at /proxy/2719/ — the proxy translates WebSocket to SSE automatically.

See aws-marimo-sagemaker for a complete setup.

How Detection Works

The injected JavaScript doesn't blindly replace WebSocket. It:

  1. Attempts a real WebSocket connection
  2. Sets a 3-second timeout for stalled connections
  3. If the WebSocket opens then immediately closes with code 1006 (abnormal closure — the signature of a proxy dropping the connection), falls back to SSE
  4. If WebSocket connects normally, uses it with zero overhead

This makes the proxy safe to use everywhere. In environments where WebSocket works, the real WebSocket is used.

Technical Details

The proxy uses /__wss/ as its namespace for shim endpoints, chosen to avoid collisions with application routes:

  • /__wss/events — SSE endpoint (server → client)
  • /__wss/send — POST endpoint (client → server)
  • /__wss/close — POST endpoint (cleanup)

All other paths are proxied to the target application. HTML responses get the JavaScript shim injected before the first <script> tag.

Dependencies: starlette, websockets, httpx, uvicorn — all pure Python, no compiled extensions required.

Development

# Install with test extras (using uv)
uv pip install -e '.[test]'

# Run the test suite
pytest

The suite covers HTTP pass-through, JavaScript shim injection (including gzip-encoded HTML), and the SSE↔WebSocket bridge end-to-end against a live proxy+target stack — text and binary round-trips, query-parameter forwarding, and connection cleanup.

Changelog

0.1.1

  • Fix: query parameters forwarded to the target WebSocket are now percent-encoded. Values containing spaces, &, or = (e.g. auth tokens) previously corrupted the WebSocket handshake.
  • Add: test suite.

0.1.0

  • Initial release.

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ws_sse_proxy-0.1.1.tar.gz (14.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ws_sse_proxy-0.1.1-py3-none-any.whl (11.2 kB view details)

Uploaded Python 3

File details

Details for the file ws_sse_proxy-0.1.1.tar.gz.

File metadata

  • Download URL: ws_sse_proxy-0.1.1.tar.gz
  • Upload date:
  • Size: 14.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for ws_sse_proxy-0.1.1.tar.gz
Algorithm Hash digest
SHA256 5165dc47fca19f13b29f71900d020b8530a5920ab342479634353c5d0abbafb7
MD5 1f113f17e7b826e7b9bd53035cae46d4
BLAKE2b-256 dd18ec243b87cf9d074185a63e5a0335d87b89c64a7ec55a18267a4340f7624b

See more details on using hashes here.

Provenance

The following attestation bundles were made for ws_sse_proxy-0.1.1.tar.gz:

Publisher: publish.yml on scttfrdmn/ws-sse-proxy

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ws_sse_proxy-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: ws_sse_proxy-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 11.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for ws_sse_proxy-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 c2ba3df94dbf2ae12577085c31203c2276d59ec4d4527f4746e77a4630d1bc87
MD5 c1329a8a06d72bddce8aa06ffa0dfc85
BLAKE2b-256 c5435281deb81fb0a4f8e724141ae2816dad7b093f0e032aeceb71a303598e7c

See more details on using hashes here.

Provenance

The following attestation bundles were made for ws_sse_proxy-0.1.1-py3-none-any.whl:

Publisher: publish.yml on scttfrdmn/ws-sse-proxy

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.1.3

2 files

This release

0.1.1 This release

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page