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

# Host a static site on Bunny Storage

> Deploy static sites and single-page apps to Bunny Storage and serve them through Bunny CDN, with the Bunny CLI or by hand.

A static site on bunny.net is a storage zone that holds your built files, with a pull zone in front that serves them from the CDN. You can set that up with the [Bunny CLI](/docs/cli), which detects your framework, runs the build, and uploads the output, or you can create the zones in the dashboard and upload the files yourself.

## Deploy your site

**Bunny CLI**

> [!WARNING]
>
> `bunny sites` is in [Public Preview](/docs/product-release-stages#stage-2-public-preview)
> and available only in the Bunny CLI. It's in active development, so commands,
> flags, and behaviour may change. Share feedback on
> [Discord](https://discord.gg/bunnynet). The storage zone and pull zone behind
> each site appear in the dashboard under **Storage** and **CDN**.

From your project root:

```bash theme={null}
bunny sites create my-site
bunny sites deploy --build
```

`bunny sites create` makes the storage zone and pull zone and links your directory to the site. `bunny sites deploy --build` detects your framework, runs its build with the package manager from your lockfile (Bun, pnpm, Yarn, or npm), and publishes the output directory. Each deploy is kept, so `bunny sites deployments publish --previous` rolls back to the last one.

Run `bunny sites ci init` to add a GitHub Actions workflow that deploys on every push to `main`. See [`bunny sites`](/docs/cli/commands/sites) for every command.

**Manually**

**1. Build your site**

From your project root, run your framework's build command. The [framework guides](#framework-guides) list the command and output directory for each framework.

**2. Create a storage zone**

In the [bunny.net dashboard](https://dash.bunny.net), go to **Storage** and click **Add Storage Zone**. Give it a name, pick the main region closest to your audience, and click **Add Storage Zone**.

**3. Upload your files**

Open the storage zone's file manager and upload the contents of your output directory to the root of the zone. To script uploads, use the [Storage HTTP API](/docs/storage/http).

**4. Create a pull zone**

Go to **CDN** and click **Add Pull Zone**. Set **Origin Type** to **Storage Zone**, select the zone you created, and click **Add Pull Zone**.

Your site is live at `https://<pull-zone-name>.b-cdn.net`.

## Site configuration

When you deploy with the Bunny CLI and detection doesn't fit your project, add a `sites` block to `bunny.jsonc` at your project root. Every field is optional:

```jsonc bunny.jsonc theme={null}
{
  "sites": {
    "name": "my-site",
    "build": "npm run build",
    "dir": "dist",
    "spa": true
  }
}
```

| Field | Description |
| - | - |
| `name` | The site this directory deploys to |
| `build` | The command `bunny sites deploy --build` runs |
| `dir` | The directory to deploy, relative to `bunny.jsonc` |
| `spa` | Serve `index.html` for unknown paths, for apps with client-side routing |

`bunny sites ci init` reads the same file, so GitHub Actions builds and deploys what you deploy locally.

## Framework guides

Each guide lists the build command and output directory, any config your framework needs for static output, and the steps for both the CLI and a manual deploy.

### JavaScript frameworks

> **[Analog](/docs/storage/static-site-hosting/analog)**

> **[Angular](/docs/storage/static-site-hosting/angular)**

> **[Astro](/docs/storage/static-site-hosting/astro)**

> **[Brunch](/docs/storage/static-site-hosting/brunch)**

> **[Ember](/docs/storage/static-site-hosting/ember)**

> **[Next.js](/docs/storage/static-site-hosting/nextjs)**

> **[Nuxt](/docs/storage/static-site-hosting/nuxt)**

> **[Preact](/docs/storage/static-site-hosting/preact)**

> **[Qwik](/docs/storage/static-site-hosting/qwik)**

> **[React](/docs/storage/static-site-hosting/react)**

> **[React Router](/docs/storage/static-site-hosting/react-router)**

> **[SolidStart](/docs/storage/static-site-hosting/solidstart)**

> **[SvelteKit](/docs/storage/static-site-hosting/sveltekit)**

> **[Vite](/docs/storage/static-site-hosting/vite)**

> **[Vue](/docs/storage/static-site-hosting/vue)**

### Static site generators

> **[Elder.js](/docs/storage/static-site-hosting/elderjs)**

> **[Eleventy](/docs/storage/static-site-hosting/eleventy)**

> **[Gatsby](/docs/storage/static-site-hosting/gatsby)**

> **[Gridsome](/docs/storage/static-site-hosting/gridsome)**

> **[Hexo](/docs/storage/static-site-hosting/hexo)**

> **[Hugo](/docs/storage/static-site-hosting/hugo)**

> **[Jekyll](/docs/storage/static-site-hosting/jekyll)**

> **[Pelican](/docs/storage/static-site-hosting/pelican)**

> **[Zola](/docs/storage/static-site-hosting/zola)**

### Documentation

> **[Docusaurus](/docs/storage/static-site-hosting/docusaurus)**

> **[MkDocs](/docs/storage/static-site-hosting/mkdocs)**

> **[Sphinx](/docs/storage/static-site-hosting/sphinx)**

> **[VitePress](/docs/storage/static-site-hosting/vitepress)**

### Other

> **[Blazor WebAssembly](/docs/storage/static-site-hosting/blazor)**

> **[Static HTML](/docs/storage/static-site-hosting/static-html)**

## Single-page apps

Single-page apps (React, Vue, Angular, and others) handle routing in the browser. When someone opens a deep link such as `/dashboard` or refreshes the page, the request has to return `index.html` so the router can take over.

The Bunny CLI sets this up for frameworks that build single-page apps, and asks when a build looks like one. For a manual deploy, configure your storage zone:

**1. Open your storage zone**

In the [bunny.net dashboard](https://dash.bunny.net), go to **Storage** and select your storage zone.

**2. Open Error handling**

Open the **Error handling** page.

**3. Serve index.html for missing paths**

Enter `/index.html` in the **404 File path** field, check **Rewrite 404 to 200 status code**, and click **Save**.

Requests for missing files with an extension, such as `.js`, `.css`, or images, still return a 404. See [Storage zone settings](/docs/storage/settings#error-handling) for details.

## Add a custom domain

With the Bunny CLI, add the domain to your site:

```bash theme={null}
bunny sites domains add example.com
```

The CLI prints the `CNAME` record to create with your DNS provider. Once the record resolves, issue a free certificate with `bunny sites domains ssl example.com`.

For a manual deploy, add the hostname to your pull zone by following [Custom hostnames](/docs/cdn/custom-hostname).

## Resize images with Bunny Optimizer

[Bunny Optimizer](/docs/optimizer/dynamic-images/overview) resizes images at the edge from query parameters such as `?width=640`, so your build can ship one copy of each image.

`bunny sites create` leaves query strings and Optimizer off, so open the site's pull zone under **CDN → Pull Zones**:

* Turn on [URL Query String](/docs/cdn/vary-cache#url-query-string) vary, so each `?width=` gets its own cache entry.
* Enable [Bunny Optimizer](/docs/optimizer/quickstart), then turn on the Dynamic Image API and automatic WebP under **Optimizer → Settings**. Your build has already minified CSS and JavaScript, so turn Optimizer's minification off.

The [Next.js](/docs/storage/static-site-hosting/nextjs#resize-images-with-bunny-optimizer), [Nuxt](/docs/storage/static-site-hosting/nuxt#resize-images-with-bunny-optimizer), [Astro](/docs/storage/static-site-hosting/astro#resize-images-with-bunny-optimizer), and [Angular](/docs/storage/static-site-hosting/angular#resize-images-with-bunny-optimizer) guides plug Optimizer into each framework's image component. Anywhere else, write the `srcset` yourself:

```html theme={null}
<img
  src="/images/photo.jpg?width=1200"
  sizes="100vw"
  alt="Photo"
/>
```

## Cache configuration

Static frontends benefit from long cache times on assets. Consider these settings in your pull zone:

| File type | Recommended cache time |
| - | - |
| HTML | Short (1 hour) or use cache busting |
| JS/CSS (hashed) | Long (1 year) |
| Images | Long (1 month+) |
| Fonts | Long (1 year) |

> [!NOTE]
>
> Build tools such as Vite add content hashes to filenames (for example,
> `main.a1b2c3.js`), so these files are safe to cache indefinitely.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.