FlaresendDocs

Dashboard

Deploy the admin dashboard as its own Worker, protected by Cloudflare Access.

The dashboard (apps/dashboard) is a Next.js app deployed as the Worker flaresend-dashboard with @opennextjs/cloudflare. It has no login of its own: Cloudflare Access is the login, and the dashboard's middleware checks the Access token again on every request.

It never holds an API key. Every read and write goes to the mailer's AdminRpc entrypoint over a service binding, and all of those calls run on the server.

apps/dashboard/wrangler.jsonc
"services": [
  { "binding": "MAILER_ADMIN", "service": "flaresend", "entrypoint": "AdminRpc" }
]

What you can do in it is described in Dashboard.

Deploy

Deploy the mailer first

The MAILER_ADMIN binding points at the Worker flaresend, so it has to exist. See Deploy.

Create the Access application

  1. In the Zero Trust dashboard, go to Access → Applications → Add an application → Self-hosted.
  2. Application domain: the hostname the dashboard will use, for example dashboard.acme.com.
  3. Add a policy, for example Action Allow, Include Emails = your address, or an email domain. Login methods: one-time PIN or Google.
  4. Save, open the application's overview and copy the Application Audience (AUD) Tag.
  5. Find your team domain under Settings → Custom Pages (or Settings → General). It looks like <team>.cloudflareaccess.com.

Put the Access values in wrangler.jsonc

apps/dashboard/wrangler.jsonc
"vars": {
  "ACCESS_AUD": "<the AUD tag>",
  "ACCESS_TEAM_DOMAIN": "<team>.cloudflareaccess.com"
}

Also set account_id to your account.

Add the hostname

Serve the dashboard on the same hostname you gave the Access application:

apps/dashboard/wrangler.jsonc
"routes": [{ "pattern": "dashboard.acme.com", "custom_domain": true }]

custom_domain: true creates the DNS record and the certificate.

Deploy

pnpm --filter @flaresend/dashboard run deploy

This runs opennextjs-cloudflare build && opennextjs-cloudflare deploy. The dashboard uses no cache bindings (every page is dynamic), so there is nothing else to create.

Building on Windows

opennextjs-cloudflare build fails on Windows with EPERM: operation not permitted, symlink unless your account may create symlinks (Developer Mode on, or an elevated shell). Build in WSL or CI, or turn on Developer Mode. next dev and next build are not affected.

How the Access check works

apps/dashboard/src/middleware.ts runs on every request, including /_next assets that go through the Worker:

ACCESS_AUD and ACCESS_TEAM_DOMAINBuildResult
Both setanyRequires the Cf-Access-Jwt-Assertion header (403 without it) and verifies it against https://<team>/cdn-cgi/access/certs, issuer https://<team>, audience ACCESS_AUD (403 if invalid).
Either emptynext dev (NODE_ENV=development)No check.
Either emptyproduction500 "Cloudflare Access is not configured for this dashboard".

Static files under /_next/static are served straight from the assets binding and skip the middleware. They contain no data.

Run it locally

The quickest way is next dev against the mailer's HTTP admin API. Start the mailer (pnpm --filter @flaresend/mailer dev, on http://localhost:8787), then create apps/dashboard/.dev.vars:

apps/dashboard/.dev.vars
MAILER_URL=http://localhost:8787
MAILER_ADMIN_KEY=<the mailer's ADMIN_API_KEY>
pnpm --filter @flaresend/dashboard dev

When both are set and NODE_ENV is not production, the dashboard uses an HTTP client for /v1/admin/* instead of the binding. In production, the binding always wins when it exists.

Keep the admin key out of .env

Don't put these in .env or .env.local. opennextjs-cloudflare build bundles .env, .env.local and .env.production* into the Worker, so the admin key would ship inside it. .dev.vars* and .env.development* are not bundled.

To test with the real service binding, run the mailer with wrangler dev in one terminal and pnpm --filter @flaresend/dashboard preview in another. Wrangler connects MAILER_ADMIN to the local flaresend Worker. This is a production build, so with the Access vars empty every request gets a 500.

Checks

pnpm --filter @flaresend/dashboard typecheck
pnpm --filter @flaresend/dashboard test
pnpm --filter @flaresend/dashboard build

After changing apps/dashboard/wrangler.jsonc, run pnpm --filter @flaresend/dashboard cf-typegen to regenerate worker-configuration.d.ts.

On this page