> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sevalla.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Nuxt

> This guide explains how to deploy a basic Nuxt.js site to Sevalla.

[Nuxt](https://nuxt.com/) is a high-performance, open-source framework built on Vue.js. It provides a powerful environment for building full-stack applications, static sites, and server-rendered experiences with minimal configuration.

Nuxt can be deployed using either Application Hosting (SSR) or Static Site Hosting (SSG). The primary difference lies in how you configure the `nuxt.config.ts` file. If not all pages are prerendered, you should use Application Hosting; otherwise, you should use Static Site Hosting.

## Application Hosting

Choose Application Hosting (Server-Side Rendering) when your application requires server-side logic or dynamic behavior that static files cannot provide. SSR is ideal when your site includes:

* Personalized user experiences, such as dashboards and profiles.
* Real-time or frequently changing data that must be fetched on every request.
* Dynamic routes that cannot be known ahead of time, making static pre-rendering impractical.
* Authentication-dependent content, where access and rendering vary based on the user’s session or permissions.

### Configuration

To configure your Nuxt application for Sevalla, we recommend you follow these best practices:

* Use Nitro’s built-in caching (`defineCachedEventHandler`) for expensive operations.
* Apply Nuxt Route Rules for static generation and edge caching.
* Use Nuxt Image for optimized image delivery.
* Enable compression in your Nitro config.
* Prerender pages that don’t require SSR.

Nuxt requires `runtimeConfig` for handling environment variables, ensuring they are properly injected and kept server-only unless explicitly exposed.

The following is an example `nuxt.config.ts` file for deploying a Nuxt.js app on Sevalla:

```javascript theme={null}
// nuxt.config.ts
export default defineNuxtConfig({
  nitro: {
    preset: 'node-server',
    compressPublicAssets: true,
  }
})
```

### Containerization

#### Dockerfile

The build for [Dockerfiles](https://docs.sevalla.com/applications/build-options/dockerfile) is fully customizable. The following is an example Dockerfile for Nuxt.js:

```javascript theme={null}
FROM node:lts-alpine

WORKDIR /app

COPY package.json package-lock.json* ./

RUN npm ci

COPY . .

RUN npm run build

EXPOSE 3000

ENV PORT=3000
ENV HOST=0.0.0.0

CMD ["npm", "start"]
```

#### Nixpacks

You can customize the [Nixpacks](https://docs.sevalla.com/applications/build-options/nixpacks) build process by defining a `nixpacks.toml` file and using [**Nixpacks-specific environment variables**](https://nixpacks.com/docs/configuration/environment). This allows you to fine-tune how dependencies are installed, how your application is built, and which runtime settings are applied.

The following is an example `nixpacks.toml` configuration:

```javascript theme={null}
# nixpacks.toml

# Override phases
[phases.setup]
nixPkgs = ["nodejs-22_x", "pnpm"]

[phases.install]
cmds = ["pnpm install --frozen-lockfile"]

[phases.build]
cmds = ["pnpm run build"]

# Set start command
[start]
cmd = "node .output/server/index.mjs"

# Environment variables available at build time
[variables]
NODE_ENV = "production"
```

Nixpacks will automatically detect your project’s lock file and select the appropriate package manager during deployment.

Additionally, you can specify the [**Node.js version**](https://nixpacks.com/docs/providers/node) used during the build by setting the `NIXPACKS_NODE_VERSION`[**environment variable**](https://nixpacks.com/docs/providers/node#:~:text=Setting%20the%20NIXPACKS_NODE_VERSION%20environment%20variable).

#### Buildpacks

If you’re using [Buildpacks](https://docs.sevalla.com/applications/build-options/buildpacks), you cannot modify the underlying build phases directly or control dependencies. You must rely on the runtime environment that Buildpacks detects.

You can influence the build process by adjusting the `build` script in your `package.json`. Buildpacks will run whatever command you specify under the build script. For example, the standard command used to compile a Nuxt.js application is:

```javascript theme={null}
 "build": "nuxt build"
```

You can also add additional logic before the build runs, for example:

```javascript theme={null}
"build": "echo \"Hi mom!\" && nuxt build"
```

### CDN

Sevalla provides a premium, Cloudflare-powered CDN for Application Hosting at no additional cost. To get the most out of Sevalla’s CDN when deploying your Nuxt.js application, we recommend the following best practices:

* [Enable the CDN](https://docs.sevalla.com/applications/cdn) for all production applications to ensure global, low-latency delivery.
* Use the `<NuxtImg>` component for automatic image optimization and responsive formats.
* Set appropriate `Cache-Control` headers on API routes to ensure proper caching behavior.
* [Purge CDN cache](https://docs.sevalla.com/applications/cdn#clear-the-cdn-cache) after critical deployments to ensure users receive updated content.
* Rely on Nuxt’s versioned assets in the `_nuxt/ `directory, which are safe to cache indefinitely.
* Store static files in the `public/` directory, allowing the CDN to serve them efficiently.

### Edge caching

[**Edge caching**](https://docs.sevalla.com/applications/edge-caching) stores your Sevalla site cache on Cloudflare’s 260+ global data centers, delivering responses from the location nearest to each visitor for faster performance. To maximize the benefits of Sevalla’s Edge Caching for your Nuxt.js application, we recommend the following best practices:

* Set appropriate `Cache-Control` headers on server-rendered pages to control caching behavior.
* Combine edge caching with the CDN to cover both dynamic and static assets.
* Monitor cache efficiency using the `cf-cache-status` header returned by Cloudflare.

Fully static pages are cached indefinitely by default, providing maximum performance.

Edge caching works best when you combine static and dynamic content with proper cache headers. Below are strategies to optimize your Nuxt.js application on Sevalla for edge caching.

#### `Cache-Control`

With Sevalla’s Cloudflare integration, `Cache-Control` headers are respected at the edge, giving you precise control over how content is cached and served globally. The following are some common directives you can use in your Nuxt.js application:

* `public, s-maxage=3600` - Caches the response on the CDN for 1 hour, improving performance for frequently accessed content.
* `public, max-age=31536000, immutable` - Ideal for versioned static assets (e.g., files in `.output/public/_nuxt/`), allowing them to be cached for up to 1 year with no revalidation.
* `private` - Prevents CDN caching and ensures the response is only cached by the end user’s browser, for personalized or sensitive data.

<Info>
  Sevalla does not yet support the `stale-while-revalidate` Cache-Control directive. To prevent unexpected caching behavior, we recommend not using this directive in your API or asset caching settings.
</Info>

For example:

```javascript theme={null}
// server/api/public-data.ts
export default defineEventHandler(async (event) => {
  const data = await fetchPublicData();

  setResponseHeader(event, "Cache-Control", "public, s-maxage=3600");

  return data;
});
```

#### Server route caching with Nitro

Nuxt.js uses Nitro's caching capabilities for server routes:

```javascript theme={null}
// server/api/products.ts
export default defineCachedEventHandler(
  async (event) => {
    const products = await fetchProducts();

    return products;
  },
  {
    maxAge: 60 * 60, // Cache for 1 hour
    name: "products",
    getKey: () => "all-products",
  }
);
```

**Custom cache with more control**

```javascript theme={null}
// server/api/products.ts
export default defineEventHandler(async (event) => {
  const cached = await useStorage("cache").getItem("products");

  if (cached) {
    return cached;
  }

  const products = await fetchProducts();

  await useStorage("cache").setItem("products", products, {
    ttl: 60 * 60, // 1 hour
  });

  return products;
});
```

#### Route rules for page caching

You can use route rules to cache specific pages:

```javascript theme={null}
// nuxt.config.ts
export default defineNuxtConfig({
  routeRules: {
    // Static page generated at build time
    "/about": { prerender: true },

    // SWR (Stale-While-Revalidate) - serve cached, update in background
    "/blog/**": { swr: 3600 },

    // ISR (Incremental Static Regeneration)
    "/products/**": { isr: 3600 },

    // Disable caching for dynamic content
    "/api/user/**": { cache: false },
  },
});
```

```javascript theme={null}
<!-- pages/blog/[slug].vue -->
<script setup lang="ts">
const route = useRoute();
const slug = route.params.slug as string;

// Data is automatically cached based on route rules
const { data: post } = await useFetch(`/api/posts/${slug}`);
</script>

<template>
  <article>
    <h1>{{ post?.title }}</h1>
    <div v-html="post?.content" />
  </article>
</template>
```

#### Cache Invalidation

Nuxt.js provides cache invalidation through Nitro's storage layer:

**Clear specific cache entry:**

```javascript theme={null}
// server/api/revalidate.post.ts
export default defineEventHandler(async (event) => {
  const { key, secret } = await readBody(event);

  const config = useRuntimeConfig();
  if (secret !== config.revalidationSecret) {
    throw createError({
      statusCode: 401,
      message: "Invalid secret",
    });
  }

  try {
    // Clear specific cache entry
    await useStorage("cache").removeItem(key);

    return { revalidated: true };
  } catch (err) {
    throw createError({
      statusCode: 500,
      message: "Error revalidating",
    });
  }
});
```

**Clear all cache:**

```javascript theme={null}
// server/api/clear-cache.post.ts
export default defineEventHandler(async (event) => {
  const { secret } = await readBody(event);

  const config = useRuntimeConfig();
  if (secret !== config.revalidationSecret) {
    throw createError({
      statusCode: 401,
      message: "Invalid secret",
    });
  }

  try {
    // Clear all cache
    await useStorage("cache").clear();

    return { cleared: true };
  } catch (err) {
    throw createError({
      statusCode: 500,
      message: "Error clearing cache",
    });
  }
});
```

**Programmatic cache invalidation:**

```javascript theme={null}
// server/utils/cache.ts
export async function invalidateProductCache(productId: string) {
  await useStorage("cache").removeItem(`product-${productId}`);
}

export async function invalidateAllProducts() {
  const keys = await useStorage("cache").getKeys("product-");
  await Promise.all(keys.map((key) => useStorage("cache").removeItem(key)));
}
```

### Health checks

Ensure your application remains available during deployments by implementing health checks:

* Always implement [**health checks**](https://docs.sevalla.com/applications/processes#health-checks) for production applications.
* Keep checks lightweight; responses should complete in under 1 second.
* Verify critical dependencies (e.g., databases, Redis) as part of the checks.
* Return 200 for degraded states to allow deployments to continue smoothly.
* Return 503 only for critical failures that require pod restarts.
* Close connections cleanly (e.g., database pools, Redis) to prevent resource leaks.
* Test deployments in staging with monitoring scripts to validate health checks.

**Basic health check**

```javascript theme={null}
// server/api/health.ts
export default defineEventHandler(() => {
  return {
    status: "ok",
    timestamp: new Date().toISOString(),
  };
});
```

### Multi-tenancy

Sevalla fully supports multi-tenancy. You can build multi-tenant Nuxt.js applications using [**wildcard domains**](https://docs.sevalla.com/applications/domains), allowing you to serve multiple tenants from separate subdomains efficiently and securely. Use the following best practices for multi-tenancy on Sevalla with your Nuxt.js application:

* Use wildcard domains to serve subdomain-based tenants (e.g., `tenant1.app.com`).
* Add custom domains individually for tenants who have their own domains.
* Cache tenant data to reduce database queries and improve performance.
* Validate tenant existence before rendering pages to prevent errors or unauthorized access.
* Isolate tenant data in the database using separate schemas or a `tenant_id` column.
* Leverage free SSL certificates, which are automatically provided for both wildcard and custom domains.

#### **Nuxt.js multi-tenant implementation**

Below is a reference architecture for extracting tenant information from the hostname and injecting it into Nuxt's request lifecycle.

**Extract tenant from subdomain using middleware:**

```javascript theme={null}
// server/middleware/tenant.ts
export default defineEventHandler((event) => {
  const host = getRequestHeader(event, "host") || "";
  const subdomain = host.split(".")[0];

  // Skip for main domain
  if (subdomain === "www" || subdomain === "yourdomain") {
    return;
  }

  // Add tenant to event context
  event.context.tenantId = subdomain;
});
```

**Use tenant in API routes:**

```javascript theme={null}
// server/api/tenant-info.ts
export default defineEventHandler(async (event) => {
  const tenantId = event.context.tenantId;

  if (!tenantId) {
    return { error: "No tenant specified" };
  }

  const tenant = await getTenantData(tenantId);

  return {
    tenant: tenant.name,
    subdomain: tenantId,
  };
});
```

**Use tenant in pages:**

```javascript theme={null}
<!-- pages/index.vue -->
<script setup lang="ts">
const { data: tenant } = await useFetch("/api/tenant-info");
</script>

<template>
  <div>
    <h1>Welcome to {{ tenant?.tenant }}</h1>
  </div>
</template>
```

**Tenant-specific data:**

```javascript theme={null}
// server/utils/tenant.ts
import { pool } from "./db";

export async function getTenantData(tenantId: string) {
  const result = await pool.query(
    "SELECT * FROM tenants WHERE subdomain = $1",
    [tenantId]
  );

  return result.rows[0] || null;
}
```

#### Custom domains per tenant

Allow tenants to use their own domains (e.g., `customdomain.com` → tenant data).

**Map custom domains to tenants:**

```javascript theme={null}
// server/middleware/tenant.ts
export default defineEventHandler(async (event) => {
  const host = getRequestHeader(event, "host") || "";
  const tenant = await getTenantByDomain(host);

  event.context.tenantId = tenant?.id || host.split(".")[0];
});
```

## [​](https://docs.sevalla.com/quick-starts/javascript/astro#astro-multi-tenant-implementation)Static Site Hosting

Static Site Generation (SSG) pre-renders all pages as static HTML files at build time, delivering fast and reliable performance. Use SSG when your site includes:

* Content that changes infrequently, such as blog posts, documentation, or product catalogs.
* Pages that can be pre-rendered for all users without personalization.
* Requirements for maximum performance and minimal hosting costs.
* A global audience, as static pages benefit from fast CDN distribution.

### Configuration

Use `nuxt generate` to build and prerender your application using Nitro’s crawler. This command is similar to running `nuxt build` with `nitro.static` enabled (or `nuxt build --prerender`).

During generation, Nuxt builds your site, starts a temporary server, and automatically crawls pages beginning with `/` pre-rendering every reachable route.

```javascript theme={null}
npx nuxt generate
```

After the build completes, you can deploy the contents of `.output/public` to any static hosting provider, or preview the result locally using:

```javascript theme={null}
npx serve .output/public
```

For more information, refer to [Nuxt.js documentation](https://nuxt.com/docs/4.x/getting-started/prerendering#crawl-based-pre-rendering).

### Redirects

To apply custom [redirect rules](https://docs.sevalla.com/static-sites/redirects), add a `_redirects` file containing your redirects to your repository's root directory. Sevalla will automatically parse the file’s contents and apply the custom redirect rules.

### Pretty URLs

In Sevalla, you can enable [Pretty URLs](https://docs.sevalla.com/static-sites/settings#redirects) to standardize your site’s URLs and improve SEO. This feature automatically enforces a trailing slash on the path of static site requests using a 301 redirect.
