Skip to content
StaticBackend

Deploying a static HTML site from the terminal

Each host needs one command: curl to ship.page with no account (25 MB ZIP, link lasts 7 days), npx wrangler pages deploy for Cloudflare Pages (20,000 files) or npx surge (450 MB, free custom domains).

# ship.page: no account, prints JSON with the live url
curl -X POST https://ship.page/deploy \
  -H "Content-Type: application/zip" \
  --data-binary @site.zip

# Cloudflare Pages: logs in through the browser on the first run
npx wrangler pages deploy ./site --project-name my-site

# Surge: asks for an email and password on the first run
npx surge ./site my-site.surge.sh

# Netlify: anonymous deploy that has to be claimed within 1 hour
npx netlify-cli deploy --allow-anonymous --no-build --dir ./site --created-via cli

All four print a URL you can open right away. In our November 2026 test from Frankfurt with an 18 MB folder of 41 files, ship.page returned its URL after a median 5.6 seconds, Netlify's anonymous deploy after 6.6 seconds in its single run, and Wrangler after 9.7 seconds. The methodology page lists every command and run.

Command line deploy options compared

Tool Account Free limit Link lifetime
ship.page with curl None 10 MB HTML, 25 MB ZIP, 100 files, 10 deploys a minute 7 days, 30 after a free claim
Cloudflare Pages with Wrangler Free Cloudflare account 20,000 files, 25 MiB per file No expiry
Surge Created in the terminal 450 MB, 10,100 files, 200 publishes a day per domain No expiry
Netlify CLI, anonymous None until you claim 300 credits a month once claimed Claim within 1 hour

ship.page: curl a file or ZIP and read the JSON

ship.page takes three body types on POST /deploy: raw HTML up to 10 MB, a ZIP with Content-Type: application/zip (up to 25 MB packed and 100 MB unpacked on the free plan), or JSON with a map of files (deploy docs). The response holds url, expires_at and, for anonymous uploads, a one-time claim_token and claim_url. Free anonymous pages expire after 7 days. Claiming with a free account extends that to 30 days from upload, and Pro at $4 a month removes the expiry while you stay subscribed and raises the caps to 100 MB ZIPs and 10,000 files (limits).

To grab just the URL in a script, pipe the response through jq:

curl -s -X POST https://ship.page/deploy \
  -H "Content-Type: text/html" \
  --data-binary @index.html | jq -r .url

Cloudflare Pages with Wrangler

wrangler pages deploy uploads a folder to a Pages project; the first run opens a browser to log in, or reads CLOUDFLARE_API_TOKEN in CI. Direct uploads through Wrangler accept up to 20,000 files at 25 MiB each, against 1,000 files for drag and drop in the dashboard (direct upload docs). Sites have no expiry and take up to 100 custom domains per project on the free plan, and Cloudflare's limits page lists no bandwidth cap (Pages limits).

One quirk from our test: on a brand-new project, the per-deploy URL that Wrangler prints (https://<hash>.<project>.pages.dev) failed its TLS handshake for about 2 minutes, while the production URL https://<project>.pages.dev answered about 10 seconds after the deploy command started. If a fresh deploy link errors, open the production URL instead.

Surge from npm

Surge needs Node.js 18 or later. The first npx surge run asks for an email and password and creates the account in the terminal, then publishes to a *.surge.sh name or your own domain (getting started). Custom domains and their certificates are free, Surge says nothing is metered, and a project can hold 450 MB across 10,100 files. Surge Pro is $30 a month and adds password protection and a router for redirects (Surge pricing).

Netlify's anonymous deploy flag

netlify deploy --allow-anonymous publishes without logging in and returns a temporary project URL that Netlify's docs say you can claim within one hour (Netlify deploys). Two things to know. First, after 3 of our anonymous deploys in one day, the fourth was refused with "You've reached the daily limit for anonymous deploys". Second, netlify-cli 27.17.0's source treats an anonymous deploy without --created-via cli as a password-protected Drop site, which is why the command above passes that flag. Claimed projects move to the Free plan: $0 with 300 credits a month (Netlify pricing).

Deploying from GitHub Actions

For CI, ship.page has an official action, bitgate/ship-page-action, that deploys a folder from a workflow. Cloudflare's wrangler-action runs the same pages deploy command:

- uses: bitgate/ship-page-action@v1
  with:
    path: ./site

- uses: cloudflare/wrangler-action@v3
  with:
    apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
    accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
    command: pages deploy ./site --project-name my-site

Keep tokens in repository secrets, never echo them in logs, and scope a Cloudflare token to the one account and the Pages permission it needs. A leaked token with account-wide rights can do far more damage than redeploy a page.

Looking for the original StaticBackend CLI

If an old link sent you here for the StaticBackend command line tool: that tool belonged to the open-source Go backend at github.com/staticbackendhq/core, which is now archived. This page covers deploying static files from a terminal with tools that are maintained in 2026.

Frequently asked questions about command line deploys

How do I deploy an HTML file with curl?

Send it to ship.page: curl -X POST https://ship.page/deploy -H "Content-Type: text/html" --data-binary @index.html. No account or token is needed. The JSON response includes the live URL, which stays up for 7 days, or 30 days after you claim it with a free account. Files up to 10 MB work.

Can I deploy to Cloudflare Pages without the dashboard?

Yes. Run npx wrangler pages deploy ./site --project-name my-site. Wrangler logs you in through the browser once, or uses a CLOUDFLARE_API_TOKEN in CI, and uploads up to 20,000 files at 25 MiB each. The project is created on the first deploy if it does not exist yet.

Why did my Netlify anonymous deploy disappear?

Anonymous deploys are temporary. Netlify's docs say you can claim one within an hour of deploying; after that the link should be treated as gone. Log in with netlify login and claim it inside the hour, or deploy from a logged-in CLI so the site belongs to your free account from the start.