The Best Vue Hosting Platforms in 2026
Almost every confusing thing about hosting Vue comes from the fact that 'a Vue app' means two completely different deployment artifacts. A Vite-built SPA compiles to a dist folder of HTML, JS, and CSS — no server, no runtime, nothing to keep alive. A Nuxt app in SSR mode compiles to a Node server that has to be running when a request arrives.
Get that distinction right and hosting is easy. Get it wrong and you either pay for a container that's serving three static files, or you deploy a server to a static host and spend an afternoon working out why every route except the homepage 404s.
Which one do you have?
Check your build output. If npm run build leaves you with a dist directory full of files you could open from disk, you have a static SPA. If it produces .output/server or you're running node .output/server/index.mjs, you have a server.
- Vue 3 + Vite (create-vue default) → static SPA. dist/.
- Nuxt with ssr: false, or nuxt generate → static. Prerendered HTML per route, still just files.
- Nuxt default (SSR) → Node server. Needs a process.
- Nuxt with server routes or Nitro API endpoints → Node server, even if most pages are prerendered.
The hybrid case is the one that trips people: a Nuxt app where 95% of pages are prerendered but there's one /api/contact route. That's a server, and it needs a host that runs one.
If it's a static SPA
You want a CDN and nothing else. There is no advantage to running a container here — a Node process serving static files from disk is strictly worse than a CDN edge serving them from a POP near the user, and it costs money to keep alive.
Cloudflare Pages, Netlify, Vercel, and GitHub Pages all do this well and all have a free tier that's genuinely sufficient for a small SPA. The differences are marginal: Cloudflare's network is the largest, Netlify's build configuration is the most flexible, Vercel's preview deployments are the smoothest, GitHub Pages is the one that needs no extra account.
PandaStack handles this case by not running a VM at all. The build-output classifier detects that a deploy produced static files, uploads them, and the edge serves them straight from object storage — no microVM, no cold start, and no idle cost, because there's nothing running to bill for. If your Vue frontend later grows a backend, it's the same platform and the same bill; until then it's a static site.
{
"type": "vite",
"outputDir": "dist",
"installCommand": "pnpm install",
"buildCommand": "pnpm build"
}The one piece of configuration every SPA host needs is the history-mode rewrite. Vue Router's default mode uses real URLs, so a direct request to /settings must return index.html rather than a 404 — the router then handles it client-side. Every platform has a way to say this and it looks slightly different on each:
# Netlify — _redirects
/* /index.html 200
# Cloudflare Pages — _redirects (same format)
/* /index.html 200
# Vercel — vercel.json
{ "rewrites": [{ "source": "/(.*)", "destination": "/index.html" }] }
# nginx, if you're self-hosting
location / { try_files $uri $uri/ /index.html; }If it's Nuxt SSR
Now you need somewhere to run a Node process, and the roundup looks like any other Node hosting decision.
Vercel and Netlify
Nuxt's Nitro build layer targets both natively — set the preset, or let it detect the environment, and your server routes become serverless functions. For a content site with API routes that do modest work, this is the shortest path and the ergonomics are excellent.
Caveat: it's serverless, with serverless constraints. Cold starts on a route nobody has hit recently, execution time limits, no persistent in-memory state between requests, and no long-lived connections. Fine for most Nuxt sites, wrong if you're holding a WebSocket or running a background timer.
Cloudflare Workers and Pages
Nitro has a Cloudflare preset, and the edge runtime means your SSR runs close to the user with minimal cold start. For a globally-distributed content site this is genuinely hard to beat on latency.
Caveat: the Workers runtime is V8 isolates, not Node. Most things work; the ones that don't tend to be a native dependency or a Node built-in some library reaches for, and you find out at build time or, worse, at runtime on one code path.
Render, Railway, Fly.io, PandaStack
The long-running-process options, which is what you want if Nuxt is the frontend of a real application rather than a content site. A plain node .output/server/index.mjs, no runtime quirks, no cold-start caveats, and anything Node can do works.
Render and Railway are the low-friction picks; Fly.io if you care about regions. PandaStack's angle is what happens when nobody's using the app: it sleeps after an idle window and releases its resources entirely, waking on the next real request. At $0.054 per active vCPU-hour and $0.0162 per working-set GiB-hour, billed only on what you actually burn and hold resident, a staging Nuxt app that sees traffic twice a week costs approximately nothing, which is a different economic shape to a container you rent by the month.
Caveat, uniformly: you're now running a server, so you own the things servers have — a health check that has to pass, memory that has to be enough, and a process that can crash.
The build-time environment variable trap
Vite inlines anything prefixed with VITE_ into the bundle at build time. This has two consequences people learn the hard way.
The first is that changing the variable requires a rebuild, not a restart. Updating VITE_API_URL on your host and redeploying without rebuilding leaves the old value baked into the JavaScript you're serving.
The second is more serious: the value ends up in a file you serve to every visitor. A VITE_-prefixed variable is public, permanently, and no amount of encryption on your host's side changes that.
# Public — inlined into the bundle, visible to anyone with devtools
VITE_API_URL=https://api.example.com
VITE_POSTHOG_KEY=phc_public_key
# NOT public — never give a secret a VITE_ prefix.
# In Nuxt, secrets belong in runtimeConfig (server-only), not runtimeConfig.public
DATABASE_URL=postgres://...Nuxt models this properly: runtimeConfig is server-only and read at runtime, runtimeConfig.public is exposed to the client. Use the distinction rather than reaching for VITE_ out of habit.
Which one
- Static Vue SPA, nothing else → Cloudflare Pages or Netlify. Free, fast, done.
- Static SPA that will grow a backend or a database → a platform that does both, so you're not migrating in six months.
- Nuxt SSR content site, mostly reads → Vercel, Netlify, or Cloudflare via the Nitro preset.
- Nuxt SSR as the frontend of a real application, with server routes doing real work → a long-running Node host: Render, Railway, Fly.io, or PandaStack.
- Nuxt app that's idle most of the time — internal tool, staging, low-traffic product → a host that scales to zero, so you stop paying for the quiet hours.
And the one rule worth repeating: check what your build actually produces before you choose. Half the Vue hosting questions on the internet are someone deploying a server to a static host, or paying for a container to serve a dist folder.
Frequently asked questions
Do I need a server to host a Vue app?
Only if you're using SSR. A Vue 3 app built with Vite compiles to static HTML, JavaScript, and CSS — there is no runtime, so a CDN serves it better and cheaper than any server could. Nuxt in its default SSR mode is different: it produces a Node server that renders pages per request, and that needs a process running. Nuxt can also generate a fully static site with nuxt generate or ssr: false, which puts you back in the CDN case. Check whether your build output is a dist folder or a .output/server directory; that answers the question definitively.
Why does my Vue app 404 on page refresh after deploying?
Because Vue Router's history mode uses real URLs, and your host is looking for a file at that path. A request to /settings hits the server, there's no settings.html on disk, and you get a 404 — even though the app would have handled the route fine client-side. The fix is a catch-all rewrite that returns index.html for any path that isn't a real file, letting the router take over. Every static host supports this: a _redirects file on Netlify and Cloudflare Pages, a rewrites entry in vercel.json, try_files in nginx. It's a one-line change and it's almost always the cause.
Is Nuxt SSR better hosted on serverless or on a long-running server?
It depends on what your server routes do. For a content site — pages rendered from a CMS, a handful of lightweight API endpoints — serverless via Nitro's Vercel, Netlify, or Cloudflare presets is an excellent fit and requires almost no configuration. For an application where Nuxt is the frontend of something substantial, a long-running process is better: no cold starts on unpopular routes, no execution-time ceiling, persistent connections work, and in-memory caching between requests actually caches. The signal is whether your server code needs to remember anything or stay connected to anything.
Are VITE_ environment variables safe for API keys?
No. Anything prefixed with VITE_ is inlined into the JavaScript bundle at build time and shipped to every visitor's browser — it is public by design, and viewing it takes one devtools tab. The prefix is a deliberate opt-in signal meaning 'this value is public'. Publishable keys for analytics or a client-side SDK are fine; a database URL, a service-role key, or any secret is not. In Nuxt, use runtimeConfig for server-only values and runtimeConfig.public only for things you'd be happy printing on the page. If you find a secret behind a VITE_ prefix, rotate it — it's been public since the deploy.
How much should hosting a Vue app cost?
A static SPA should cost nothing until you have real traffic — every major static host has a free tier that covers a small site comfortably, and beyond that you're paying for bandwidth rather than compute. An SSR Nuxt app is where costs appear, because something has to be running. On serverless you pay per invocation, which is cheap at low traffic and worth modelling at high traffic. On a long-running host you pay for the instance whether or not anyone visits — unless the platform scales to zero, in which case an idle app releases its resources and bills only for the seconds it actually served. For staging and internal tools, that difference is most of the bill.
Keep reading
- App hosting on PandaStack — Static output served without a VM; SSR in a microVM
- How to deploy a Nuxt app without Docker
- The best React hosting platforms in 2026
- The best Netlify alternatives in 2026
- How to manage environment variables and secrets
49ms p50 cold start. Fork, snapshot, and scale to zero.