Skip to content

Dynamic ES-module imports lose credentials behind Cloudflare Access / SSO cookie (white screen) #5987

@wegelcloud

Description

@wegelcloud

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

  1. 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).
  2. Put it behind Cloudflare Access with an email-based policy (or any auth-required application).
  3. Log in once, so the browser holds the CF_Authorization cookie.
  4. Open the NiceGUI app → blank page.
  5. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    questionStatus: Needs clarification from the author

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions