Is there an existing issue for this?
OS/Web Information
-
Web Browser: Google Chrome
-
Local OS: macOS
-
Remote OS: Linux container running on OpenShift
-
Remote Architecture: amd64 / x64
-
Working code-server --version:
1.107.1
8c077bf605a76ebb600d23b71e21c8756a2aee2a
x64
Failing code-server --version:
1.127.0
1e6ed874e3138141a5636f6e0dbe8570aa6cd001
x64
Also reproduced with code-server --version:
1.131.0
a3fc2899bd0fcd388253c0e79ce33b8acd48c688
x64
Streamlit version: 1.60.0
Python versions tested: 3.12.12 and 3.12.13
code-server is started with:
/usr/bin/code-server \
--bind-addr 0.0.0.0:8888 \
--auth none \
--disable-telemetry \
--disable-update-check \
--disable-workspace-trust \
--disable-getting-started-override \
/home/jovyan
The application is exposed through the code-server port proxy.
The proxy URL has the following structure:
https:///notebook///proxy/{{port}}/
The corresponding environment variable is:
VSCODE_PROXY_URI=https:///notebook///proxy/{{port}}/
Steps to Reproduce
Steps to Reproduce
-
Create the following minimal Streamlit application as app.py:
import streamlit as st
st.title("Upload Test")
uploaded_file = st.file_uploader(
"Select CSV or Excel file",
type=["csv", "xlsx", "xls"],
)
if uploaded_file is not None:
st.success("Upload successful")
st.write("Filename:", uploaded_file.name)
st.write("Content-Type:", uploaded_file.type)
st.write("Size:", uploaded_file.size)
-
Install Streamlit 1.60.0.
-
Start the application on port 8501:
streamlit run app.py \
--server.address=0.0.0.0 \
--server.port=8501 \
--server.headless=true
-
Open the application through the code-server port proxy: https://<host>/notebook/<namespace>/<instance>/proxy/8501/
-
Select any CSV or Excel file in the Streamlit file uploader.
-
Observe the failed PUT request to an endpoint similar to:
/notebook/<namespace>/<instance>/proxy/8501/_stcore/upload_file/0
-
As a control test, access the same Streamlit process directly through a Kubernetes port-forward:
kubectl port-forward pod/<pod-name> 8501:8501
-
Open the application directly: http://localhost:8501
-
Repeat the upload.
The upload works when Streamlit is accessed directly and only fails when the request passes through the code-server port proxy.
Expected
The code-server port proxy should forward the _streamlit_xsrf cookie value unchanged.
The backend should receive matching values:
X-Xsrftoken: 2|token-part|signature|timestamp
Cookie: _streamlit_xsrf=2|token-part|signature|timestamp
The Streamlit file upload should complete successfully while XSRF protection remains enabled.
Actual
The upload request fails with:
HTTP 403 Forbidden
XSRF token missing or invalid
With the working code-server version, the backend receives matching values:
X-Xsrftoken:
2|20cfe2b7|98e85396a82b2161c85917b07a434073|1785392786
Cookie:
_streamlit_xsrf=2|20cfe2b7|98e85396a82b2161c85917b07a434073|1785392786
With the failing versions, the X-Xsrftoken header is forwarded unchanged:
X-Xsrftoken:
2|d8d4d4ff|b32ba6c11e6ea28d32288cbee992ec90|1785396144
However, the cookie value received by the backend is percent-encoded:
Cookie:
_streamlit_xsrf=2%7Cd8d4d4ff%7Cb32ba6c11e6ea28d32288cbee992ec90%7C1785396144
%7C is the percent-encoded representation of |.
Streamlit therefore receives two different strings:
Header:
2|d8d4d4ff|b32ba6c11e6ea28d32288cbee992ec90|1785396144
Cookie:
2%7Cd8d4d4ff%7Cb32ba6c11e6ea28d32288cbee992ec90%7C1785396144
Because the XSRF cookie and request header no longer match, Streamlit rejects the request.
The problem also reproduces with code-server 1.131.0.
Logs
Screenshot/Video
No response
Does this bug reproduce in native VS Code?
This cannot be tested in native VS Code
Does this bug reproduce in VS Code web?
I did not test VS Code web
Does this bug reproduce in GitHub Codespaces?
I did not test GitHub Codespaces
Are you accessing code-server over a secure context?
Notes
The following observations helped isolate the problem:
- The same Streamlit
1.60.0 application works through code-server 1.107.1.
- The issue reproduces with code-server
1.127.0.
- The issue still reproduces with code-server
1.131.0.
- The application works when accessed directly through a Kubernetes port-forward.
- The problem is independent of the uploaded file type.
- The problem occurs before CSV or Excel processing begins.
- Setting an explicit Streamlit
server.cookieSecret did not resolve the issue.
- Deleting browser cookies and testing in a private browser session did not resolve the issue.
- The relevant request headers such as
Host, Origin, Referer, X-Forwarded-Host, and X-Forwarded-Proto were otherwise equivalent between working and failing environments.
- No
X-Forwarded-Prefix header was present in either environment.
- A separate deployment of the same minimal Streamlit application behind an Istio
VirtualService with a URI rewrite successfully uploads files.
- This indicates that the issue is specific to the code-server port-proxy path rather than Streamlit or Istio in general.
Disabling Streamlit XSRF protection makes the upload work:
streamlit run app.py --server.enableXsrfProtection=false
This is only a workaround and is not suitable as a permanent solution because it disables a security mechanism.
Could you confirm whether percent-encoding cookie values in the port proxy is intentional?
The following transformation appears to occur:
| -> %7C
Applications such as Streamlit compare the cookie value directly with a corresponding request header, so modifying the cookie value causes the XSRF validation to fail.
Optional: Header echo server used for verification
The following HTTP echo server was used instead of Streamlit to inspect the request received after passing through the code-server proxy:
from http.server import BaseHTTPRequestHandler, HTTPServer
class Handler(BaseHTTPRequestHandler):
def log_request_details(self) -> None:
print("\n" + "=" * 80)
print(f"{self.command} {self.path}")
print("=" * 80)
for name, value in self.headers.items():
if name.lower() == "cookie":
xsrf_cookie = next(
(
part.strip()
for part in value.split(";")
if part.strip().startswith("_streamlit_xsrf=")
),
"<no _streamlit_xsrf cookie>",
)
value = xsrf_cookie
print(f"{name}: {value}")
def do_GET(self) -> None:
self.log_request_details()
body = b"""<!doctype html>
<html>
<head>
<title>Proxy Header Test</title>
</head>
<body>
<h1>Proxy Header Test</h1>
</body>
</html>
"""
self.send_response(200)
self.send_header("Content-Type", "text/html; charset=utf-8")
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
def do_PUT(self) -> None:
self.log_request_details()
content_length = int(self.headers.get("Content-Length", "0"))
if content_length:
self.rfile.read(content_length)
self.send_response(204)
self.end_headers()
HTTPServer(("0.0.0.0", 8501), Handler).serve_forever()
After opening the echo server through the port proxy, the following request was sent from the browser console:
const cookieValue = document.cookie
.split("; ")
.find(cookie => cookie.startsWith("_streamlit_xsrf="))
?.split("=")
.slice(1)
.join("=");
fetch(
window.location.pathname.replace(/\/test$/, "/header-test"),
{
method: "PUT",
headers: {
"Content-Type": "text/plain",
"X-Xsrftoken": decodeURIComponent(cookieValue)
},
body: "upload-test"
}
);
Is there an existing issue for this?
OS/Web Information
Web Browser: Google Chrome
Local OS: macOS
Remote OS: Linux container running on OpenShift
Remote Architecture: amd64 / x64
Working
code-server --version:Failing code-server --version:
Also reproduced with code-server --version:
Streamlit version: 1.60.0
Python versions tested: 3.12.12 and 3.12.13
code-server is started with:
The application is exposed through the code-server port proxy.
The proxy URL has the following structure:
https:///notebook///proxy/{{port}}/
The corresponding environment variable is:
VSCODE_PROXY_URI=https:///notebook///proxy/{{port}}/
Steps to Reproduce
Steps to Reproduce
Create the following minimal Streamlit application as
app.py:Install Streamlit 1.60.0.
Start the application on port 8501:
Open the application through the code-server port proxy:
https://<host>/notebook/<namespace>/<instance>/proxy/8501/Select any CSV or Excel file in the Streamlit file uploader.
Observe the failed PUT request to an endpoint similar to:
/notebook/<namespace>/<instance>/proxy/8501/_stcore/upload_file/0As a control test, access the same Streamlit process directly through a Kubernetes port-forward:
Open the application directly:
http://localhost:8501Repeat the upload.
The upload works when Streamlit is accessed directly and only fails when the request passes through the code-server port proxy.
Expected
The code-server port proxy should forward the
_streamlit_xsrfcookie value unchanged.The backend should receive matching values:
The Streamlit file upload should complete successfully while XSRF protection remains enabled.
Actual
The upload request fails with:
With the working code-server version, the backend receives matching values:
With the failing versions, the X-Xsrftoken header is forwarded unchanged:
However, the cookie value received by the backend is percent-encoded:
%7C is the percent-encoded representation of |.
Streamlit therefore receives two different strings:
Because the XSRF cookie and request header no longer match, Streamlit rejects the request.
The problem also reproduces with code-server 1.131.0.
Logs
Screenshot/Video
No response
Does this bug reproduce in native VS Code?
This cannot be tested in native VS Code
Does this bug reproduce in VS Code web?
I did not test VS Code web
Does this bug reproduce in GitHub Codespaces?
I did not test GitHub Codespaces
Are you accessing code-server over a secure context?
Notes
The following observations helped isolate the problem:
1.60.0application works through code-server1.107.1.1.127.0.1.131.0.server.cookieSecretdid not resolve the issue.Host,Origin,Referer,X-Forwarded-Host, andX-Forwarded-Protowere otherwise equivalent between working and failing environments.X-Forwarded-Prefixheader was present in either environment.VirtualServicewith a URI rewrite successfully uploads files.Disabling Streamlit XSRF protection makes the upload work:
This is only a workaround and is not suitable as a permanent solution because it disables a security mechanism.
Could you confirm whether percent-encoding cookie values in the port proxy is intentional?
The following transformation appears to occur:
| -> %7C
Applications such as Streamlit compare the cookie value directly with a corresponding request header, so modifying the cookie value causes the XSRF validation to fail.
Optional: Header echo server used for verification
The following HTTP echo server was used instead of Streamlit to inspect the request received after passing through the code-server proxy:
After opening the echo server through the port proxy, the following request was sent from the browser console: