Symptom
NiceGUI apps served behind an SSO layer that uses cookies (e.g. Cloudflare Access, AWS Cognito with cookie mode, Tailscale Funnel with Access) render a blank page. The root HTML comes through fine, but every request to /_nicegui/<version>/components/<hash>/*.js returns a 302 Redirect to the SSO login page instead of the actual JS module.
The browser's Network tab shows:
GET / → 200 (HTML, contains <script type="module" src="/_nicegui/3.9.0/components/.../quasar.umd.prod.js">)
GET /_nicegui/3.9.0/components/<hash>/html.js → 302 → login page (HTML)
- JS fails to evaluate:
Uncaught SyntaxError: Unexpected token '<'
Root cause
Dynamic <script type="module" src=...> and dynamic import() fetches are treated as cross-origin credentialless fetches by default (no cookies sent) unless explicitly marked with crossorigin="use-credentials" or credentials: 'include'.
Cookie-based SSO reverse proxies (CF Access, Cognito cookie mode, Tailscale Funnel with Access policy) require the SSO session cookie on every request — including static JS asset requests from inside the same origin. Without cookies, the proxy responds with a 302 to its login endpoint, which the browser then tries to parse as JS.
Reproduction
- Deploy any NiceGUI 3.x app (3.4+ is fine, this is about module-import-fetch semantics, not a NiceGUI version regression — but 3.9+ makes it worse because more components are lazy-loaded as modules).
- Put it behind Cloudflare Access with an email-based policy (or any auth-required application).
- Log in once, so the browser holds the
CF_Authorization cookie.
- Open the NiceGUI app → blank page.
- DevTools → Network → filter
_nicegui → every .js is 302 to /cdn-cgi/access/login/....
Workaround we're using
Pinned NiceGUI to 3.4.1 where inline-evaluated bootstrap minimizes the module-fetch surface (not a real fix — same class of bug, just less visible). Upgrade to 3.9 is blocked for us.
Suggested fix
Render the script tags with crossorigin="use-credentials":
<script type="module" crossorigin="use-credentials" src="/_nicegui/.../html.js"></script>
And for any internal import() calls, use the options form:
import(/* webpackIgnore: true */ url, { credentials: 'include' })
(when that's supported — or simpler: rely on the same-origin policy and don't add crossorigin attribute at all, but the current behavior seems to add it without use-credentials).
Looking at the source, the generated <script> tags appear to come from the page-template renderer (likely nicegui/templates/index.html or the dynamic router in client.py). A single template-level change + a documentation note would fix the whole class of cookie-SSO deployments (which is the majority of enterprise / internal-tool NiceGUI use cases).
Related
Environment
- NiceGUI 3.9.0 (also reproduced with 3.4.1 for different components)
- Python 3.12
- Browser: Chrome / Safari
- Reverse proxy: Cloudflare Tunnel + Cloudflare Access (cookie-based SSO)
- Hit rate: 100% white-screen for any authenticated user; unauthenticated hits the login wall at
/, so the bug only manifests for returning authenticated users where the browser has the cookie but every module-fetch is still treated as credentialless.
Happy to contribute a PR if you're open to the template change. Let me know the preferred approach — crossorigin="use-credentials" on the generated tags, or documenting the expected reverse-proxy setup.
Symptom
NiceGUI apps served behind an SSO layer that uses cookies (e.g. Cloudflare Access, AWS Cognito with cookie mode, Tailscale Funnel with Access) render a blank page. The root HTML comes through fine, but every request to
/_nicegui/<version>/components/<hash>/*.jsreturns a 302 Redirect to the SSO login page instead of the actual JS module.The browser's Network tab shows:
GET /→ 200 (HTML, contains<script type="module" src="/_nicegui/3.9.0/components/.../quasar.umd.prod.js">)GET /_nicegui/3.9.0/components/<hash>/html.js→ 302 → login page (HTML)Uncaught SyntaxError: Unexpected token '<'Root cause
Dynamic
<script type="module" src=...>and dynamicimport()fetches are treated as cross-origin credentialless fetches by default (no cookies sent) unless explicitly marked withcrossorigin="use-credentials"orcredentials: 'include'.Cookie-based SSO reverse proxies (CF Access, Cognito cookie mode, Tailscale Funnel with Access policy) require the SSO session cookie on every request — including static JS asset requests from inside the same origin. Without cookies, the proxy responds with a 302 to its login endpoint, which the browser then tries to parse as JS.
Reproduction
CF_Authorizationcookie._nicegui→ every.jsis 302 to/cdn-cgi/access/login/....Workaround we're using
Pinned NiceGUI to 3.4.1 where inline-evaluated bootstrap minimizes the module-fetch surface (not a real fix — same class of bug, just less visible). Upgrade to 3.9 is blocked for us.
Suggested fix
Render the script tags with
crossorigin="use-credentials":And for any internal
import()calls, use the options form:(when that's supported — or simpler: rely on the same-origin policy and don't add
crossoriginattribute at all, but the current behavior seems to add it withoutuse-credentials).Looking at the source, the generated
<script>tags appear to come from the page-template renderer (likelynicegui/templates/index.htmlor the dynamic router inclient.py). A single template-level change + a documentation note would fix the whole class of cookie-SSO deployments (which is the majority of enterprise / internal-tool NiceGUI use cases).Related
<script type="module">fetch mode defaults to "same-origin" but credentials default to "omit" unlesscrossoriginattribute is presentEnvironment
/, so the bug only manifests for returning authenticated users where the browser has the cookie but every module-fetch is still treated as credentialless.Happy to contribute a PR if you're open to the template change. Let me know the preferred approach —
crossorigin="use-credentials"on the generated tags, or documenting the expected reverse-proxy setup.