htmlhost.co

Documentation

Everything you need to deploy, manage, and edit HTML sites from the browser, terminal, or API.

Quick start

Get a live site in under 30 seconds. Sign in with Google, GitHub or an email link, then publish. Upgrade to Pro for more sites, longer TTLs, and custom domains.

Option 1: Browser

Go to htmlhost.co, paste or drop an HTML file, pick a TTL, and hit Publish. Done.

Option 2: CLI

bash# Install globally
npm i -g htmlhost-cli

# Authenticate — opens your browser to htmlhost.co/settings#keys
htmlhost login

# Deploy current directory (shows file list, asks for confirmation)
htmlhost deploy
# ✓ Live at https://bold-fern-x3k.htmlhost.co
#   Linked → .htmlhost (future deploys update this site)

Option 3: cURL

bashcurl -X POST https://htmlhost.co/api/sites \
  -H "Authorization: Bearer hh_live_your_token_here" \
  -H "Content-Type: application/json" \
  -d '{"html":"<h1>Hello World</h1>","ttl":"7d"}'

CLI reference

The htmlhost CLI is a zero-dependency Node.js tool. Requires Node 18+.

bash# Install
npm i -g htmlhost-cli

# Update to the latest version
npm update -g htmlhost-cli

# Check your installed version
htmlhost --version

htmlhost login

Authenticate with an API token. Running this command automatically opens Settings → API keys in your browser. Paste the token back in the terminal when prompted. Credentials are saved to ~/.htmlhostrc.

htmlhost deploy [file|dir]

Deploy an HTML file or directory and get a live URL. Passing a directory (or no argument) deploys the entire folder as a multi-page site — HTML pages and all other assets (images, CSS, JS, fonts) are packaged and uploaded together. Passing a single file deploys just that file with local assets auto-inlined.

Parameter
Type
Description
--ttl <value>stringSet TTL: 1d, 7d, 30d, never
--slug <slug>stringRe-deploy to a specific site
--title <title>stringSet the site title
--newflagForce a new site (ignore .htmlhost link)
--no-assetsflagSkip asset inlining for single-file deploys
--no-pull-checkflagSkip remote change detection before re-deploy
--jsonflagOutput result as JSON (for CI/CD)

Multi-page deploy

Deploy a directory as a complete multi-page website — HTML pages and all other files (images, CSS, JS, fonts) are uploaded together. Point htmlhost deploy at a folder (or run it with no arguments from your project root). The CLI shows you a file tree and asks for confirmation before uploading. Each HTML file maps to a clean URL:

bash# Deploy current directory
htmlhost deploy

# Deploy a specific directory
htmlhost deploy ./my-project

# File mapping:
#   index.html       → /
#   about.html       → /about
#   contact.html     → /contact
#   blog/index.html  → /blog
#   blog/post-1.html → /blog/post-1

# ✓ Live at https://bold-fern-x3k.htmlhost.co
#   3 pages · 5 assets · 30d TTL

Add a 404.html file to your project and it will be served automatically when visitors access a non-existent page. Page limits: Free plan supports 10 pages, Pro supports 100.

Automatic asset processing

When you deploy, the CLI scans your HTML for local file references and makes the deployed site fully self-contained. CSS and JS files are inlined directly into the HTML, while binary assets (images, fonts, SVGs, PDFs) are uploaded to your media library with URLs rewritten automatically.

If a CSS file contains url() references (e.g. background images, font files), those assets are uploaded first and the URLs inside the CSS are rewritten before the stylesheet is inlined.

CSS/JS files ≥ 512 KB are uploaded to the media library instead of inlined, to avoid bloating the HTML. Files larger than 10 MB are skipped entirely. External URLs (https://, data:, etc.) are left untouched.

bash# Deploy with automatic asset processing (default)
htmlhost deploy
# ✓ Found 4 local assets to process
#
# ✓ styles.css (4.2 KB) → inlined as <style>
#   ✓ hero.png (340 KB) → https://htmlhost.co/m/c9d0e1f2
#   ✓ Inter.woff2 (92 KB) → https://htmlhost.co/m/d3e4f5a6
# ✓ app.js (12.1 KB) → inlined as <script>
# ✓ logo.svg (2.8 KB) → https://htmlhost.co/m/a1b2c3d4
#
# ✓ Inlined 2 files into HTML
# ✓ Uploaded 3 assets to media library

# Skip asset processing
htmlhost deploy --no-assets

How each file type is handled:

  • <link href> — CSS files are inlined as <style> blocks (with nested url() assets uploaded first)
  • <script src> — JS files are inlined as <script> blocks
  • <img src>, <img srcset> — images uploaded to media library
  • <video src>, <video poster>, <audio src> — media uploaded
  • url() in <style> blocks, inline styles, and external CSS files — assets uploaded, URLs rewritten
  • Fonts (WOFF, WOFF2, TTF, OTF), SVGs, PDFs, ICO — uploaded to media library

Project linking (.htmlhost)

After the first deploy, the CLI saves the site slug to a .htmlhostfile in your project directory. On subsequent deploys, the CLI automatically re-deploys to the same site — no prompt needed for directory deploys. For single-file deploys, you'll be asked:

text? This project is linked to bold-fern-x3k.htmlhost.co

    1. Overwrite existing site
    2. Create a new site instead
    3. Cancel
    4. Always overwrite (remember for this project)
    5. Always create new (remember for this project)

  Enter choice (1-5):

Choosing option 4 or 5 saves the preference to .htmlhost, so you won't be asked again. Use --new to skip the prompt and force a new site.

bash# First deploy — creates site and .htmlhost link
htmlhost deploy

# Second deploy — re-deploys to the same site (directory: Y/n confirm; file: menu)
htmlhost deploy

# Deploy a specific file
htmlhost deploy contact.html

# Force a new site (bypass .htmlhost)
htmlhost deploy --new

htmlhost agents [dir]

Building with an AI coding tool (Claude Code, Codex, Cursor, Copilot…)? Run this once in your project. It writes htmlhost's instructions (hosting rules, the CLI, and how to build forms that deliver) into AGENTS.md and adds an @AGENTS.md import to CLAUDE.md, so your AI builds features that work on htmlhost. Only a marked block is managed, so your own notes in those files are kept. The instructions are added automatically by htmlhost cloneand kept up to date on each deploy. Agent files are never deployed.

bashhtmlhost agents              # add or refresh the instructions
htmlhost agents --print      # print them instead
htmlhost agents --no-claude  # leave CLAUDE.md alone

Tools that can browse can also read the guide directly at htmlhost.co/agents.md.

htmlhost list

List all your sites in a formatted table showing slug, URL, size, TTL, and expiry.

htmlhost delete <slug>

Delete a site by slug. Use --force to skip the confirmation prompt.

htmlhost open <slug>

Open a deployed site in your default browser.

bashhtmlhost open bold-fern-x3k

htmlhost whoami

Show the authenticated user's email, handle, and plan.

htmlhost logout

Remove saved credentials from ~/.htmlhostrc.

htmlhost recipients [slug]

Manage form-notification recipients for a site. Without a subcommand, lists all recipients and their status. If no slug is given, the site is detected from the .htmlhost file in the current directory.

bashhtmlhost recipients my-site                      # list recipients
htmlhost recipients add my-site user@example.com  # invite a recipient
htmlhost recipients remove my-site user@example.com  # remove (prompts for confirmation)
htmlhost recipients resend my-site user@example.com  # resend a pending invitation

Use --force to skip the confirmation prompt on remove, and --json for machine-readable output.

Sync & pull

The CLI supports bidirectional sync with htmlhost.co. When you or a collaborator edits a site through the web editor, you can pull those changes back to your local project. The CLI also detects remote changes automatically before every re-deploy.

htmlhost status [dir]

Like git status for a linked folder: shows which site it deploys to, whether the live site changed since your last deploy or pull, and what the next htmlhost deploy would add, modify, or remove. It never writes anything.

Parameter
Type
Description
--jsonflagOutput the status as JSON
bashhtmlhost status

# Example output:
# ● Linked to my-site.htmlhost.co (folder)
# ✓ Live site unchanged since your last sync (v5)
#
#   Changes to deploy:
#    + new:      contact.html
#    ~ modified: index.html
#    - removed:  old.html (only on the live site; deploy deletes it)

htmlhost pull [slug]

Download remote changes from an htmlhost.co site to your local project. If the project is linked via .htmlhost, the slug is detected automatically. Otherwise, pass it as an argument.

Parameter
Type
Description
--force, -fflagPull without confirmation prompt
--dry-runflagShow what would change without writing files
bash# Pull changes for the linked site
htmlhost pull

# Pull a specific site by slug
htmlhost pull my-site

# Show what would change without writing
htmlhost pull --dry-run

# Example output:
# ● Pulling from my-site.htmlhost.co…
#   Version 5 · Updated 2h ago
#
#    + New:       about.html (1.2 KB)
#    ~ Modified: index.html
#    ~ Modified: css/style.css
#
# ● 1 new, 2 modified files to pull
# ? Pull these changes? (y/N)

Pulled files overwrite local versions. Files that exist locally but not on the remote are left untouched — the CLI never deletes local files during a pull.

htmlhost clone <slug> [directory]

Clone a deployed site into a new local directory. This is useful for sites that were created through the web editor and haven't been deployed from the CLI before. After cloning, use htmlhost deploy to push changes and htmlhost pull to sync remote edits.

bash# Clone into a new directory named after the slug
htmlhost clone my-site
# → creates ./my-site/ with all files + .htmlhost link

# Clone into a specific directory
htmlhost clone my-site ./my-project

# After cloning:
cd my-site
htmlhost deploy   # push changes
htmlhost pull     # sync remote edits

Use --force to overwrite an existing non-empty directory.

Pre-deploy sync check

When re-deploying to a linked site, the CLI automatically checks if the remote site was modified since your last deploy or pull. If remote changes are detected, you'll be prompted before overwriting:

text! Remote changes detected — my-site.htmlhost.co was modified since your last deploy.
    Remote version: 5 · Local version: 3

? What would you like to do?

    1. Pull changes first, then deploy
    2. Deploy anyway (overwrites remote changes)
    3. Cancel

  Enter choice (1-3):

Use --no-pull-check to skip this check (useful for CI/CD pipelines where you always want to deploy).

REST API

All endpoints accept Authorization: Bearer <token> headers. Generate tokens at Settings → API keys.

Base URL: https://htmlhost.co

GET
/api/sites
List all your sites with usage stats
POST
/api/sites
Deploy a new site or re-deploy to existing slug
DELETE
/api/sites?slug={slug}
Delete a site by slug
GET
/api/sites/pull?slug={slug}
Pull site content (pages + assets) for CLI sync
GET
/api/media
List your media files (supports ?search=, ?type=, ?limit=, ?offset=)
POST
/api/media
Upload a media file (multipart/form-data)
DELETE
/api/media
Delete a media file by ID
GET
/api/tokens
List your API tokens
POST
/api/tokens
Create a new API token
DELETE
/api/tokens?id={id}
Revoke an API token
GET
/api/tokens/me
Validate token and return user info

Deploy

POST /api/sites

Create a new site or update an existing one. Supports both single-page and multi-page deploys.

Single-page deploy

Parameter
Type
Description
htmlstringRequired. The HTML content to deploy
titlestringOptional. Falls back to <title> tag or auto-generated name
ttlstringOptional. 1d, 7d, 30d, or never. Defaults to plan default
slugstringOptional. If provided, re-deploys to that existing site

Multi-page deploy

Parameter
Type
Description
pagesarrayRequired. Array of { path, html, title? } objects
pages[].pathstringURL path for the page, e.g. "/" or "/about"
pages[].htmlstringHTML content for this page
pages[].titlestringOptional. Page title
titlestringOptional. Site-level title
ttlstringOptional. 1d, 7d, 30d, or never
slugstringOptional. Re-deploy to existing multi-page site
json// Multi-page deploy example
{
  "pages": [
    { "path": "/", "html": "<html>Homepage</html>" },
    { "path": "/about", "html": "<html>About</html>" },
    { "path": "/contact", "html": "<html>Contact</html>" }
  ],
  "title": "My Portfolio",
  "ttl": "30d"
}

Response

json{
  "ok": true,
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "slug": "bold-fern-x3k",
  "url": "bold-fern-x3k.htmlhost.co",
  "version": 1,
  "ttl": "7d",
  "expiresAt": "2026-05-11T08:00:00.000Z"
}

GET /api/sites

Returns all your sites and aggregate usage stats.

json{
  "sites": [
    {
      "id": "...",
      "slug": "bold-fern-x3k",
      "title": "My Portfolio",
      "url": "bold-fern-x3k.htmlhost.co",
      "sizeBytes": 4200,
      "size": "4.1 KB",
      "ttl": "7d",
      "expiresAt": "2026-05-11T08:00:00.000Z",
      "createdAt": "2026-05-04T08:00:00.000Z",
      "updatedAt": "2026-05-04T08:00:00.000Z"
    }
  ],
  "usage": {
    "siteCount": 1,
    "totalBytes": 4200,
    "maxBytes": 524288000,
    "plan": "free"
  }
}

DELETE /api/sites

Delete by slug or UUID.

bashcurl -X DELETE "https://htmlhost.co/api/sites?slug=bold-fern-x3k" \
  -H "Authorization: Bearer hh_live_your_token"

Media library

Upload images, fonts, and other assets. Each file gets a permanent URL served from Cloudflare's edge CDN. Use these URLs in your HTML.

Tip: When you htmlhost deploy, local assets are automatically uploaded to your media library and the HTML references are rewritten — no manual upload step needed. See the CLI → Automatic asset uploading section for details.

Upload via cURL

bashcurl -X POST https://htmlhost.co/api/media \
  -H "Authorization: Bearer hh_live_your_token" \
  -F "file=@logo.png"

Upload via deploy

When you run htmlhost deploy, local assets are automatically uploaded to your media library and HTML references are rewritten — no manual upload step needed. See the CLI → Automatic asset processing section for details.

Allowed file types

Images (PNG, JPG, GIF, SVG, WebP, AVIF, ICO), fonts (WOFF, WOFF2, TTF, OTF), documents (PDF), video (MP4, WebM), and audio (MP3, OGG, WAV).

AI visual editing

The browser editor includes an AI assistant that can edit your HTML visually. Click any element, describe the change in natural language, and watch it update live.

Built-in AI

Free plan: 10 AI edits per day. Pro plan: 100 edits per day. Uses platform-managed API keys — no setup required.

Bring your own key (BYOK)

For unlimited edits, add your own API key from any supported provider. Keys are stored in your browser's localStorage and never sent to our servers.

Provider
Models
Key prefix
OpenAIGPT 5.5, GPT 4.1, GPT 4.1 Minisk-...
AnthropicClaude Opus 5, Claude Sonnet 5, Claude Haiku 4.5sk-ant-...
GoogleGemini 3.1 Pro, Gemini 2.5 Pro, Gemini 2.5 FlashAIza...

Forms

Any form on your site can deliver straight to you, with no backend and no third-party form service. Visitors' messages land in your dashboard and your inbox, and you decide what visitors see after they send.

Add a form

  • In the editor, open Library → Elements → Forms and drag a Contact Form or Email Signup onto your page. Add fields (input, text area, dropdown, checkbox, file upload) by dropping them inside the form.
  • Or ask the AI: “add a contact form with name, email and message”. Forms the AI writes are set up to deliver automatically.
  • Publish. Forms only send from your published site; in the editor preview, pressing Send shows what a visitor would see instead.

Writing your own form

Point any form at /__hh/form with method="post" and give every field a name. A hidden _form field names the form, so one site can run several (a contact form and a newsletter signup, say).

html<form action="/__hh/form" method="post">
  <input type="hidden" name="_form" value="Contact">
  <input name="name" placeholder="Your name" required>
  <input name="email" type="email" placeholder="you@example.com" required>
  <textarea name="message" required></textarea>
  <button type="submit">Send</button>
</form>

Your published site adds a small script that sends the form in the background, so visitors stay on the page. Without JavaScript the form still works: it sends normally and shows a confirmation page.

For the same spam protection as Library forms, add this hidden trap field inside the form. People never see it; bots that fill it in are discarded:

html<div aria-hidden="true" style="position:absolute;left:-10000px;width:1px;height:1px;overflow:hidden">
  <label>Leave this field empty <input type="text" name="_hh_hp" tabindex="-1" autocomplete="off"></label>
</div>

Connect a form you already have

A form you pasted in or wrote yourself may not send anywhere yet. In the editor, click anything inside it: the AI panel shows This form doesn't send anywhere yet. Click Send to my inbox (or right-click the form → Send to my inbox). htmlhost points it at your inbox and names it for you. Publish, and it's live. Search boxes are left alone.

Once a form delivers, the same panel shows 📨, its name and how many messages it has received, with links to read them and to its settings. The 📨 also marks delivering forms in the breadcrumb and the Structure tab.

Where messages go

Open your site from the dashboard and go to Messages to read, mark as spam, or export messages to CSV. The Inbox tab on your dashboard shows messages from all your sites in one list. Each new message is also emailed to you, and replying to that email goes straight to the visitor.

Sending messages to other people

In your site's Forms tab, under Who gets new messages, click Add recipient(1 extra person on Free, up to 10 on Pro). They get one email asking them to confirm; nothing else reaches them until they do, and every email has a link to stop. They don't need an htmlhost account.

By default everyone gets every form. To split them, for example Careers to your hiring manager and Contact to sales, click All forms under a person's name and choose Only these forms. You always get everything, and you can switch your own emails off once someone else is receiving them.

You can also manage recipients from the terminal with htmlhost recipients — see the CLI section above.

How often you're emailed

In the Forms tab, Email notifications has three options. Instantly (the default) sends an email for every message. Daily summarysends one email a day, around 08:00 UTC, listing the day's messages. Paused sends nothing. Messages are always saved on the Messages tab whichever you choose.

Letting collaborators read messages

Collaborators can edit your site but can't see its messages unless you allow it. In the Forms tab, turn on Collaborators can read messages. They'll see the site in their Inbox and can read, mark and export messages; only you can delete them.

What visitors see after sending

After a successful send, a visitor either sees a confirmation message under the form or is taken to another page on your site (a “thank you” page, for example). You set this in two places:

  • For the whole site: dashboard → your site → Forms → After someone sends a form. Choose a message or a page, and optionally an error message.
  • For one form: in the editor, right-click the form (or anything inside it) → Form Settings…. Leave it on Site default, or give this form its own message or page.

A form's own setting always wins over the site default. If a form has its own message, it shows that message even when the site default is to go to a page. Pages must be on your own site and start with /; sending visitors to other websites isn't allowed. The error message, if you set one, replaces the specific reason a message couldn't be sent.

Form Settings saves your choices in the form's HTML as hidden inputs, so you can also write them by hand:

Parameter
Type
Description
_formnameThe form's name. Groups its messages in your dashboard.
_successmessageConfirmation shown under the form after sending.
_redirectpathPage on your site to open after sending, e.g. /thanks.
_errormessageShown instead of the specific reason when sending fails.
html<form action="/__hh/form" method="post">
  <input type="hidden" name="_form" value="Quote request">
  <input type="hidden" name="_redirect" value="/thanks">
  <input type="hidden" name="_error" value="Sorry, that didn't go through. Email us at hi@example.com.">
  ...
</form>

Styling the confirmation

The message appears in a <p data-hh-form-msg> added to the end of the form, withdata-state="success" or "error". It uses your page's font and colour unless you style it:

css[data-hh-form-msg] { padding: 12px 16px; border-radius: 8px; background: #ecfdf5; color: #065f46; }
[data-hh-form-msg][data-state="error"] { background: #fef2f2; color: #991b1b; }

File uploads (Pro)

Drag File Upload from Library → Elements → Forms into a form; the form is set up for files automatically. Visitors can attach up to 3 files (images, PDFs, Office documents, text or CSV). Files are checked by their content, not just their name, and kept private: you download them from the message in your dashboard, and emails only list their names.

Google Sheets (Pro)

Keep a spreadsheet of your form messages that fills itself in. Your site's Forms tab → Integrations → Google Sheetsoffers two ways. You don't connect a Google account to htmlhost for either.

Instant (recommended): each message becomes a new row within seconds.

  1. 1Create a sheet (sheets.new) and open Extensions → Apps Script.
  2. 2In htmlhost, click Copy script and paste it into Apps Script, replacing what's there. It contains your private signing secret, so don't share it.
  3. 3Click Deploy → New deployment, choose Web app, set Execute as to Me and Who has access to Anyone, then Deploy and allow access.
  4. 4Copy the Web app URL, paste it into htmlhost and click Connect & send test. A test row appears in your sheet.

New fields become new columns, rows are only ever added (so your own notes beside them stay put), and a visitor can't sneak a formula into your sheet. The script checks a signature on every request, so only htmlhost can add rows. It uses your site's single webhook slot.

Hourly link: click Create Google Sheets link and paste the =IMPORTDATA("…") formula into cell A1. Google refreshes it about once an hour. It mirrors your dashboard (read-only, oldest first), so new messages appear at the bottom. The link is shown once and works like a password: make a new one if it leaks.

Webhooks (Pro)

Send every message to your own server, or to tools like Zapier, Make or n8n. In the Forms tab → Integrations, paste an https:// URL and click Save, then Send test. Each message is POSTed as JSON a moment after it's received:

json{
  "event": "form.submission",
  "id": "5f0c…",
  "site": { "slug": "bakery", "address": "bakery.htmlhost.co" },
  "form": "Contact",
  "page": "/contact",
  "submittedAt": "2026-09-23T10:00:00.000Z",
  "email": "ada@example.com",
  "fields": [{ "name": "name", "value": "Ada" }, { "name": "message", "value": "Hi!" }],
  "files": [{ "name": "brief.pdf", "size": 48213, "type": "application/pdf" }]
}

To check a request really came from htmlhost, compare the X-Htmlhost-Signature header with an HMAC-SHA256 of the raw body, using the signing secret shown in the Forms tab:

jsconst expected = "sha256=" + crypto.createHmac("sha256", SECRET).update(rawBody).digest("hex");
const valid = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(req.headers["x-htmlhost-signature"]));

Your endpoint should reply within 5 seconds. Redirects aren't followed, and only public addresses can be used. The Forms tab shows the result of the latest delivery. Attachments are listed by name only; download them from the dashboard.

Using AI with forms

If you build locally with an AI coding tool, run htmlhost agents in your project first. It tells your AI exactly how forms work here, so the forms it writes deliver to you.

The AI knows how forms work here. Try “after this form is sent, go to the thanks page”, “change the confirmation to ‘We'll reply within a day’”, or “make the form's success message green”. In Discuss mode you can also just ask how forms work.

Spam protection & safety

  • Every delivered form includes a hidden honeypot field and a fill-time check; caught bots are discarded or filtered into Spam, which is cleared after 30 days
  • Each visitor can send 5 messages per site every 10 minutes, and 20 per hour across all sites
  • Forms can only be sent from your own site, and password-protected sites must be unlocked first
  • Fields that ask for passwords, card numbers or similar details are rejected, to protect your visitors from phishing

Limits

Free accounts receive up to 50 messages a month across all sites; Pro receives up to 5,000. We email you once if you reach the limit. File attachments are a Pro feature: up to 3 files per message, 10 MB each and 25 MB in total (images, PDFs, Office documents, text and CSV), with 2 GB of attachments a month.

Custom domains

Serve any site from your own domain. Custom domains are included with Pro, or you can buy a domain add-on for a single domain, billed yearly. Go to Settings → Domains, enter the domain and choose the site it should show. You can also start from a site in your dashboard: ⋮ → Add custom domain.

DNS setup

After you add the domain, the Domains page shows the exact record to create. Copy it from there, because the value can differ from domain to domain. It will be one of these two:

Parameter
Type
Description
example.comARoot (apex) domain. Name: @, value: the IP address shown (usually 76.76.21.21).
www.example.comCNAMEAny subdomain. Name: the subdomain part (www, blog, shop.eu…), value: the hostname shown (usually cname.vercel-dns.com).
text# Root domain (example.com)
Type:  A
Name:  @
Value: 76.76.21.21        ← use the value shown in Settings → Domains

# Subdomain (www.example.com)
Type:  CNAME
Name:  www
Value: cname.vercel-dns.com   ← use the value shown in Settings → Domains
  • Remove any other A, AAAA or CNAME records for the same name first (for example, your registrar's parking page). They conflict with the new record.
  • A root domain can't use a CNAME at most providers, so use the A record for it.
  • On Cloudflare, set the record to DNS only (grey cloud), not Proxied, so the certificate can be issued.
  • We detect your DNS provider and link to its dashboard and instructions on the Domains page.
  • Want both example.com and www.example.com? Add each as its own domain, one A record and one CNAME.

Verification & HTTPS

The Domains page checks your DNS automatically and moves through Awaiting DNS → Provisioning TLS → DNS & TLS ready. The HTTPS certificate is issued for you, with nothing to upload. Most DNS changes are picked up within minutes, but some networks can take a few hours (rarely up to 24) to see the new record, so the domain may work on one connection before another.

A site on an active custom domain doesn't expire while the domain stays connected. Forms and password protection work on custom domains exactly as they do on your htmlhost.co address.

API tokens

Tokens authenticate CLI and API requests. Generate them at Settings → API keys.

Token format

texthh_live_a91fc7b3d2e014c8f5a6b9d0e1f2a3b4c5d6e7f8

Tokens start with hh_live_ and contain 48 hex characters. The raw token is shown once at creation. We store only the SHA-256 hash on our servers.

Security

  • Tokens are hashed server-side — we never store the raw token
  • ~/.htmlhostrc is created with 0600 permissions (owner-only)
  • Revoke a token instantly from Settings — all sessions using it stop immediately
  • Maximum 10 tokens per account

Plans & limits

Limit
Free
Pro
Sites3Unlimited
Storage500 MB10 GB
Max TTL30 daysNever
AI edits10/day100/day
Expired site restore—✓
Custom domains—✓
API / CLI✓✓
Pages per site10100
Media library50 MB10 GB
Collaborators—5/site
Form messages50/month5,000/month
Form recipients1/site10/site
Form attachments—✓

What happens when a site expires?

When a site's TTL elapses, the public URL goes offline (returns a 410). The site stays in your dashboard so you don't lose track of it.

  • Free publishing is limited; deleting or expiring sites does not renew free publishing access
  • Free API/CLI publishing requires one-time browser verification at /new; contact support if unavailable
  • Free users can upgrade to Pro to restore expired sites
  • Pro users can still preview expired sites and republish with one click to bring them back online
  • Expired sites still count toward your total site quota until deleted

Questions? Reach out at hey@htmlhost.co