• Skip to main content
  • Skip to secondary menu
  • Skip to footer

Exclusive Domains

Domain Name Market

  • Domain Tools
    • Domain Age Checker
    • Domain Extension Checker
    • Domain History Checker
  • Sponsored Post
  • About
  • Contact
    • GDPR

BitSpeed.org: How to Build a Cloudflare Workers Speed Test — and Why the Domain Is the Real Asset

May 9, 2026 By admin

There is a category of domain that does not need much imagination to value. You read the name, you immediately know what it does, and you know who would pay for it. bitspeed.org is that kind of domain. Clean, technical, memorable, and sitting in a space — internet speed measurement — that has real institutional buyers: ISPs, network hardware vendors, telcos, SaaS monitoring companies.

The question is not whether the domain has value. The question is how to surface that value to the right buyer. The answer, in this case, is a working product. Not a parking page. Not a coming-soon placeholder. A functional, live internet speed test running at bitspeed.org, built on Cloudflare Workers, with zero hosting cost and a deployment time measured in hours.

This post covers both things at once: the domain case and the technical build. By the end, you will have a complete architectural blueprint you can execute yourself.


Why Cloudflare Workers

The architecture choice matters here because it reflects the domain’s identity. A speed test built on Cloudflare Workers is not a toy demo. Cloudflare operates over 300 points of presence worldwide, and a Worker executes at the PoP nearest to the user. That means latency measurements are honest — the probe hits real edge infrastructure, not a single origin server in a distant data center.

This is the same principle that makes Cloudflare’s own speed test at speed.cloudflare.com credible. You are borrowing that infrastructure. For a domain like bitspeed.org, that is a meaningful credibility signal to any technical buyer who looks under the hood.

The secondary benefit is operational: the Cloudflare Workers free tier covers 100,000 requests per day. A full speed test — ping, download, upload — consumes roughly three to five requests. That is 20,000 to 30,000 complete tests per day before any cost appears. For a domain showcase, this is effectively infinite.


The Architecture

The entire application lives in a single Worker script. No external origin server. No database. No CDN configuration beyond the Worker itself. The Worker handles four routes:

GET / serves the complete HTML, CSS, and JavaScript application inline — a single self-contained response. GET /ping returns a minimal one-byte response used for latency measurement. GET /download streams a configurable volume of generated bytes to the browser. POST /upload receives a binary payload from the browser and acknowledges the byte count received.

Nothing else is required. The Worker file structure is minimal:

bitspeed-worker/
├── wrangler.toml
├── package.json
└── src/
    └── index.js

The wrangler.toml configuration maps the Worker to the bitspeed.org domain:

name = "bitspeed"
main = "src/index.js"
compatibility_date = "2024-11-01"

routes = [
  { pattern = "bitspeed.org/*", zone_name = "bitspeed.org" }
]

For this to work, bitspeed.org must be on Cloudflare’s nameservers. GoDaddy supports NS delegation — you point the domain’s nameservers at Cloudflare, Cloudflare takes over DNS, and the Worker route activates.


The Three-Phase Test

The test runs in sequence: ping and jitter first, download second, upload third. Each phase has a distinct method.

Ping and jitter fires ten consecutive GET /ping requests. The browser records round-trip time for each using performance.now(). The first result is discarded — it includes connection establishment overhead that inflates the number. The remaining nine produce an average latency and a jitter figure derived from standard deviation. This is the same methodology serious speed test implementations use.

Download sends a GET /download?mb=25 request. The Worker responds with a streaming ReadableStream that pushes chunks of generated bytes in a controller loop. The browser consumes the stream using response.body.getReader(), sampling bytes received against elapsed time every 200 milliseconds. This produces a live Mbps readout that updates in real time during the test. Final throughput is computed from total bytes transferred over total wall time.

Upload generates a local ArrayBuffer in the browser — starting at 5 MB, scaling to 25 MB automatically if the connection is fast enough to warrant it — and POSTs it to /upload. The Worker reads the request body and acknowledges the byte count. The browser divides payload size by wall time to derive upload throughput. Auto-scaling the payload avoids wasting time on slow connections while ensuring accurate measurement on fast ones.


Server Location and ISP Detection

Every Cloudflare response includes a CF-Ray header in the format hash-IATA, where the IATA code identifies the PoP that handled the request. Parsing the last segment gives you the airport code — FRA for Frankfurt, SIN for Singapore, LAX for Los Angeles — which the UI displays as the server location. This is a small detail that adds significant credibility to the result.

The user’s IP is available from the cf-connecting-ip request header. ISP identification requires one outbound call from the Worker to a geolocation API such as ipapi.co, which returns ISP name and ASN. For a pure Workers-only build with no external dependencies, this can be omitted and displayed as a future enhancement.


The UI

The interface follows the established visual language of speed test applications because buyers and visitors will immediately recognize what they are looking at. The centerpiece is an arc gauge — an SVG semicircle that sweeps from zero to maximum as download or upload speed is measured. The needle position corresponds to current Mbps, and the number updates live during the test.

Below the gauge, three result cards display the final values: ping in milliseconds, download in Mbps, upload in Mbps. A status line above the gauge reads “Testing download… 47.3 Mbps” during measurement and settles to the result when the phase completes. The footer shows server location, user IP, and ISP name.

The visual goal is not to replicate Speedtest.net’s exact aesthetic. It is to produce something that looks intentional, professional, and purpose-built for the domain. A buyer encountering bitspeed.org should see a product, not a prototype.


Deployment

The full deployment sequence from zero to live is four steps. Install the Cloudflare Workers CLI with npm create cloudflare@latest bitspeed-worker. Write the Worker script and inline HTML. Run wrangler deploy. Point bitspeed.org’s nameservers at Cloudflare through GoDaddy’s DNS settings.

Build time for someone with basic JavaScript familiarity is three to four hours. Ongoing maintenance is zero — the Worker runs indefinitely with no servers to patch, no certificates to renew, no infrastructure to monitor.


The Domain Case

Speed testing has real institutional demand. ISPs use branded speed test tools for customer self-diagnosis, reducing support call volume. Hardware vendors embed them in router admin interfaces. Enterprise network monitoring companies use them as lightweight probes. Any of these buyers would find bitspeed.org immediately useful as a brand — the name communicates exactly what the product does without explanation.

The difference between a parked domain and a domain with a live product is not just aesthetic. It is the difference between a buyer having to imagine the use case and a buyer being able to see it. At the price point where .org domains in this space trade — typically five to fifteen thousand dollars for a clean, generic, on-brand name — the cost of building a proof-of-concept on Cloudflare Workers is negligible against the potential upside.

The technical work described in this post is not a business plan. It is a domain positioning strategy. BitSpeed.org running a real speed test is a more persuasive asking-price justification than any outreach email could be on its own.

Build it once. Let it run. Let the domain speak for itself.

BitSpeed.org — Full Cloudflare Workers Speed Test: The Complete Code

Two files. No build step. Deploy with wrangler deploy.


wrangler.toml

name = "bitspeed"
main = "src/index.js"
compatibility_date = "2024-11-01"

routes = [
  { pattern = "bitspeed.org/*", zone_name = "bitspeed.org" }
]

src/index.js

const HTML = `
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>BitSpeed — Internet Speed Test</title>
  <style>
    * { box-sizing: border-box; margin: 0; padding: 0; }
    body {
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
      background: #0a0a0f;
      color: #e0e0e0;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      min-height: 100vh;
      padding: 2rem;
    }
    h1 { font-size: 1.6rem; font-weight: 300; letter-spacing: .2em; color: #fff; margin-bottom: 2.5rem; }
    h1 span { color: #00c8ff; font-weight: 700; }
    .gauge-wrap { position: relative; width: 280px; height: 160px; margin-bottom: 1.5rem; }
    .gauge-wrap svg { width: 100%; height: 100%; }
    .track { fill: none; stroke: #1e1e2e; stroke-width: 18; stroke-linecap: round; }
    .arc  { fill: none; stroke: #00c8ff; stroke-width: 18; stroke-linecap: round;
            transition: stroke-dashoffset .15s ease; }
    .speed-num {
      position: absolute; bottom: 0; left: 50%; transform: translateX(-50%);
      font-size: 3rem; font-weight: 700; color: #fff; line-height: 1;
    }
    .speed-unit { font-size: .85rem; color: #888; text-align: center; margin-bottom: 2rem; letter-spacing: .1em; }
    .status { font-size: .95rem; color: #aaa; margin-bottom: 2.5rem; min-height: 1.4em; }
    .cards { display: flex; gap: 1.5rem; margin-bottom: 2.5rem; flex-wrap: wrap; justify-content: center; }
    .card {
      background: #13131f;
      border: 1px solid #1e1e2e;
      border-radius: 12px;
      padding: 1.2rem 2rem;
      text-align: center;
      min-width: 110px;
    }
    .card-label { font-size: .7rem; letter-spacing: .12em; color: #666; text-transform: uppercase; margin-bottom: .4rem; }
    .card-val { font-size: 1.6rem; font-weight: 700; color: #fff; }
    .card-unit { font-size: .7rem; color: #555; }
    button {
      background: #00c8ff;
      color: #000;
      border: none;
      border-radius: 50px;
      padding: .85rem 3rem;
      font-size: 1rem;
      font-weight: 700;
      cursor: pointer;
      letter-spacing: .05em;
      transition: opacity .2s;
    }
    button:disabled { opacity: .4; cursor: not-allowed; }
    .meta { margin-top: 2rem; font-size: .75rem; color: #444; text-align: center; line-height: 1.8; }
  </style>
</head>
<body>
  <h1><span>BIT</span>SPEED</h1>

  <div class="gauge-wrap">
    <svg viewBox="0 0 280 160" xmlns="http://www.w3.org/2000/svg">
      <path class="track" d="M 30 150 A 110 110 0 0 1 250 150"/>
      <path class="arc"  id="arc" d="M 30 150 A 110 110 0 0 1 250 150"
            stroke-dasharray="345" stroke-dashoffset="345"/>
    </svg>
    <div class="speed-num" id="num">—</div>
  </div>
  <div class="speed-unit" id="unit"></div>
  <div class="status" id="status">Press Go to begin</div>

  <div class="cards">
    <div class="card">
      <div class="card-label">Ping</div>
      <div class="card-val" id="r-ping">—</div>
      <div class="card-unit">ms</div>
    </div>
    <div class="card">
      <div class="card-label">Download</div>
      <div class="card-val" id="r-dl">—</div>
      <div class="card-unit">Mbps</div>
    </div>
    <div class="card">
      <div class="card-label">Upload</div>
      <div class="card-val" id="r-ul">—</div>
      <div class="card-unit">Mbps</div>
    </div>
  </div>

  <button id="btn" onclick="runTest()">Go</button>

  <div class="meta" id="meta"></div>

  <script>
    const arc    = document.getElementById('arc');
    const numEl  = document.getElementById('num');
    const unitEl = document.getElementById('unit');
    const status = document.getElementById('status');
    const btn    = document.getElementById('btn');
    const ARC_LEN = 345;

    function setGauge(mbps, max) {
      const pct = Math.min(mbps / max, 1);
      arc.style.strokeDashoffset = ARC_LEN - ARC_LEN * pct;
      numEl.textContent = mbps >= 1 ? mbps.toFixed(1) : mbps.toFixed(0);
    }
    function resetGauge() {
      arc.style.strokeDashoffset = ARC_LEN;
      numEl.textContent = '—';
      unitEl.textContent = '';
    }

    async function measurePing() {
      status.textContent = 'Measuring ping…';
      const times = [];
      for (let i = 0; i < 10; i++) {
        const t0 = performance.now();
        await fetch('/ping', { cache: 'no-store' });
        times.push(performance.now() - t0);
      }
      times.shift(); // discard first (connection warmup)
      const avg = times.reduce((a,b) => a+b, 0) / times.length;
      const jitter = Math.sqrt(times.map(t => (t - avg)**2).reduce((a,b) => a+b, 0) / times.length);
      document.getElementById('r-ping').textContent = avg.toFixed(0);
      return { ping: avg, jitter };
    }

    async function measureDownload() {
      status.textContent = 'Testing download…';
      unitEl.textContent = 'Mbps download';
      const MB = 25;
      const t0 = performance.now();
      let loaded = 0;
      const res = await fetch('/download?mb=' + MB, { cache: 'no-store' });
      const reader = res.body.getReader();
      let last = t0;
      while (true) {
        const { done, value } = await reader.read();
        if (done) break;
        loaded += value.byteLength;
        const now = performance.now();
        if (now - last > 200) {
          const mbps = (loaded * 8) / ((now - t0) / 1000) / 1e6;
          setGauge(mbps, 500);
          status.textContent = 'Download: ' + mbps.toFixed(1) + ' Mbps';
          last = now;
        }
      }
      const elapsed = (performance.now() - t0) / 1000;
      const mbps = (loaded * 8) / elapsed / 1e6;
      setGauge(mbps, 500);
      document.getElementById('r-dl').textContent = mbps.toFixed(1);
      return mbps;
    }

    async function measureUpload(dlMbps) {
      status.textContent = 'Testing upload…';
      unitEl.textContent = 'Mbps upload';
      const MB = dlMbps > 100 ? 25 : 5;
      const buf = new ArrayBuffer(MB * 1024 * 1024);
      crypto.getRandomValues(new Uint8Array(buf));
      const t0 = performance.now();
      await fetch('/upload', { method: 'POST', body: buf, cache: 'no-store' });
      const elapsed = (performance.now() - t0) / 1000;
      const mbps = (MB * 8) / elapsed;
      setGauge(mbps, 500);
      document.getElementById('r-ul').textContent = mbps.toFixed(1);
      return mbps;
    }

    async function runTest() {
      btn.disabled = true;
      resetGauge();
      ['r-ping','r-dl','r-ul'].forEach(id => document.getElementById(id).textContent = '—');
      document.getElementById('meta').textContent = '';
      try {
        const { ping } = await measurePing();
        const dlMbps = await measureDownload();
        await measureUpload(dlMbps);
        status.textContent = 'Done.';
        unitEl.textContent = '';
        numEl.textContent = '✓';
      } catch(e) {
        status.textContent = 'Error: ' + e.message;
      }
      btn.disabled = false;
    }

    // Fetch server location from CF-Ray on load
    fetch('/ping').then(r => {
      const ray = r.headers.get('cf-ray') || '';
      const pop = ray.split('-')[1] || '—';
      document.getElementById('meta').textContent = 'Server: ' + pop;
    });
  </script>
</body>
</html>
`;

// Generate random bytes for download test
function makeStream(bytes) {
  const CHUNK = 64 * 1024; // 64 KB chunks
  return new ReadableStream({
    start(controller) {
      let sent = 0;
      function push() {
        while (sent < bytes) {
          const size = Math.min(CHUNK, bytes - sent);
          controller.enqueue(new Uint8Array(size));
          sent += size;
          if (sent < bytes) { setTimeout(push, 0); return; }
        }
        controller.close();
      }
      push();
    }
  });
}

export default {
  async fetch(request) {
    const url = new URL(request.url);

    // Serve app
    if (url.pathname === '/') {
      return new Response(HTML, {
        headers: { 'Content-Type': 'text/html;charset=UTF-8' }
      });
    }

    // Ping endpoint
    if (url.pathname === '/ping') {
      return new Response('', {
        headers: {
          'Content-Type': 'text/plain',
          'Cache-Control': 'no-store',
          'Access-Control-Allow-Origin': '*'
        }
      });
    }

    // Download endpoint
    if (url.pathname === '/download') {
      const mb = Math.min(parseInt(url.searchParams.get('mb') || '25', 10), 100);
      const bytes = mb * 1024 * 1024;
      return new Response(makeStream(bytes), {
        headers: {
          'Content-Type': 'application/octet-stream',
          'Content-Length': String(bytes),
          'Cache-Control': 'no-store',
          'Access-Control-Allow-Origin': '*'
        }
      });
    }

    // Upload endpoint
    if (url.pathname === '/upload' && request.method === 'POST') {
      const buf = await request.arrayBuffer();
      return new Response(JSON.stringify({ received: buf.byteLength }), {
        headers: {
          'Content-Type': 'application/json',
          'Cache-Control': 'no-store',
          'Access-Control-Allow-Origin': '*'
        }
      });
    }

    return new Response('Not found', { status: 404 });
  }
};

Deploy

npm create cloudflare@latest bitspeed-worker
cd bitspeed-worker
# replace src/index.js with the code above
wrangler deploy

Point bitspeed.org nameservers to Cloudflare, add the Worker route in the dashboard, and the speed test is live. Total hosting cost: zero on the free tier.

Filed Under: Exclusive Domains

Footer

Recent Posts

  • ModelAggregator.com Is the Word for How AI Apps Will Buy Models
  • StockBrokerage.org Puts One of Finance’s Richest Keywords on an Independent .org
  • BatchComputing.com Is the Name for Running AI at Half Price
  • RealEstateMarket.us Reads Exactly the Way Americans Search for Housing Data
  • CodeInstances.com Names the Sandbox Layer Every AI Agent Needs
  • ComputerEngineering.org Owns the Name of the Degree Behind Every AI Chip
  • MarketIntelligence.org Names the Discipline Behind Every Competitor Dashboard
  • Ukue.com Is the Brand the One-File Job Queue Is Still Missing
  • IsraeliWine.com Is the Exact Name for a Wine Country With Kosher Buyers Worldwide
  • RefurbishedContainers.com Is the Buyer’s Word for the Used Shipping Container Trade

Media Partners

  • Syndicator.net
  • k4i.com
  • Referently.com
A Schema Diff That Warns Which Migration Will Lock a 40-Million-Row Table or Lose Data
A Single-File Log Database: Pipe Logs In, Query Them With SQL, Hand the File to Anyone
A Database Where Every Value Remembers Its Source, Confidence and Extraction Time
An AI Agent Flight Recorder Belongs in One Portable File, the Way HAR Did It for HTTP
An Evidence Graph Built From Extracted Claims Keeps Contradictions Attached to Their Sources
A Background Job Queue in One SQLite File Covers What Most Apps Deploy Redis For
Infer Your API's Real Contract From Traffic, Then Diff It Against the Docs
An Embedded Sketch Database: Billions of Events, Megabytes of Storage, Error Bars on Every Answer
Fuzzing MCP Servers: Generate Bad Arguments From the Tool Schema and Watch What Breaks
Give Any API a History: Poll It, Hash It and Query Old Versions With SQL
Venture and M&A Digest: Valon Raises $150M at $2.3B, C.H. Robinson to Acquire RXO
Tech News Digest, October 3, 2026: Supabase Buys Turso, Broadcom's $60B AI Chip Financing, Flock Ruling, Plus Five Infrastructure Projects
Borsa Istanbul Fund Scandal: Turkey's Regulator Is Prosecuting the Collapse Its Own Rule Set Off
Fed Hikes Rates for the First Time Since 2023 and the 10-Year Treasury Yield Falls Back Below 5%
Chip Stocks Sell Off on Amodei's Pacing Call While 2026 Capex Forecasts Keep Rising
Saudi Arabia Loses Both Export Routes as the Houthis Reach Bab el-Mandeb
Micron (MU) Slips as Intel-Backed Kepler Computing Takes Aim at Memory With 2,000 Wafers to Its Name
Palantir (PLTR) Gave Back Half of a 9.1% Rally While Snowflake Kept 21%
DFEN Fell 33% in a Month While Its Index Fell Only 11%
Why Marvell and Memory Stocks Are Down After Nvidia Guided FY28 Growth to 70%
Maritime Chokepoints Compared: Hormuz, Bab el-Mandeb, Malacca, Suez and Panama by Traffic and Risk
Hybrid Bonding Explained: How It Works, Why HBM Needs It and Who Supplies the Tools
Leveraged ETFs Explained: Daily Reset, Volatility Decay and Why 3x Isn't 3x Over Time
HBM Explained: How High Bandwidth Memory Works, HBM3E vs HBM4, and Who Makes It
Huawei Sanctions Timeline: Every Major US Action From 2019 to 2026
Military Alert Levels Explained: DEFCON, FPCON, MOPP and EMCON Compared
Entity List Additions Tracker: Chinese Companies Added Since 2019, by Sector
CVE, CVSS and CISA KEV Explained: How Vulnerabilities Are Scored and Prioritized
Diplomatic Ranks Explained: Ambassador, Envoy, Chargé d'Affaires and Consul
CoWoS Explained: TSMC's Advanced Packaging and Why It Bottlenecks AI Chips

Media Partners

  • Media Presser
  • Yellow Fiction
  • 3V.org
Press Release Digest, October 3, 2026: Tesla Q3 Deliveries, Thales on Frontier AI Attacks, Cable One Financing, Plus Five Infrastructure Projects
2G Energy Wins 275 MW Order From Energy Vault to Power US AI Data Centers With On-Site Gas Engines
WhiteFiber Launches Continuum, Linking Two Data Centers 83 km Apart Into One GPU Supercluster
Sivers Semiconductors Reshuffles Leadership With Semtech and Amkor Veterans for Its Photonics and Wireless Push
Qunnect Unveils Quantum Security Uses Beyond Encryption Keys, Backed by DARPA and In-Q-Tel Work
PATEO Signs Physical AI MoU With Arm as Its AI Revenue Jumps 589%
New NECK ETF Bets on AI's Bottlenecks: Memory, Optics, Power and Chips
M31 and Ambiq Cut Leakage Power by About 50% With New TSMC N12e Foundation IP for Edge AI Chips
Cognex Launches In-Sight 1750 AI Wafer Reader to Cut Traceability Stoppages in Chip Fabs
CGI Partners With D-Wave to Bring Quantum Optimization to Rail, Energy and Logistics Clients
Jeffrey Archer Dies at 86: A Tribute to Britain's Great Storyteller
East of Eden on Netflix: Good and Bad Are Transient States for Steinbeck's Characters
No News From God at 25: Díaz Yanes' Heaven and Hell Comedy Still Lands
Beauty in Black Is a Racist Show, and Tyler Perry Being Black Doesn't Change That
The Guns of Navarone and Point of Impact: When the Book Beats Even the Acclaimed Movie
Samurai Films Like 13 Assassins: Why 11 Rebels Is the One That Works
Pascali's Island: Barry Unsworth's Novel and the 1988 Ben Kingsley Film Are Both Excellent
The Death of Robin Hood Review: Hugh Jackman's Unforgiven Without the Pulse
IMAX Posts Record $52 Million Global Opening With Christopher Nolan's The Odyssey
Downton Abbey: The Grand Finale and the Ethics of the Graceful Exit
Press Release Digest: Rubrik Puts Claude Mythos 5 on Code Security, Factory Hits $5 Billion, TeRAM Takes On the AI Memory Wall
Wonderful's $5B Series C: 50x Forward ARR on $154,000 of Revenue Per Employee
Nvidia (NVDA) Buys Hugging Face for $12.9B, Under the $13B Floor Hugging Face Floated Three Days Earlier
Apple's Chinese Memory Push Is a Precedent Problem for $MU and $SNDK, Not a Volume Problem
NYC Sidewalk Sheds and Local Law 11: Why the Shed Is Cheaper Than the Repair
Robots.txt vs Noindex: Why Blocking Crawlers Does Not Remove Pages From Search
Meta's Hyperion Data Center in Louisiana Was Negotiated With Tax Breaks and Little Public Input
Kimi K3 Weights Released as Washington Debates Banning Chinese AI Models
Enigma Raises $70M for Human-Robot Interaction as Multiverse Raises $570M to Shrink Models
Infinity.inc Raises $15 Million to Build AI Inference Software for Any Chip

Copyright © 2022 Exclusive.org

Technologies, Market Analysis & Market Research