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

# Static sites

> Serve HTML, CSS, and JS on Nubo

A static site is plain HTML, CSS, and JavaScript with no server-side code. Landing pages, portfolios, marketing pages, prebuilt SPAs.

Nubo has two paths for static sites:

| If your repo has...                         | Use                                                                                      |
| ------------------------------------------- | ---------------------------------------------------------------------------------------- |
| Just an `index.html` (no `package.json`)    | [Raypacks static pack](#raypacks-static-pack) (this guide)                               |
| A `package.json` (React, Vite, Astro, etc.) | [React guide](/deploy-guides/javascript/react) or [Node.js guide](/deploy-guides/nodejs) |
| A custom nginx config you want to keep      | [Paketo static path](#paketo-with-custom-config) below                                   |

## Raypacks static pack

The Raypacks static pack ships your files behind nginx. No config, no build step.

### What you need

* `index.html` at the repo root **or** under `public/index.html`.
* No `package.json` at the root (that triggers the Node pack instead).

### Deploy

```bash theme={null}
mkdir my-site && cd my-site
cat > index.html <<'EOF'
<!doctype html>
<title>My site</title>
<h1>Hello from Nubo</h1>
EOF
git init && git add . && git commit -m "init"
# push to GitHub, then in Nubo:
```

<Steps>
  <Step title="New Frame">
    Open your Project, pick the Space you want to deploy into, hit **+ Frame**, pick the repo.
  </Step>

  <Step title="Set the port to 80">
    The static pack serves on port 80.
  </Step>

  <Step title="Deploy">
    Build takes a few seconds. Once it's live, visit the Frame's URL.
  </Step>
</Steps>

### What gets served

The entire matching directory (the repo root, or `public/`) is served as-is. CSS files, images, subdirectories: they all work.

### Limits

* No control over nginx (rewrites, redirects, cache headers).
* No directory listings.
* No custom 404 page.

If you need any of that, use the Paketo path below.

## Paketo with custom config

For SPAs that need URL rewrites, sites that need cache headers, or anything that requires nginx/Apache config tuning, switch the Frame's **Builder** to **Paketo** in **Frame Settings**.

### nginx config

Drop a `nginx.conf` or set `BP_WEB_SERVER=nginx` via `project.toml`:

```toml project.toml theme={null}
[project]
id = "static-site"
name = "Static site"
version = "1.0.0"

[[build.env]]
name = "BP_WEB_SERVER"
value = "nginx"

[[build.env]]
name = "BP_WEB_SERVER_ROOT"
value = "."

[[build.env]]
name = "BPE_PORT"
value = "8080"
```

`BP_WEB_SERVER_ROOT` should match the directory containing your `index.html`. `BPE_PORT` sets the runtime port (and the `PORT` env var); use the same value as the Frame's port.

### Apache config

Same shape, but set `BP_WEB_SERVER=httpd`:

```toml project.toml theme={null}
[[build.env]]
name = "BP_WEB_SERVER"
value = "httpd"
```

### Custom configs

If you have your own `nginx.conf` or `httpd.conf` at the repo root, Paketo detects it automatically and uses it instead of the generated default. Just drop the file in.

## SPAs (React, Vite, Astro)

If your repo has a `package.json`, the Raypacks static pack won't pick it up. Two options:

1. **Build locally, ship the output as a static site.** Run `npm run build`, then commit the output directory under `public/` (rename if needed). Now the repo has no `package.json` at the root (move it into a subfolder) and the static pack picks it up.
2. **Let Nubo build and serve.** Use the [React guide](/deploy-guides/javascript/react) which runs `npm run build` then `serve` against the output. Keeps git pushes as the deploy mechanism.

For most apps, option 2 is the right call.
