CloudFlare-ImgBed

Image hosting service powered by CloudFlare-ImgBed, deployed on Cloudflare Pages with R2 storage.

Architecture

  • Frontend + API: Cloudflare Pages (serverless, free tier)
  • Storage: Cloudflare R2 (free tier: 10GB)
  • Database: Cloudflare D1 (SQLite, free tier: 5M reads/day)
  • Domain: img.emacsbliss.com (DNS-only, no Cloudflare proxy)
  • GitHub: Forked from MarSeventh/CloudFlare-ImgBed

Prerequisites

  • Cloudflare account with R2 enabled
  • GitHub account (for Pages deployment)
  • Custom domain on Cloudflare DNS

Setup Steps

1. Fork the Repository

2. Create R2 Bucket

  • Cloudflare Dashboard → R2 Object StorageCreate bucket
  • Bucket name: imgbed
  • Storage class: Standard (free tier eligible)
  • Location: closest to your region

3. Create D1 Database

  • Cloudflare Dashboard → Storage & DatabasesD1
  • Create database, name it img_d1
  • Go to Console tab and run the init SQL from database/init.sql in the repo (creates files, settings, index_operations, index_metadata, other_data tables + indexes + triggers)

4. Deploy to Cloudflare Pages

  • Cloudflare Dashboard → Workers & PagesCreate
  • Choose Pages (NOT Workers)
  • “Connect to Git” → select the forked repo
  • Settings:
    • Framework preset: None
    • Build command: npm install
    • Build output directory: frontend-dist
    • Root directory: leave blank
  • Click Deploy

5. Bind D1 Database

  • Pages project → SettingsBindings
  • AddD1 database
    • Variable name: img_d1
    • D1 database: select img_d1

D1-binding.png

  • SaveRetry deployment (bindings only take effect after redeploy)

6. Bind R2 Bucket

  • Pages project → SettingsBindings
  • AddR2 bucket
    • Variable name: img_r2
    • R2 bucket: select imgbed

R2-binding.png

  • SaveRetry deployment

7. Configure Custom Domain

  • Pages project → Custom domainsAdd
  • Enter img.emacsbliss.com
  • Cloudflare DNS record can be proxied (orange cloud) — works fine with Pages

8. Admin Setup

  • Visit https://img.emacsbliss.com
  • On first visit, no login is required (admin not configured yet)
  • Go to Admin Dashboard:
    • Set admin username and password (Security Settings)
    • Set user auth code if you want upload password protection
    • Enable the R2_env channel in Upload Settings
    • Set public URL to https://img.emacsbliss.com R2-public-url.png
    • Set default naming method to short
    • Create an API token with upload permission (for programmatic use)

9. API Token Configuration

  • Admin panel → Security Settings → API Tokens
  • Create token with upload permission
  • Store token in ~/.hermes/.env as IMG_BED_TOKEN

Upload API

curl -X POST 'https://img.emacsbliss.com/upload?uploadChannel=cfr2&uploadNameType=short&returnFormat=full' \
  -H "Authorization: Bearer $IMG_BED_TOKEN" \
  -H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36" \
  -F "file=@/path/to/image.png"

Response: [{"src": "https://img.emacsbliss.com/file/XXXXX.png"}]

Key Decisions

  • Pages over Docker — zero server maintenance, edge-cached globally, all within Cloudflare ecosystem
  • Pages over Workers — Pages wraps Workers with automatic static asset hosting; the functions/ directory IS Workers
  • R2 over Telegram — proper object storage with direct URLs, no proxying needed, already in Cloudflare ecosystem
  • DNS-only domain — Cloudflare proxy (orange cloud) blocks API calls with 1010 bot error
  • Short naming — clean URLs for programmatic use (/file/XXXXX.png)
  • No WebP conversion — preserve original format for programmatic hosting

Troubleshooting

  • 1010 bot error — Cloudflare Pages has built-in bot protection. All API calls must include a browser User-Agent header (e.g., Mozilla/5.0 ...). Without it, requests return 403 with error code 1010.
  • “Database not configured” — D1 binding variable name must be exactly img_d1. Check Settings → Bindings and retry deployment.
  • Login loop on fresh install — D1 tables not initialized. Run the init.sql from the repo in the D1 console.
  • 403 Forbidden on upload — API token lacks upload permission. Recreate the token with upload permission enabled.
  • DNS record — proxied (orange cloud) works fine. Pages has its own CDN/SSL, but the Cloudflare proxy adds an extra layer of caching without causing issues.