Self-host

Your own 404less, on Cloudflare’s free tier.

Everything runs in one Worker with D1 (SQLite), Durable Objects and two cron triggers. All of it is on the Workers Free plan, so a personal instance costs nothing at hobby scale.

deploy Cloudflare Workers
  1. Cloned
    Into your GitHub account, ready to fork and change
  2. Provisioned
    Worker, D1, Durable Objects and two crons. Migrations run on deploy
  3. Live
    https://404less.you.workers.dev
    Workers Free plan · your data stays in your account

One-click deploy

The Deploy to Cloudflare button clones the repository into your GitHub account and provisions the D1 database and the Durable Objects declared in wrangler.jsonc. Migrations and the dashboard build run as part of the deploy.

Deploy to Cloudflare

When it finishes, open your Worker’s URL, sign in with your email, and add your first site. Without an email provider configured, the sign-in link is returned on screen (dev mode) so you can get in immediately.

Manual deploy

$ git clone https://github.com/AshikNesin/404less && cd 404less
$ npm install && npm --prefix web install
$ npx wrangler d1 create 404less        # paste database_id into wrangler.jsonc
$ npm run deploy                         # builds the dashboard, applies migrations, deploys

Run it locally first

$ npm run build:web && npm run dev       # http://127.0.0.1:8787, local D1, no account needed
$ npm test                              # the suite runs inside workerd against local D1

Email

Sign-in links and email digests go out through Resend, whose free tier covers a personal instance. Slack and Pushover digests work without it.

$ npx wrangler secret put RESEND_API_KEY
$ npx wrangler secret put MAIL_FROM        # optional, e.g. "404less <no-reply@yourdomain.com>"
No RESEND_API_KEY means dev mode: sign-in links are returned by the API instead of emailed. Fine for a single-user instance behind your own domain; set the key before inviting anyone else.

Limits & budget

Defaults are chosen so a self-hoster never trips a free-plan ceiling by accident. Each is a variable in wrangler.jsonc.

Setting Default Why
MAX_SITES_PER_TEAM 5 Keeps crawl volume and D1 writes predictable
EVENT_RETENTION_DAYS 90 Nightly prune keeps D1 far under its 500 MB cap
Ingest rate limit 500 hits / site / hour Protects the 100k daily D1 write budget
Crawl pages per run 100 (max 500) Per-site setting on the Crawler page; counts the pages a run discovers
CRAWL_INTERVAL_HOURS 24 How often sites with scheduled crawling are crawled

Cloudflare’s own numbers: 100k Worker requests, 100k D1 writes and 5M reads and 100k Durable Object requests per day on the Free plan, 50 outbound requests per invocation, and five cron triggers per account. 404less crawls without Queues: a cron tick every minute checks up to 40 links and stops, and the next tick carries on, so a crawl never exceeds what one invocation may do. A tick with nothing to do only reads.

Updating

Pull the latest commit and run npm run deploy again. Migrations are additive and apply automatically; the managed broken-links list is preserved across upgrades.

Custom domains are declared in wrangler.jsonc under routes; remove or replace the 404less.com entries with yours.