How we test HTML hosts
We upload real files, time how long it takes for a working link to serve the exact page we sent, and score each host out of 100 using our published rubric, the htmlbench Index. The timing run took place between 03:32 and 03:41 UTC on November 15, 2026; prices and limits were checked in November 2026.
Stop the clock
At the first HTTP 200 that serves the exact bytes we uploaded. Timed: ship.page, Netlify Drop and Cloudflare Pages.
Score the free plan
5 criteria, 100 points, the same for every host.
Publish the median
Dated November 15, 2026, with all 43 runs in one CSV.
Every run is published as raw data, so anyone can check the numbers or run the test again.
The five criteria in the htmlbench Index
The Index scores getting a page online without an account, then keeping its link up. Can you publish before signing up? That matters to the job we're measuring, and the scoring gives separate weight to publishing without an account and how long that anonymous link lasts.
Here's the full rubric:
| Criterion | Points | How points are given |
|---|---|---|
| Publish with no signup | 25 | No account 25; account made in the terminal 10; account made in the browser 8; account plus a Git repository 4 |
| Anonymous link lifetime | 30 | No expiry 30; 30 days 24; 7 days 18; 1 hour 3; no link without an account 0 |
| Upload from the browser | 10 | Before any account 10; after making an account 5; command line only 2 |
| Free upload size | 15 | 100 MB or more, or no published cap 15; 25 MB 11; 10 MB 8; under 10 MB 3 |
| Cost to keep the link up | 20 | $0 20; up to $5 a month 15; up to $10 a month 10; more 4 |
Speed isn't in that total.
How each host's score adds up
| Host | Signup | Lifetime | Browser upload | Upload size | Cost | Index |
|---|---|---|---|---|---|---|
| ship.page | 25 | 18 | 10 | 11 | 15 | 79 |
| Netlify Drop | 25 | 3 | 10 | 15 | 20 | 73 |
| Cloudflare Pages | 8 | 0 | 5 | 15 | 20 | 48 |
| Surge | 10 | 0 | 2 | 15 | 20 | 47 |
| GitHub Pages | 4 | 0 | 5 | 15 | 20 | 44 |
| Tiiny Host | 8 | 0 | 5 | 3 | 20 | 36 |
ship.page scores 79: 25 for publishing without signup, 18 for anonymous links that last 7 days, 10 for browser uploads, 11 for its 25 MB ZIP cap and 15 because Pro at $4 a month removes the expiry.
Netlify Drop scores 73: 25 for no signup, 3 because unclaimed deploys go within 1 hour, 10 for browser uploads, 15 for no published size cap and 20 because a claimed site is $0. The lifetime and cost scores cover different things here: the unclaimed link and the claimed site.
Cloudflare Pages scores 48: 8 for browser signup, 0 for anonymous lifetime, 5 for browser uploads after signup, 15 for size, 20 for cost. Surge scores 47: 10 for terminal signup, 0 for anonymous lifetime, 2 for command-line-only uploads, 15 for size, 20 for cost.
GitHub Pages scores 44: 4 for needing an account plus a Git repository, 0 for anonymous lifetime, 5 for browser uploads, 15 for size, 20 for cost. Tiiny Host scores 36: 8 for browser signup, 0 for anonymous lifetime, 5 for browser uploads, 3 for its 3 MB cap, 20 for cost.
Any host that needs an account before producing a link gets 0 for anonymous lifetime.
The November 15, 2026 timing run
We used two payloads. Payload A was one HTML file of 1,500,000 bytes. Payload B contained 24 files totalling 7,670,169 bytes: index.html, about.html, styles.css, app.js, 10 PNG and 10 JPG images. ship.page received Payload B as one ZIP.
Every file got a fresh random marker on every run, and each run created a new site or project.
We counted a page as live at the first HTTP 200 whose body matched the uploaded index.html byte for byte, polling every 200 ms from the moment the upload command started, with a 240 s timeout.
The published runs took place from 03:37 to 03:41 UTC through two proxy exit IPs in Frankfurt. Rounds 1 to 3 used one IP; rounds 4 to 6 used the other. All three hosts in a round used the same IP. Each round tested one payload on all three hosts, with 3 rounds per payload.
Earlier direct runs, from 03:32 to 03:36 UTC on a server in Frankfurt, gave ship.page 1.91 s for the page and 3.68 s for the site. Cloudflare Pages took 8.53 s and 9.22 s.
We used curl 8.14.1 for ship.page, netlify-cli 27.19.0 for Netlify's anonymous deploy and wrangler 4.161.0 for Cloudflare Pages. For Cloudflare, the clock starts at the deploy command, after project creation.
These are the commands each timed run used:
# ship.page with curl 8.14.1 (the ZIP goes with Content-Type: application/zip)
curl -sS -X POST https://ship.page/deploy -H "Content-Type: text/html" --data-binary @index.html
# Netlify, anonymous deploy, netlify-cli 27.19.0
netlify deploy --allow-anonymous --no-build --dir site --created-via cli --json
# Cloudflare Pages, wrangler 4.161.0 (the clock starts at the deploy command)
wrangler pages project create <name> --production-branch main
wrangler pages deploy site --project-name <name> --branch main --commit-dirty=true
The published medians, with every counted run included:
| Host | Payload | Runs | Median until the URL came back | Median until live | Fastest to slowest |
|---|---|---|---|---|---|
| ship.page | 1.5 MB HTML page | 3 | 2.20 s | 3.14 s | 2.67 s to 3.40 s |
| Netlify, anonymous CLI | 1.5 MB HTML page | 3 | 9.78 s | 10.11 s | 7.18 s to 44.43 s |
| Cloudflare Pages, Wrangler | 1.5 MB HTML page | 3 | 9.11 s | 9.52 s | 9.47 s to 10.02 s |
| ship.page | 7.7 MB site, 24 files | 3 | 4.65 s | 5.15 s | 4.50 s to 5.99 s |
| Netlify, anonymous CLI | 7.7 MB site, 24 files | 3 | 9.03 s | 9.42 s | 6.99 s to 12.49 s |
| Cloudflare Pages, Wrangler | 7.7 MB site, 24 files | 3 | 9.35 s | 9.76 s | 9.55 s to 14.82 s |
Runs that didn't make the table
Netlify refused the 4th anonymous deploy of the day from the server's IP: "You've reached the daily limit for anonymous deploys". That's why we ran the published set through proxies for all three hosts.
And 3 earlier Netlify runs from the server returned output with no URL our script could read. Those weren't counted.
One published Netlify page run took 44.4 s. We kept it. The median is still 10.11 s.
Creating each Cloudflare project took 2.5 to 5.4 s (median 2.9 s across 18 projects). That time is excluded from the deploy timing. All Cloudflare test projects were deleted afterwards.
What the Index doesn't score
We report speed, but don't score it. We also don't score custom domains, password protection, badges, analytics, editors, bandwidth and visit allowances, file counts, uptime or support.
Surge, GitHub Pages and Tiiny Host were scored but not timed, because each needs an account, a terminal login or a repository before the first link.
Where the prices and limits come from
Every price and limit comes from the vendors' own pricing, documentation and help pages, checked in November 2026. Each benchmark page lists the pages it relied on.
Sources checked November 2026
- ship.page docs: the deploy endpoint
- ship.page docs: accepted upload formats
- Netlify docs: getting started with the CLI
- Netlify docs: deploys and the claim window
- Cloudflare docs: Wrangler command reference
- Cloudflare docs: uploading without Git
- curl project site
More from htmlbench