Quasar App Builds Fine but Deploys to a Blank Page: A Diagnostic Guide
quasar build succeeds but the deployed app renders a blank page? Diagnose asset publicPath mismatches, history-mode rewrite gaps, boot errors, and stale service workers with ordered checks.
03 Apr 2026, 11:36 UTC

Your quasar build finishes cleanly, dist/ is full of files, you copy them to the server — and the browser shows a white page. The build is almost never the problem. In Quasar v2 SPA deployments, a blank page after deploy nearly always comes down to one of two mismatches: the asset base path doesn't match where the app is actually served, or history-mode routing has no server fallback. This guide walks through recognizing which one you have and fixing the right layer.
Version assumptions: Quasar v2 with @quasar/app-vite or @quasar/app-webpack. Config option names differ slightly between the two — check which app extension is installed (devDependencies in package.json) before editing quasar.config.js.
Recognize the condition before changing anything
Open DevTools on the broken deployment and look at two panels:
- Network: Does
index.htmlload (status 200) while JS/CSS chunk requests return 404? That's an asset-base problem. - Console: Do you see Vue Router warnings about no matching route, or does a refresh of a deep link (
/app/settings) return the server's 404 page instead of your app? That's a history-mode rewrite problem. - Console: Do all assets load with 200s but a red error appears during boot (a thrown exception in a boot file or plugin)? That's a runtime bug, not a deployment config issue — fix the code, not the router.
Cause and diagnostic table
| Symptom | Likely cause | Confirm by |
|---|---|---|
| index.html loads, hashed chunks 404 | build.publicPath doesn't match the served sub-path | Compare the failing request URL prefix to the path the app is deployed under |
| Home page works, refresh on deep route 404s | vueRouterMode: 'history' with no server fallback to index.html | Request a deep route directly with curl; a file server returns 404, a correct setup returns index.html |
| All assets 200, blank page, console exception | Boot file or plugin throws in production (often env-dependent) | Build with sourcemaps and read the stack trace |
| Works in incognito, broken in normal profile | Stale service worker / PWA cache from a previous deploy | Unregister the service worker and hard-reload |
| SSR or PWA checklist applied to wrong output | Mode confusion — SSR bundles have different entry assumptions than SPA | Confirm dist/ output type matches the mode you built (quasar build vs quasar build -m ssr) |
Ordered checks
1. Reproduce locally with the production build
Don't debug against quasar dev — the dev server hides both failure modes. Build and serve dist/spa from the same sub-path you use in production. From the project root, with any static server (example using Node's serve, run as your normal user):
quasar build
npx serve dist/spa -l 5000If the app is deployed under /portal/ in production, test locally under that prefix too, because a root-path local test can pass while production fails.
2. Find the first failing request
In DevTools Network, sort by status and find the first 404. Compare its URL to the files actually emitted in dist/spa. If the browser requests https://example.com/assets/index-abc123.js but your app lives at https://example.com/portal/, the prefix is wrong.
3. Check the configured public path
In quasar.config.js, the build.publicPath value must match the deployed sub-path:
// quasar.config.js
build: {
vueRouterMode: 'history',
publicPath: '/portal/' // app is served at https://example.com/portal/
}Change it, rebuild, redeploy. Note this is baked in at build time — editing it without rebuilding does nothing. If a reverse proxy strips or adds a prefix, the proxy config and publicPath must agree.
4. Test a deep-route refresh
Navigate to a nested route, then refresh. Also test from the server side (run from any machine that can reach the host):
curl -i https://example.com/portal/app/settingsExpected for history mode: HTTP 200 with the contents of index.html. If you get the server's 404 page, add a fallback rewrite. For nginx, inside the location block:
location /portal/ {
try_files $uri $uri/ /portal/index.html;
}For Apache, an .htaccess rewrite that forwards non-file requests to index.html achieves the same. Risk note: a broad fallback can swallow genuine 404s for missing files; scope the rule to the app's path prefix.
5. Separate asset problems from boot failures
If every asset returns 200 and the page is still blank, rebuild with sourcemaps to get a readable stack:
quasar build --debugor enable build.sourcemap in the config. A common production-only boot failure is reading an environment variable that existed in dev but wasn't defined for the build. Fix the thrown error — do not touch router settings for this class of failure.
Fixes tied to findings, and what not to do
- Wrong prefix found in step 2/3: correct
build.publicPath(and any reverse-proxy base) and rebuild. This is a build-time value; redeploy the newdist/. - Deep-route 404 in step 4: add the history fallback at the hosting layer. Resist switching
vueRouterModeto'hash'as a quick fix — it masks the server misconfiguration and changes every shareable URL to the/#/form. - Boot exception in step 5: fix the code path that throws. Verify env variables are set for the build, not just the dev shell.
- Stale-cache suspicion: if the app is a PWA, an old service worker can keep serving a broken deployment. Test in a clean profile or with cache disabled and the service worker unregistered (DevTools → Application → Service Workers → Unregister) before concluding the new build is broken.
Verify the fix
- Serve the fresh
dist/spalocally under the production sub-path: no asset 404s in Network. curl -ia deep route: it returnsindex.htmlwith status 200.- Load the deployed site in an incognito window, navigate to a deep route, refresh: the app renders.
- If PWA is enabled, confirm the new service worker activates and old cached chunks are replaced.
When to escalate
Escalate beyond config-level fixes when: assets load and no console error appears but the page stays blank (possible hydration or CSP issue — check Content-Security-Policy headers blocking inline scripts); the failure only occurs behind a corporate proxy you don't control (get the exact response headers and body for the failing request before filing); or you're running SSR, where this SPA checklist does not apply and the server bundle's logs are the starting point. In each case, capture the first failing request URL, its response, and the console output — those three artifacts distinguish every cause above.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.