Skip to main content

Host a public website

A static website (marketing page, documentation, single-page app, portfolio) can run entirely on Filebase: upload the build artifacts to a public bucket, set sensible Cache-Control headers, and the Filebase CDN serves them globally with edge caching.

Step 1 — Create a public bucket

aws --endpoint https://s3.filebase.io s3api create-bucket \
--bucket my-marketing-site \
--acl public-read

Step 2 — Upload the build

For most static-site generators (Vite, webpack, Astro, Next.js export, Hugo, etc.), build into a dist/ directory and sync:

# Long-cache hashed assets
aws --endpoint https://s3.filebase.io s3 sync ./dist/ s3://my-marketing-site/ \
--acl public-read \
--cache-control "public, max-age=31536000, immutable" \
--exclude "*.html" \
--exclude "*.xml"

# Short-cache HTML and feeds
aws --endpoint https://s3.filebase.io s3 sync ./dist/ s3://my-marketing-site/ \
--acl public-read \
--cache-control "public, max-age=300, must-revalidate" \
--exclude "*" \
--include "*.html" \
--include "*.xml"

The two-pass sync gives hashed assets aggressive caching while keeping HTML responsive to deploys.

Step 3 — Serve from the bucket URL

Your site is now live at:

https://my-marketing-site.s3.filebase.io/index.html

The Filebase CDN is in front automatically — popular pages serve from edge points-of-presence.

Step 4 — Custom domain

To serve via your own domain (e.g. example.com):

  1. Create a CNAME record at your DNS provider:
    www.example.com. CNAME my-marketing-site.s3.filebase.io.
  2. The Filebase CDN auto-provisions a TLS certificate for the CNAME.
  3. Visit https://www.example.com/ — your site loads via the custom domain.

For root domains (example.com), an ALIAS / ANAME record is required, which depends on your DNS provider's capabilities. Cloudflare, Route 53, and most modern DNS providers support root-domain CNAMEs.

Step 5 — Index and 404 fallback

The Filebase CDN serves index.html as the directory index. Requests to https://example.com/ return https://example.com/index.html.

For 404s, upload a 404.html to the bucket and the CDN serves it for unmatched paths.

For SPAs that use client-side routing, upload your index.html as 404.html so unmatched paths fall through to your SPA bootstrap.

CI/CD example (GitHub Actions)

.github/workflows/deploy.yml
name: Deploy site
on:
push:
branches: [main]

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'

- run: npm ci
- run: npm run build

- uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.FILEBASE_KEY }}
aws-secret-access-key: ${{ secrets.FILEBASE_SECRET }}
aws-region: auto

- name: Sync to Filebase
run: |
aws --endpoint https://s3.filebase.io s3 sync ./dist/ s3://my-marketing-site/ \
--acl public-read \
--cache-control "public, max-age=31536000, immutable" \
--exclude "*.html"

aws --endpoint https://s3.filebase.io s3 sync ./dist/ s3://my-marketing-site/ \
--acl public-read \
--cache-control "public, max-age=300, must-revalidate" \
--exclude "*" --include "*.html"

Add FILEBASE_KEY and FILEBASE_SECRET to the repo's GitHub Actions secrets.

Atomic deploys via content-hashed filenames

Modern bundlers (Vite, webpack 5+, Rollup) emit hashed filenames like app.a3f9d2.js. Combined with the long Cache-Control above:

  • New deploys upload new hashed files (no overwrite).
  • Old hashed files keep working until cache expiry.
  • The HTML (short cache) updates within minutes.

This gives you instant rollback (re-deploy the old commit), zero-downtime deploys, and aggressive edge caching with no invalidation needed.

What's next