Docs

Everything you need to host a site on fuush.

Getting started

  1. Sign in at app.fuu.sh with your email.
  2. Create a site — pick a name, get your-name.fuu.sh.
  3. Zip your built site (the directory containing index.html) and upload it.

Single-page apps (React, Vue, etc.)

By default we serve static files as-is: a request to /about looks for about or about/index.html on disk. If neither exists, we 404.

React Router, Vue Router, and other client-routed apps need the server to fall back to index.html for any unknown path. Turn on SPA mode on your site page and we’ll do that.

GitHub sync Preview — coming soon

Available to invited accounts while we shake out edge cases. Ask for access.

Mirror a GitHub repo branch into your fuush site. Every push deploys.

  1. On your site’s dashboard, open GitHub and paste the owner/repo (or the full GitHub URL), the branch (default main), and optionally a sub-directory to deploy (e.g. public, dist).
  2. For private repos, paste a fine-grained personal access token with Contents: Read on the repo.
  3. We’ll give you a Payload URL + Secret. On GitHub, open Settings → Webhooks → Add webhook, paste both, set Content-Type to application/json, leave it on the push event, and save.
  4. Done. Every push to the configured branch triggers a sync. You can also click Sync now in the dashboard at any time.

What gets deployed: exactly the files in the repo (or the configured sub-directory). No build step. If your site needs a build (Vite, Next, Hugo), commit the built output to a branch and point fuush at that branch.

Limits: 150 MB compressed tarball, 100 MB uncompressed (same cap as a manual upload). Symlinks are stripped. Each sync produces one new release that you can roll back from Deploy history.

File browser & inline editor Preview — coming soon

Available to invited accounts while we shake out edge cases. Ask for access.

Skip the re-zip cycle for small changes. Every site has a built-in file browser at Files on its dashboard page. From there you can:

Each action creates a new release, so you can roll back from Deploy history just like a zip upload. The editor caps at 1 MB per file — anything bigger is download-only.

Web forms

Collect form submissions on a static site without standing up a backend. Each site has its own endpoint:

POST https://api.fuu.sh/f/<site_id>/<form_name>

<form_name> is anything matching [a-z0-9][a-z0-9_-]{0,63} — pick one per form. Find your site’s ID and the full URL on the site’s Forms page in the dashboard.

Minimal HTML

<form method="POST" action="https://api.fuu.sh/f/<site_id>/contact">
  <!-- Hidden honeypot. Bots fill it; the submission is silently dropped. -->
  <input type="text" name="_fuush_hp" tabindex="-1" autocomplete="off" style="display:none">
  <input type="hidden" name="next" value="https://yoursite.fuu.sh/thanks.html">

  <label>Email <input type="email" name="email" required></label>
  <label>Message <textarea name="message" required></textarea></label>
  <button type="submit">Send</button>
</form>

Custom 404 page

Drop a 404.html into your zip and turn on Custom 404 page in your site’s advanced options. Any path that doesn’t match a file is served from /404.html with HTTP 404.

Redirects and custom headers

Two optional files in your zip control routing and HTTP headers.

_redirects

One rule per line: from to [status]. Status defaults to 302; 200 means a rewrite (serve the destination without changing the URL bar). Splat patterns (/*) capture a tail you can substitute with :splat.

/old-page    /new-page          301
/blog/*      /journal/:splat    301
/api/*       https://api.example.com/:splat  200

Up to 500 rules per site. Lines starting with # are comments.

_headers

Path patterns followed by header lines (indented). /assets/* matches anything under /assets/.

/*
  X-Frame-Options: DENY
  Referrer-Policy: same-origin

/assets/*
  Cache-Control: public, max-age=31536000, immutable

Up to 200 rules per site.

Command-line tool

The fuush CLI deploys a directory in one command and is the recommended way to wire fuush into CI.

Install

If you have Go:

go install fuu.sh/fuush/cmd/fuush@latest

Or grab a pre-built binary from github.com/baramustafa/fuush/releases (linux/macOS, x86_64 + arm64).

Use

fuush login                  # paste a token from app.fuu.sh/tokens
fuush whoami                 # confirm the token works
fuush sites                  # list your sites
fuush deploy your-slug ./dist
fuush deploy your-slug       # current directory

The CLI zips the directory locally (skipping .git, node_modules, .env, .DS_Store), then POSTs through api.fuu.sh. Auth precedence: FUUSH_TOKEN env var wins (use this in CI); otherwise ~/.config/fuush/token from fuush login.

GitHub Actions

Save your token as a repo secret (FUUSH_TOKEN), then drop this into .github/workflows/deploy.yml:

name: Deploy to fuush
on:
  push:
    branches: [main]
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-go@v5
        with: { go-version: '1.23' }
      - run: go install fuu.sh/fuush/cmd/fuush@latest
      - run: fuush deploy your-slug ./public   # ← change to your site slug + build dir
        env:
          FUUSH_TOKEN: ${{ secrets.FUUSH_TOKEN }}

Or — if your build outputs static files already — skip the CLI and use the GitHub sync webhook flow instead. The CLI is the right answer when you need a build step (Vite, Next export, Hugo, mdBook, etc.) before the upload.

Raw HTTP API

Underneath the CLI, every endpoint is a plain HTTP call. Generate an API token at app.fuu.sh/tokens.

GET  https://api.fuu.sh/v1/whoami                  # { user_id, email }
GET  https://api.fuu.sh/v1/sites                   # { sites: [{slug, url, size_bytes, ...}] }
POST https://api.fuu.sh/v1/sites/<slug>/deploy     # multipart/form-data, field "file"

Deploy example:

curl -X POST \
    -H "Authorization: Bearer fuu_..." \
    -F file=@./dist.zip \
    https://api.fuu.sh/v1/sites/your-slug/deploy

Response on success:

{
  "site_id": "...",
  "release_id": "...",
  "site_url": "https://your-slug.fuu.sh",
  "size_bytes": 1234567
}

Common error codes: missing_token, invalid_token, forbidden, not_found, too_large, rate_limited, zip_slip, zip_bomb, banned_extension, zip_empty.

Deploying from Claude / Cursor / any MCP client

fuush ships a Model Context Protocol server at https://api.fuu.sh/mcp so LLM agents can list, create, deploy, and configure sites as native tools. Authenticates with the same API tokens as the curl flow above.

Claude Desktop

Add to ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or the Windows equivalent:

{
  "mcpServers": {
    "fuush": {
      "url": "https://api.fuu.sh/mcp",
      "headers": { "Authorization": "Bearer fuu_..." }
    }
  }
}

Available tools

All MCP requests are rate-limited the same way as /v1. Deployments inherit the 100 MB hard cap and the 10-uploads-per-hour cap. Rolling back from MCP is intentionally disabled for now — use the deploy-history page in the web dashboard until the server-side Spaces copy is wired into the MCP path.

Custom domains

From your site page, add a domain (e.g. www.example.com). We’ll show you a TXT record to publish at _fuush-challenge.www.example.com; this proves you own the domain.

Once verified, add a CNAME: www.example.comyour-slug.fuu.sh. The first HTTPS hit issues a Let’s Encrypt cert automatically (5–10 seconds the first time).

Forgot which email you used?

Visit app.fuu.sh/recover and type in any subdomain or custom domain you own. We’ll send a sign-in link to the email that controls it.

Limits