# Assets

Nitro supports two types of assets: **public assets** served directly to clients and **server assets** bundled into the server for programmatic access.

## Public Assets

Nitro handles assets via the `public/` directory.

All assets in `public/` directory will be automatically served. This means that you can access them directly from the browser without any special configuration.

```md
public/
  image.png     <-- /image.png
  video.mp4     <-- /video.mp4
  robots.txt    <-- /robots.txt
```

### Caching and Headers

Public assets are served with automatic `ETag` and `Last-Modified` headers for conditional requests. When the client sends `If-None-Match` or `If-Modified-Since` headers, Nitro returns a `304 Not Modified` response.

For assets served from a non-root `baseURL` (such as `/build/`), Nitro prevents fallthrough to application handlers. If a request matches a public asset base but the file is not found, a `404` is returned immediately.

### Production Public Assets

When building your Nitro app, the `public/` directory will be copied to `.output/public/` and a manifest with metadata will be created and embedded in the server bundle.

```json
{
  "/image.png": {
    "type": "image/png",
    "etag": "\"4a0c-6utWq0Kbk5OqDmksYCa9XV8irnM\"",
    "mtime": "2023-03-04T21:39:45.086Z",
    "size": 18956
  },
  "/robots.txt": {
    "type": "text/plain; charset=utf-8",
    "etag": "\"8-hMqyDrA8fJ0R904zgEPs3L55Jls\"",
    "mtime": "2023-03-04T21:39:45.086Z",
    "size": 8
  },
  "/video.mp4": {
    "type": "video/mp4",
    "etag": "\"9b943-4UwfQXKUjPCesGPr6J5j7GzNYGU\"",
    "mtime": "2023-03-04T21:39:45.085Z",
    "size": 637251
  }
}
```

This allows Nitro to know the public assets without scanning the directory, giving high performance with caching headers.

### Custom Public Asset Directories

You can configure additional public asset directories using the `publicAssets` config option. Each entry supports the following properties:

- `dir` -- Path to the directory (resolved relative to `rootDir`).
- `baseURL` -- URL prefix for serving assets (default: `"/"`).
- `maxAge` -- Cache `max-age` in seconds. When set, a `Cache-Control: public, max-age=<value>, immutable` header is applied via route rules.
- `fallthrough` -- Whether requests should fall through to application handlers when the asset is not found. Top-level (`baseURL: "/"`) directories default to `true`; non-root directories default to `false`.
- `ignore` -- Pass `false` to disable ignore patterns, or an array of glob patterns to override the global `ignore` option.

```js [nitro.config.ts]
import { defineConfig } from "nitro";

export default defineConfig({
  publicAssets: [
    {
      baseURL: "build",
      dir: "public/build",
      maxAge: 3600,
    },
  ],
});
```
In this example, files in `public/build/` are served under `/build/` with a one-hour cache and no fallthrough to application handlers.

### Compressed Public Assets

Nitro can generate pre-compressed versions of your public assets during the build. When a client sends an `Accept-Encoding` header, the server will serve the compressed version if available. Supported encodings are gzip (`.gz`), brotli (`.br`), and zstd (`.zst`).

Set `compressPublicAssets: true` to enable all encodings:

```js [nitro.config.ts]
import { defineConfig } from "nitro";

export default defineConfig({
  compressPublicAssets: true,
});
```

Or pick specific encodings:

```js [nitro.config.ts]
import { defineConfig } from "nitro";

export default defineConfig({
  compressPublicAssets: {
    gzip: true,
    brotli: true,
    zstd: false,
  },
});
```

> [!NOTE]
> Only compressible MIME types (text, JavaScript, JSON, XML, WASM, fonts, SVG, etc.) with a file size of at least 1 KB are compressed. Source map files (`.map`) are excluded.

## Server Assets

All assets in `assets/` directory will be added to the server bundle. After building your application, you can find them in the `.output/server/chunks/raw/` directory. Be careful with the size of your assets, as they will be bundled with the server bundle.

> [!TIP]
> Unless using `useStorage()`, assets won't be included in the server bundle.

They can be addressed by the `assets:server` mount point using the [storage layer](/docs/storage).

For example, you could store a json file in `assets/data.json` and retrieve it in your handler:

```js
import { defineHandler } from "nitro";

export default defineHandler(async () => {
  const data = await useStorage("assets:server").get("data.json");

  return data;
});
```

### Custom Server Assets

In order to add assets from a custom directory, you will need to define a path in your nitro config. This allows you to add assets from a directory outside of the `assets/` directory.

Each entry in `serverAssets` supports the following properties:

- `baseName` -- Name used as the storage mount point (accessed via `assets:<baseName>`).
- `dir` -- Path to the directory (resolved relative to `rootDir`).
- `pattern` -- Glob pattern for file inclusion (default: `"**/*"`).
- `ignore` -- Array of glob patterns to exclude files.

```js [nitro.config.ts]
import { defineConfig } from "nitro";

export default defineConfig({
  serverAssets: [
    {
      baseName: "templates",
      dir: "./templates",
    },
  ],
});
```
Then you can use the `assets:templates` base to retrieve your assets.

```ts [handlers/success.ts]
import { defineHandler } from "nitro";

export default defineHandler(async (event) => {
  const html = await useStorage("assets:templates").get("success.html");

  return html;
});
```

> [!TIP]
> During development, server assets are read directly from the filesystem using the `fs` unstorage driver. In production, they are bundled into the server as lazy imports with pre-computed metadata (MIME type, ETag, modification time).
