Skip to content

Warn when the Bokeh session token may exceed proxy request-header limits - #8638

Open
MarcSkovMadsen wants to merge 2 commits into
mainfrom
warn-large-ws-token
Open

Warn when the Bokeh session token may exceed proxy request-header limits#8638
MarcSkovMadsen wants to merge 2 commits into
mainfrom
warn-large-ws-token

Conversation

@MarcSkovMadsen

@MarcSkovMadsen MarcSkovMadsen commented Jun 6, 2026

Copy link
Copy Markdown
Collaborator

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-Protocol header when opening the WebSocket. If that header exceeds the proxy's per-request-header limit (nginx large_client_header_buffers, default 8 KB), the proxy rejects the upgrade with HTTP 400 and the user sees only a cryptic Could 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.get now 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 hit Could not open websocket. Non-fatal; purely diagnostic.

The Bokeh session token for '/app' is 8123 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

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_large verifies 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

@codecov

codecov Bot commented Jun 6, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 69.97%. Comparing base (b2fb6b8) to head (12f7847).
⚠️ Report is 23 commits behind head on main.

❗ There is a different number of reports uploaded between BASE (b2fb6b8) and HEAD (12f7847). Click for more details.

HEAD has 37 uploads less than BASE
Flag BASE (b2fb6b8) HEAD (12f7847)
42 5
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.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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

Copy link
Copy Markdown
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']

@MarcSkovMadsen

Copy link
Copy Markdown
Collaborator Author

Failing UI tests are unrelated to this PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants