Warn when the Bokeh session token may exceed proxy request-header limits - #8638
Open
MarcSkovMadsen wants to merge 2 commits into
Open
Warn when the Bokeh session token may exceed proxy request-header limits#8638MarcSkovMadsen wants to merge 2 commits into
MarcSkovMadsen wants to merge 2 commits into
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests.
Additional details and impacted files@@ Coverage Diff @@
## main #8638 +/- ##
===========================================
- Coverage 85.89% 69.97% -15.92%
===========================================
Files 350 349 -1
Lines 57169 57192 +23
===========================================
- Hits 49104 40022 -9082
- Misses 8065 17170 +9105 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
The browser echoes the Bokeh session token in the Sec-WebSocket-Protocol request header when opening the WebSocket. Many proxies reject request headers larger than 8 kB (e.g. nginx large_client_header_buffers), which surfaces as a cryptic 'Could not open websocket' (see #8634, #7909). Log a one-time warning per app when the generated session token approaches that size, pointing the operator at `--exclude-cookies`, the proxy header buffer, and the OAuth troubleshooting docs. Refs #8634. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
MarcSkovMadsen
force-pushed
the
warn-large-ws-token
branch
from
June 6, 2026 04:53
4535e70 to
d2c38c5
Compare
Collaborator
Author
|
Manual test script.py"""Manual test for ``panel.io.server._warn_if_ws_token_too_large`` (PR #8638).
Run on the ``warn-large-ws-token`` branch with:
python script.py
It calls the helper directly with controlled token sizes and prints what
happens, so you can eyeball the one-time-per-app warning behaviour without
needing a real oversized OAuth token. Expected:
[1] small token -> no warning
[2] large token /big-app -> WARNING (once)
[3] large token /big-app -> no warning (already warned this path)
[4] large token /other-app -> WARNING (new path)
"""
import logging
import sys
# Make the panel.io.server logger visible on stdout (so warnings interleave in
# order with the print() lines below) regardless of how Panel configures
# logging in this environment.
_logger = logging.getLogger("panel.io.server")
_logger.setLevel(logging.WARNING)
_handler = logging.StreamHandler(sys.stdout)
_handler.setFormatter(logging.Formatter(" >> %(levelname)s %(name)s: %(message)s"))
_logger.addHandler(_handler)
_logger.propagate = False # avoid duplicate lines if a root handler also exists
from panel.io.server import ( # noqa: E402
WS_TOKEN_SIZE_WARNING_THRESHOLD, _warn_if_ws_token_too_large,
_ws_token_size_warned,
)
threshold = WS_TOKEN_SIZE_WARNING_THRESHOLD
big = "x" * (threshold + 1)
small = "x" * 100
# Start from a clean slate in case the module already warned for some path.
_ws_token_size_warned.clear()
print(f"WS_TOKEN_SIZE_WARNING_THRESHOLD = {threshold} bytes\n")
print(f"[1] small token ({len(small)} bytes) on /small-app -> expect NO warning")
_warn_if_ws_token_too_large("/small-app", small)
print(f"\n[2] large token ({len(big)} bytes) on /big-app -> expect ONE warning")
_warn_if_ws_token_too_large("/big-app", big)
print(f"\n[3] large token ({len(big)} bytes) on /big-app again -> expect NO new warning")
_warn_if_ws_token_too_large("/big-app", big)
print(f"\n[4] large token ({len(big)} bytes) on /other-app -> expect a NEW warning")
_warn_if_ws_token_too_large("/other-app", big)
print(f"\nPaths that have warned: {sorted(_ws_token_size_warned)}")$ python script.py
WS_TOKEN_SIZE_WARNING_THRESHOLD = 7600 bytes
[1] small token (100 bytes) on /small-app -> expect NO warning
[2] large token (7601 bytes) on /big-app -> expect ONE warning
>> WARNING panel.io.server: The Bokeh session token for '/big-app' is 7601 bytes. The browser sends this token in the 'Sec-WebSocket-Protocol' header when opening the WebSocket connection. Some proxies reject request headers larger than 8 kB (e.g. nginx 'large_client_header_buffers'); if yours does, the connection will fail with 'Could not open websocket'. This is only a problem if you actually see that error - if the app connects fine you can ignore this message. Otherwise, raise the proxy's request-header buffer or reduce the token size (e.g. `panel serve --exclude-cookies ...`). See https://panel.holoviz.org/how_to/authentication/trouble_shooting.html
[3] large token (7601 bytes) on /big-app again -> expect NO new warning
[4] large token (7601 bytes) on /other-app -> expect a NEW warning
>> WARNING panel.io.server: The Bokeh session token for '/other-app' is 7601 bytes. The browser sends this token in the 'Sec-WebSocket-Protocol' header when opening the WebSocket connection. Some proxies reject request headers larger than 8 kB (e.g. nginx 'large_client_header_buffers'); if yours does, the connection will fail with 'Could not open websocket'. This is only a problem if you actually see that error - if the app connects fine you can ignore this message. Otherwise, raise the proxy's request-header buffer or reduce the token size (e.g. `panel serve --exclude-cookies ...`). See https://panel.holoviz.org/how_to/authentication/trouble_shooting.html
Paths that have warned: ['/big-app', '/other-app'] |
Collaborator
Author
|
Failing UI tests are unrelated to this PR. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Approved in #8634 (comment).
When OAuth is enabled, Bokeh embeds the request's cookies/headers into the session token, which the browser echoes back in the
Sec-WebSocket-Protocolheader when opening the WebSocket. If that header exceeds the proxy's per-request-header limit (nginxlarge_client_header_buffers, default 8 KB), the proxy rejects the upgrade withHTTP 400and the user sees only a crypticCould not open websocket— with nothing in the Panel server logs, because the request never reaches Panel. This was the root cause in #8634 (and #7909) and took the reporter ~2 years to diagnose.Change
DocHandler.getnow logs a one-time, informational warning per app when the generated session token approaches the common 8 KB proxy limit. The message is deliberately conditional — a large token is only a problem if a proxy actually rejects it, so it tells the operator to ignore the message if the app connects fine, and only points at remedies (--exclude-cookies, raising the proxy header buffer, the troubleshooting docs) for the case where they do hitCould not open websocket. Non-fatal; purely diagnostic.The threshold (
WS_TOKEN_SIZE_WARNING_THRESHOLD = 7600) leaves headroom under the 8 KB default for the"bokeh, "subprotocol prefix.Tests
test_warn_if_ws_token_too_largeverifies a small token does not warn, a large token warns, and the warning fires at most once per app path.Refs #8634, #7909.
🤖 Generated with Claude Code