Summary
Version 16.1 introduced a change that blocks non-ASCII values in Headers (changelog: "Blocked non-ASCII values in Headers", July 9, 2026 — https://websockets.readthedocs.io/en/stable/project/changelog.html#id2). This breaks legitimate, previously-working setups where header values legitimately contain UTF-8 characters, with no opt-out and (as far as I can tell) no mention in the migration guide.
Cross-posting this per a maintainer's suggestion on streamlit/streamlit#16030: "This looks like an intended change by websockets so there might be a valid reason to exclude non-ASCII header. It would be good if you report this to the python-websockets/websockets repo as well to get more clarity on why this was done." I fully expect there's a good reason for this — mainly looking for clarity on what it is, and whether there's a recommended migration path for the use case below.
Use case that broke
A common reverse-proxy authentication pattern (e.g. Authelia forward-auth behind nginx/Traefik) injects headers such as Remote-Name or Remote-User containing a user's display name before forwarding the request to the backend application. These names routinely contain non-ASCII characters (accents, non-Latin scripts, etc.).
This pattern is used by, at minimum, Streamlit, which depends on websockets for its WebSocket support. After upgrading to websockets 16.1, the WebSocket handshake fails outright for any user whose proxy-injected header value isn't pure ASCII, with no clear error surfaced to the end user (connection just fails).
Question / request
-
Is this restriction motivated purely by the HTTP spec (RFC 7230 requires header field values to be token / quoted-string in printable US-ASCII, technically excluding raw UTF-8), or is there also a security rationale (e.g. request smuggling, header injection)? The changelog entry doesn't explain the reasoning, which makes it hard to know whether this is a hard security boundary or a strictness/spec-compliance choice.
-
If it's spec-compliance rather than a hard security requirement, would you consider:
- documenting this as a breaking change rather than filing it under "Improvements" in the changelog (it silently breaks existing deployments on a pip install -U, since most consumers depend on websockets with a >= constraint), and/or
- providing an opt-in/opt-out flag for strict ASCII validation
- If this is intentional and non-negotiable for spec-compliance/security reasons, could the migration guide / docs be updated with guidance for the reverse-proxy + non-ASCII-header case (e.g. recommend percent-encoding or base64-encoding such headers), since this seems like it will affect a fair number of users running behind auth proxies?
Environment
websockets version: 16.1
Python version: 3.12
Observed via: Streamlit (streamlit/streamlit), which pins websockets>=12.0.0,<17 and therefore can resolve to 16.1
Additional context
Happy to provide a minimal reproduction (a WebSocket handshake with a header containing e.g. "Maël") if useful.
Summary
Version 16.1 introduced a change that blocks non-ASCII values in Headers (changelog: "Blocked non-ASCII values in Headers", July 9, 2026 — https://websockets.readthedocs.io/en/stable/project/changelog.html#id2). This breaks legitimate, previously-working setups where header values legitimately contain UTF-8 characters, with no opt-out and (as far as I can tell) no mention in the migration guide.
Cross-posting this per a maintainer's suggestion on streamlit/streamlit#16030: "This looks like an intended change by websockets so there might be a valid reason to exclude non-ASCII header. It would be good if you report this to the python-websockets/websockets repo as well to get more clarity on why this was done." I fully expect there's a good reason for this — mainly looking for clarity on what it is, and whether there's a recommended migration path for the use case below.
Use case that broke
A common reverse-proxy authentication pattern (e.g. Authelia forward-auth behind nginx/Traefik) injects headers such as Remote-Name or Remote-User containing a user's display name before forwarding the request to the backend application. These names routinely contain non-ASCII characters (accents, non-Latin scripts, etc.).
This pattern is used by, at minimum, Streamlit, which depends on websockets for its WebSocket support. After upgrading to websockets 16.1, the WebSocket handshake fails outright for any user whose proxy-injected header value isn't pure ASCII, with no clear error surfaced to the end user (connection just fails).
Question / request
Is this restriction motivated purely by the HTTP spec (RFC 7230 requires header field values to be token / quoted-string in printable US-ASCII, technically excluding raw UTF-8), or is there also a security rationale (e.g. request smuggling, header injection)? The changelog entry doesn't explain the reasoning, which makes it hard to know whether this is a hard security boundary or a strictness/spec-compliance choice.
If it's spec-compliance rather than a hard security requirement, would you consider:
Environment
websockets version: 16.1
Python version: 3.12
Observed via: Streamlit (streamlit/streamlit), which pins websockets>=12.0.0,<17 and therefore can resolve to 16.1
Additional context
Happy to provide a minimal reproduction (a WebSocket handshake with a header containing e.g. "Maël") if useful.