Deploy Nuxt.js Without Guessing the Hosting Model

Indigo server core branching to CDN, Node.js, and edge networks.

A Nuxt application can run as static files, a Node.js server, or a serverless and edge deployment. The correct option depends on how your pages get data and whether they need server-side code.

To deploy Nuxt.js reliably, choose the rendering model first. Then configure Nitro, build locally, set runtime variables, and verify the production output before connecting a hosting provider.

CHOOSE THE RENDERING MODEL FIRST

Nuxt uses Nitro as its server engine. Nitro builds the application for different targets without forcing you to use one hosting company.

The main deployment choices are:

  • Static generation creates HTML files that a CDN or static host can serve.
  • SSR, also called universal rendering, runs the application server for each request.
  • Hybrid rendering applies different rules to different routes.
  • SPA mode sends a client-side application without server-rendered HTML.

The official Nuxt deployment guide covers these targets and the related Nitro presets.

USE STATIC GENERATION FOR CONTENT-LED SITES

Static generation fits documentation, marketing pages, blogs, portfolios, and other routes that can be built ahead of time.

The build process visits the routes you configure and writes HTML, JavaScript, CSS, and assets to the generated output. A static host doesn’t need to run Node.js for every request.

Static hosting is a poor fit when a page needs a private token at request time, a user session, or server-side access to a database. You can still call a separate API from the browser, but you must protect that API independently.

USE SSR WHEN REQUEST DATA MATTERS

SSR is the default Nuxt approach in current Nuxt 3 and Nuxt 4 documentation. The server renders a response when a user requests a route.

Use SSR when pages depend on request headers, authentication, personalization, server-only credentials, or data that shouldn’t be bundled into static files.

SSR requires a runtime. That runtime can be a Node server, a serverless function, or an edge worker, depending on the Nitro target and hosting platform.

USE HYBRID RENDERING FOR MIXED APPLICATIONS

Hybrid rendering is useful when a site has both public content and dynamic application routes. A blog can be prerendered while account pages remain server-rendered.

Nuxt route rules let you assign behavior by path. The Nuxt rendering documentation explains how route-level rendering and caching work.

Hybrid rendering still needs a Nitro runtime for routes that use SSR, server handlers, caching, or server-side data. A plain static host cannot execute those routes.

PREPARE THE NUXT PROJECT

Start with a clean production build. Use the package manager and Node version already defined by the project. If the repository has an .nvmrc, package.json engine field, or CI configuration, follow that version instead of selecting a different one for deployment.

Install dependencies and run the production build:

npm ci
npm run build

Your package.json normally contains a script like this:

{
  "scripts": {
    "build": "nuxt build",
    "dev": "nuxt dev",
    "generate": "nuxt generate",
    "preview": "nuxt preview"
  }
}

The exact scripts can differ between projects. Check them before running a command copied from another repository.

TEST THE PRODUCTION BUILD LOCALLY

A successful development server doesn’t prove that the production build works. Run the built application locally before deploying it.

For a standard Nitro build, use:

npm run build
node .output/server/index.mjs

Then open http://localhost:3000.

Nuxt’s generated server uses port 3000 by default. You can provide another port through PORT or NITRO_PORT. The host can be set through HOST or NITRO_HOST.

Stop here if the application fails locally. Fix missing imports, server-only code used in the browser, invalid route rules, and missing environment variables before adding a hosting provider.

DEPLOY NUXT.JS AS A STATIC SITE

Use nuxt generate when you want pre-rendered files for static hosting:

npm run generate

You can also run the command directly:

npx nuxt generate

Nuxt generates the static output under .output/public. Upload that directory, or configure your hosting provider to publish it as the deployment directory.

The generated site needs correct handling for client-side navigation. Most static hosts support fallback behavior that sends unknown routes to the application entry point. Configure the fallback according to the host and the router strategy used by your application.

CHECK WHICH ROUTES ARE GENERATED

Nuxt can’t generate a page it doesn’t know how to reach. Static routes that exist in your code are usually discovered during the build. Dynamic routes may need explicit route generation.

For a route such as /products/[slug], provide the available slugs through your data and route-generation setup. Then inspect the generated directory and test several real URLs.

A static deployment can appear healthy while a dynamic page returns a 404 after a direct refresh. Test both navigation inside the site and direct requests to generated URLs.

KNOW THE STATIC LIMITS

Static generation freezes page output at build time. A price, inventory value, dashboard result, or personalized greeting won’t update on the server unless the browser calls an API after loading.

Don’t place private credentials in static code. Anything included in browser JavaScript or generated HTML can be inspected by a visitor. Use a server-side API or a separate backend for protected operations.

DEPLOY NUXT.JS WITH A NODE SERVER

Use the Node preset when you control a VM, container, managed Node service, or conventional application server.

Set the preset in nuxt.config.ts:

export default defineNuxtConfig({
  nitro: {
    preset: 'node-server'
  }
})

You can also choose it during the build:

NITRO_PRESET=node-server npm run build

The production entry point is:

.output/server/index.mjs

Start it with:

NODE_ENV=production node .output/server/index.mjs

The Nuxt 3 deployment documentation shows the same core Node deployment flow. Nuxt 3 and Nuxt 4 use different documentation structures, but these commands remain aligned. Check the documentation version that matches your project before changing configuration.

CONFIGURE THE SERVER PROCESS

Your hosting service must keep the Node process running and route public traffic to it. A process manager, container platform, or managed application service can handle restarts and health checks.

Set the port through the host’s environment settings:

PORT=8080 NODE_ENV=production node .output/server/index.mjs

Don’t assume port 3000 is available on a managed platform. Many providers inject their own PORT value.

Place TLS termination at the reverse proxy or hosting layer in most production setups. Nitro supports certificate variables, but the Nuxt documentation notes that direct Nitro HTTPS is mainly useful for testing. A proxy such as nginx or a managed load balancer normally handles certificates.

USE SERVERLESS OR EDGE HOSTING

Nuxt can build for serverless functions and edge runtimes through Nitro presets. Many providers detect Nuxt automatically. Others require a project setting or a build-time NITRO_PRESET value.

The Nuxt server documentation lists supported deployment targets, including Node servers, Cloudflare Workers, Netlify Functions, and Vercel.

Choose serverless when the platform should create and scale request handlers for you. This works well for APIs, SSR pages with variable traffic, and applications that don’t need one continuously running process.

Choose edge hosting when request latency and regional execution matter, and your code is compatible with the edge runtime.

CHECK EDGE COMPATIBILITY

Edge runtimes don’t provide every Node.js API. Code that depends on native modules, unrestricted filesystem access, child processes, or long-running connections may fail after an edge build.

Review server routes, plugins, and dependencies before selecting an edge preset. A package that works in local Node.js can still use an unsupported API in a worker environment.

Keep server functions short and stateless unless the platform documents another model. Store persistent data in an external database, object store, or approved service. Don’t rely on files written to the deployment filesystem surviving between requests.

CONFIGURE ENVIRONMENT VARIABLES SAFELY

Nuxt separates runtime configuration into private values and public values. Define the structure in nuxt.config.ts:

export default defineNuxtConfig({
  runtimeConfig: {
    apiSecret: '',
    public: {
      apiBase: ''
    }
  }
})

Read the values in application code with useRuntimeConfig():

const config = useRuntimeConfig()


const response = await $fetch(`${config.public.apiBase}/products`, {
  headers: {
    Authorization: `Bearer ${config.apiSecret}`
  }
})

apiSecret is server-only because it sits outside runtimeConfig.public. apiBase is public and can be exposed to browser code.

The Nuxt runtime configuration guide documents this boundary and the environment-variable override pattern.

NEVER EXPOSE PRIVATE KEYS

Don’t put passwords, database credentials, private API tokens, or signing keys inside runtimeConfig.public. Don’t hard-code them in Vue components. Don’t commit them in .env files.

A public value can reach browser JavaScript. Once it reaches the browser, users can inspect it.

Nuxt uses NUXT_ environment variables to override runtime configuration at deployment time. For the example above, a host can provide values similar to:

NUXT_API_SECRET=replace-with-server-secret
NUXT_PUBLIC_API_BASE=https://api.example.com

Set these variables in the hosting provider’s secret or environment settings. Don’t assume the production server will read your local .env file.

The Nuxt configuration documentation also distinguishes runtime configuration from public build-time settings. Use app.config for public values that belong in the application bundle and don’t need to stay secret.

ADD ROUTE RULES FOR HYBRID DEPLOYMENTS

Route rules let you assign different behavior to different URL patterns. A basic example looks like this:

export default defineNuxtConfig({
  routeRules: {
    '/': { prerender: true },
    '/blog/**': { prerender: true },
    '/account/**': { ssr: true },
    '/api/**': { cors: true }
  }
})

Use prerender: true for stable public routes. Keep account and server routes dynamic. Add caching rules only after you understand how often the underlying data changes.

A cached response can outlive an update in your database. Treat cache duration as a data-consistency decision, not only a performance setting.

Route rules require a compatible Nitro deployment. If you run nuxt generate and upload only .output/public, server-side route rules won’t create a working backend. Build and deploy the server-capable target instead.

VERIFY THE DEPLOYMENT BEFORE LAUNCH

Use a production checklist after the first deployment. Test the application through the public domain, not only through a local preview.

Check these paths:

  • The home page loads with JavaScript disabled or delayed.
  • Direct requests to dynamic routes return the correct status.
  • Client-side navigation works after the first page load.
  • API routes reject missing or invalid credentials.
  • Public runtime values appear where expected.
  • Private values never appear in page source or browser requests.
  • Forms work through the production domain.
  • Static assets use the expected base URL.
  • Error pages return useful HTTP status codes.
  • Logs show server errors without printing secrets.

Also test a fresh deployment after changing environment variables. Some platforms inject variables only during the build, while others provide them when the server starts. The difference matters for static output and runtime configuration.

COMMON DEPLOYMENT FAILURES

A blank page often comes from an incorrect asset base URL, a failed client-side JavaScript request, or a deployment that published the wrong directory.

A direct-route 404 usually means the static host lacks fallback configuration or the dynamic route wasn’t generated.

A secret that appears as undefined usually means the variable name doesn’t match the runtime configuration path, the variable wasn’t added to the production environment, or the value was only available during a local build.

An SSR deployment that works locally but fails on a provider often depends on a Node API unavailable in the selected serverless or edge runtime. Review the generated server logs and test the provider’s required Nitro preset.

Exact settings vary by Nuxt version, package manager, build adapter, and hosting platform. Use the current Nuxt documentation for your version, then check the provider’s deployment requirements.

CONCLUSION

To deploy Nuxt.js correctly, match the hosting model to the application. Use nuxt generate for a site that can be pre-rendered, nuxt build with a server-capable target for SSR, and route rules when different pages need different behavior.

Build locally, test the generated output, keep private configuration outside runtimeConfig.public, and verify direct routes after deployment. The hosting provider can change, but the deployment decision still starts with how each route renders.

Leave a Reply

Your email address will not be published. Required fields are marked *

Verified by MonsterInsights