Deploy your own cliick
cliick is a personal URL shortener that runs entirely on Cloudflare's free tier — Workers for serverless compute, D1 for the SQLite database, and Access to lock the dashboard to just you. This guide walks you through everything from cloning the repo to going live on your own domain in under 15 minutes.
What you'll end up with
A fully working URL shortener at your own domain (e.g. s.yoursite.com) with:
| Feature | How it works |
|---|---|
| Redirect | Visit /abc → instantly redirected to the long URL. |
| Dashboard | Private at /dashboard, protected by Cloudflare Access (no password to manage yourself). |
| Click analytics | Country, device, browser, OS, and referrer tracked per click via Cloudflare's edge metadata. |
| Projects | Group links and filter stats by project. |
| Import / Export | CSV round-trip for backups and bulk imports. |
Everything runs on Cloudflare's free tier. Workers: 100k requests/day. D1: 5 GB storage, 25M row reads/day.
Prerequisites
- Node.js 18+ — used only to run Wrangler locally. nodejs.org
- A Cloudflare account — free at dash.cloudflare.com
- A domain on Cloudflare — any domain you own, with DNS managed by Cloudflare (free). A subdomain works too.
- Git — to clone the repo.
1 Clone the repository
Clone the repo and enter the directory:
git clone https://github.com/yourusername/cliick.git
cd cliick
The project structure looks like this:
2 Install dependencies
npm install
This installs Wrangler (Cloudflare's CLI) and TypeScript. Nothing else is needed — there is no runtime framework.
Log in to Cloudflare:
npx wrangler login
A browser window will open. Authenticate with your Cloudflare account. Your credentials are saved locally — you only need to do this once per machine.
3 Note your Cloudflare Account ID
You'll need your Account ID for wrangler.jsonc. Find it at:
- Go to dash.cloudflare.com
- Click any domain, or go to Workers & Pages in the left sidebar
- Your Account ID is shown in the right-hand panel — copy it
You don't need to put this in wrangler.jsonc manually — Wrangler reads it automatically from your login session.
4 Configure the Worker name
Open wrangler.jsonc and set the name field to anything you like. This becomes the internal Worker name in Cloudflare:
{
"name": "my-shortener", // ← change this
"main": "src/index.ts",
"compatibility_date": "2026-04-08",
...
}
name only matters for the internal Cloudflare Workers dashboard. It has no effect on your public URL — that's set by routes and custom domains.5 Create the D1 database
Run this command to create a new D1 SQLite database:
npx wrangler d1 create my-shortener
Wrangler will print output like this:
✅ Successfully created DB 'my-shortener'
[[d1_databases]]
binding = "DB"
database_name = "my-shortener"
database_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" ← copy this
Open wrangler.jsonc and update the d1_databases section:
"d1_databases": [
{
"binding": "DB",
"database_name": "my-shortener",
"database_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" ← paste here
}
]
binding value exactly as "DB" — the Worker code references this name. Only change database_name and database_id.6 Run the database schema
Push the schema to your remote D1 database:
npx wrangler d1 execute my-shortener --remote --file=./schema.sql
Replace my-shortener with the database name you chose in step 5. This creates the links, clicks, and projects tables.
Verify it worked:
npx wrangler d1 execute my-shortener --remote --command="SELECT name FROM sqlite_master WHERE type='table'"
You should see:
┌──────────┐
│ name │
├──────────┤
│ projects │
│ links │
│ clicks │
└──────────┘
--remote flag writes to your live Cloudflare database. Without it, changes only apply to your local development copy.7 Set up your custom domain & DNS
Your domain must be on Cloudflare DNS. If it isn't yet, add it at dash.cloudflare.com → Add a site and point your registrar's nameservers to Cloudflare's.
example.com)Open wrangler.jsonc and update the routes and custom_domains sections:
"routes": [
{ "pattern": "example.com/*", "zone_name": "example.com" },
{ "pattern": "www.example.com/*", "zone_name": "example.com" }
],
"custom_domains": ["example.com", "www.example.com"]
s.example.com)"routes": [
{ "pattern": "s.example.com/*", "zone_name": "example.com" }
],
"custom_domains": ["s.example.com"]
Cloudflare will automatically create the DNS record when you deploy.
*.workers.dev subdomain automatically at deploy time.8 Protect the dashboard with Cloudflare Access
Cloudflare Access replaces a traditional login page. It authenticates visitors via email OTP, Google, GitHub, or other providers — no username/password to manage in your app. Only /dashboard/* is protected; everything else is public.
- Go to one.dash.cloudflare.com
- Select your account and click Get started if prompted (it's free)
- Choose the Free plan — supports up to 50 users
- In the Zero Trust sidebar, go to Access → Applications
- Click Add an application
- Choose Self-hosted
- Fill in:
- Application name: cliick Dashboard (or anything)
- Session duration: 24 hours (or longer if you prefer)
- Application domain: your domain, e.g.
example.com - Path:
dashboard
- Click Next
- Policy name:
Allow me(or anything) - Action: Allow
- Under Include, add a rule:
- Selector: Emails
- Value: your email address
- Click Next, then Add application
When you visit yourdomain.com/dashboard, Cloudflare Access intercepts the request, shows a login page, sends a one-time passcode to your email (or redirects to Google/GitHub), and only then grants access. A session cookie is set so you stay logged in for the configured duration.
All other paths (/, /abc short links, /docs, /api/*) are completely unprotected — redirects work without any authentication.
9 Deploy
You're ready. Deploy to Cloudflare:
npx wrangler deploy
Wrangler will:
- Compile TypeScript → JavaScript (bundled, no build step needed)
- Upload your static assets (
public/) to Cloudflare's CDN - Deploy the Worker to Cloudflare's global edge network
- Attach it to your custom domain routes
When deployment completes you'll see:
Deployed cliick triggers
example.com/* (zone name: example.com)
Visit https://yourdomain.com — the landing page should load. Try /dashboard — Cloudflare Access will prompt you to authenticate.
npx wrangler deploy again. There's no build pipeline — Wrangler bundles on the fly.Using the dashboard
Once deployed, the dashboard at /dashboard gives you:
| Section | What it does |
|---|---|
| Dashboard | Overview stats — total links, clicks, time windows, top countries, top links, daily chart. Filter by project. |
| Links | Create, edit, and delete short links. Filter by project. Assign a link to a project at creation or editing time. |
| Stats | Per-link or aggregate analytics — clicks over time, by country, device, browser, OS, and referrer domain. Time period filters. |
| Projects | Group links into named, color-coded projects. Deleting a project cascades to its links and all click history. |
| Export / Import | Download all links as CSV (or per-project). Import from CSV with a live conflict-preview before committing. |
Local development
Run the dev server with a local D1 database:
npx wrangler dev
First, seed your local database:
npx wrangler d1 execute my-shortener --local --file=./schema.sql
The dev server runs at http://localhost:8787. Hot-reload is automatic — save a file and the Worker restarts instantly.
/dashboard is directly accessible. This is expected; Access only runs on Cloudflare's edge.| Command | Purpose |
|---|---|
npx wrangler dev | Start local dev server on port 8787 |
npx wrangler deploy | Deploy to production |
npx wrangler d1 execute <db> --local --command="..." | Run SQL on local database |
npx wrangler d1 execute <db> --remote --command="..." | Run SQL on production database |
npx wrangler types | Regenerate TypeScript types after changing bindings |
npx tsc --noEmit | Type-check without building |