Skip to content
Siteon
Contents Hosting and delivery

Docs

Hosting and delivery

Hosting on every plan (siteon deploy, rollback, subdomains, propagation time, suspension; the Free plan shows a "Made with Siteon" badge), and how to keep your current hosting (Cloudflare Pages relay, data-embed).

Siteon is infrastructure for AI-built websites: it adds hosting, a CMS your client can edit, contact forms, analytics and control from AI agents (CLI and MCP) around static HTML built with AI or by hand, so it is ready to ship.

Three ways to host

There are three ways to put CMS values into your HTML. Forms and traffic-source recording work with the script tag alone in all three.

Siteon hostingCloudflare Pages relaydata-embed (other hosting)
Where files liveSiteon (published with siteon deploy)Your Cloudflare PagesYour hosting
When values are insertedBefore delivery (server side)Before delivery (server side)After the page renders (browser side)
Search engines and visitors without JavaScriptReceive the page with values filled inReceive the page with values filled inMay pick up the static text
Flicker on loadNoneNoneStatic text shows for a moment
Per-article pages, tag pages, article urlAvailableAvailableNot available
GTM ID injection from the dashboardAvailableAvailableNot available (use data-gtm)
Time for edits to go liveWithin 15 secondsUp to 1 minuteUp to 1 minute

Hosting (siteon deploy)

Publish a built folder (dist/ or similar) with a single command. You can publish on the Free plan; paid plans (Lite, Standard, Business) add more storage, custom domains Soon and badge removal. There's no extra charge for hosting.

  • Sites are published at https://<subdomain>.siteun.com/
  • CMS values are embedded into HTML before delivery. You need neither relay files nor data-embed. Keep the script tag in place for forms and traffic-source recording
  • Everything is done from the CLI and MCP server. The API key needs the deploy:write scope
  • Free sites can also go live at https://<subdomain>.siteun.com/ and are indexed by search engines. A small "Made with Siteon" badge appears in a corner of the page (not on paid plans). Free sites can deploy up to 20 times a day, and every deploy is scanned automatically (a suspicious version is held instead of going live)
  • Custom domains and badge removal come with paid plans (from $4/mo)

Your first deploy

siteon hosting enable --site hanaya     # First time only. Picks the subdomain
siteon deploy ./dist --site hanaya      # Publishes. Prints the URL
  • If you don't specify one, the subdomain is the site ID
  • Subdomains use 3 to 63 lowercase letters, digits, and hyphens (no hyphen at the start or end). Reserved words such as www, admin, and mail, and names containing banking, payment, or major brand names, are not allowed
  • If the site ID doesn't meet these rules, the command stops with subdomain_required. Run it again with --subdomain another-name
  • A subdomain that was once in use cannot be taken by another account for 90 days after it is released (to prevent link hijacking)
  • You can change a site's subdomain up to 3 times a month. You can also change it in the dashboard under "Hosting" → "Subdomain"
  • If you omit --site, the id in sitekit/site.json is used

What happens on deploy

  1. The CLI reads the files in the folder and computes a sha256 for each one
  2. It uploads only the files the server doesn't have yet (6 in parallel). Files unchanged since the last deploy are not sent
  3. Once everything has arrived, the live version is switched over in one step. If anything fails midway, the live site stays on the previous version

If the connection drops, the CLI retries the same operation, so nothing is published twice. On failure, the CLI cancels the reservation for anything it had already uploaded before exiting.

Files you can deploy

ItemRule
ExtensionsOnly html htm css js mjs json txt xml svg png jpg jpeg webp avif gif ico woff woff2 ttf mp4 webm pdf webmanifest map. Anything else (exe zip apk, etc.) is stopped with file_type_not_allowed before upload
Reserved pathsPaths starting with admin/, v1/, or _sk/ cannot be used (reserved_path; case-insensitive)
DotfilesNames starting with a dot, such as .git, .env, and .DS_Store, are not uploaded. Only .well-known/ is published
Single fileUp to 25MiB (file_too_large)
Storage, file count, retained versionsDepends on the plan (table below)
FreeLiteStandardBusiness
Storage (total of retained versions)20MB200MB1GB5GB
Files per deploy2001,0005,00020,000
Retained versions (including the live one)252050

Storage is the total size of the files used by retained versions. Files with identical content count once across versions. Photos uploaded from the dashboard are counted separately.

How URLs map to files

RequestReturns
/index.html
/aboutabout.html (or an extensionless file named about, if present)
/about/about/index.html
/about.html, /index.html, /about/index.html301 to the form without the extension or index.html (/about, /, /about/), keeping the query string
/about/ (no about/index.html, but about.html exists)301 to /about
/about (about/index.html exists)301 to /about/
/sitemap.xml, /robots.txtYour file if you deployed one; otherwise generated automatically (see below)
Not foundThe site's 404.html with status 404 if it exists, otherwise a shared 404 page

Variants are redirected with a 301 so each page has exactly one URL. Links with .html still reach the page, but we recommend writing links without the extension.

Automatic sitemap.xml and robots.txt: if you don't deploy your own, we build sitemap.xml from your published HTML (excluding 404.html, templates whose path has a segment starting with _, and noindex pages) plus per-article pages. robots.txt allows everything and points to the sitemap. Files you deploy yourself are never overwritten.

  • Per-article pages, tag pages, and per-language paths work the same way as with the relay (A page per article)
  • Language prefixes come from locales in schema.json. The first language is served at /, the others at /<language code> (for example, ["ja", "en"] gives ja:/,en:/en). The article page path is news. Ways to change either are in preparation Soon

Propagation time and caching

Switching the live version, rollback, saving in the dashboard, and suspension all reach every page within 15 seconds. Files already cached in a visitor's browser may look stale for the periods below.

FileBrowser cache
HTMLRevalidated every time (no-cache)
Files with a hash in the name (app.3f9a8b7c.js, etc.), and anything under _astro/ or assets/1 year (immutable)
Images, fonts, video, PDF1 day
Everything else (CSS, JS, JSON, etc.)5 minutes

If you turn on your build tool's "hash file names" option, new files are served the moment you publish. To check how a page looks right after saving, use the dashboard preview.

Rolling back to an earlier version

siteon deploys --site hanaya              # List versions (* marks the live one)
siteon rollback --site hanaya             # Roll back one version
siteon rollback <deploy_id> --site hanaya # Roll back to a specific version

You can only roll back to retained versions (see the table above for how many are kept). Older versions are removed as new deploys push them out (deploy_not_retained).

Search engines

Site stateSearch enginesPage
Paid planIndexedAs is
Free, or a site treated like Free after cancellation or 31 days of unpaid invoicesIndexedA small "Made with Siteon" badge appears in a corner of the page
Shared subdomain while a custom domain is in use SoonNot indexed (canonical points to the custom domain)As is

When a site goes offline and when it doesn't

Delivery does not stop for late payment, cancellation, or exceeding limits. A site stops being served only in these cases:

StateResponse
Suspended after an abuse report (phishing, etc.) or an inspectionEvery page returns a 451 suspension page
Site deleted or account closed404
Never published404 "Not published yet"
Siteon cannot check the site's state because of an outage on our side503 (retry in a few seconds)
  • deploy is still accepted while a site is suspended (so you can fix it before appealing). The new version goes live once the suspension is lifted
  • From day 8 to day 30 of an unpaid invoice, new deploys are refused (402). After cancellation or from day 31 unpaid, the site is treated like Free and can deploy within the Free plan's limits. In every case the live version keeps being served (for behavior by payment state, see Plans and limits)

Custom domains Soon

You can publish on your own domain (www.example.com); requests are not open yet.

  • www is the primary host: point www.example.com at Siteon with a CNAME. You also add a TXT record to verify ownership
  • example.com (without www) redirects to www
  • Lite allows 1, Standard 3, and Business 10 (a www host and its non-www form count as one)
  • Once a custom domain is active, the shared subdomain is set to stay out of search, and the custom domain is declared as the canonical URL

The DNS records to add, examples by DNS provider, and what each status and error means are in Custom domains and allowed origins.

Pre-publish inspection and abuse reports

Every deploy inspects the HTML automatically. If it finds a password field, a form that submits to another site, or a brand name next to sign-in wording, the Free plan does not publish and stops with 422 check_failed (the live version keeps serving). On the Free plan the URL is also scanned before it goes live. Paid plans publish first and send the site for inspection.

A site being abused (phishing and the like) can be reported through the form at https://siteun.com/report.

Cloudflare Pages relay

Files to add

Add two relay files to your site's functions/ directory.

functions/_middleware.ts
functions/_cms/render.ts
  • _cms/render.ts is generated. Don't edit it (edits will conflict with future updates)
  • A public way to distribute these files (download, CLI) is in preparation Soon

Environment variables

Set these in the Pages project's vars (in wrangler.jsonc or the Cloudflare dashboard).

VariableRequiredDescription
CMS_SITERequiredSite ID
CMS_ORIGINRequiredhttps://cdn.siteon.dev (no trailing slash)
CMS_LOCALESComma-separated language:prefix pairs. Defaults to ja:/
CMS_NEWS_PATHArticle page path, also used as the collection key. Defaults to news
CMS_INDEXABLE_HOSTSComma-separated host names allowed in search. Does nothing if omitted

If CMS_SITE or CMS_ORIGIN is missing, the relay does nothing and returns the static HTML as is.

{
  "name": "hanaya",
  "compatibility_date": "2026-08-01",
  "pages_build_output_dir": "public",
  "vars": {
    "CMS_SITE": "hanaya",
    "CMS_ORIGIN": "https://cdn.siteon.dev",
    "CMS_LOCALES": "ja:/,en:/en",
    "CMS_NEWS_PATH": "news"
  }
}

Language detection

Write CMS_LOCALES like ja:/,en:/en,zh-tw:/zh-tw. The first entry is the primary language. The language is chosen by the longest matching path prefix.

PathLanguage
/, /about/ja (unknown paths also fall here)
/en, /en/, /en/about, /en.htmlen
/englishja (a different page that merely starts with the prefix doesn't match)
/zh-tw/news/xzh-tw

Accept-Language is ignored. The same URL shows the same language to everyone.

Keeping out of search (CMS_INDEXABLE_HOSTS)

If a preview host (such as *.pages.dev) and your production host serve the same content, they are treated as duplicate pages, and the preview can end up in search results. List only your production hosts in CMS_INDEXABLE_HOSTS, and HTML served from any other host gets X-Robots-Tag: noindex.

"CMS_INDEXABLE_HOSTS": "www.example.com,example.com"
ValueMeaning
Unset or emptyDoes nothing
www.example.com,example.comnoindex on HTML from any host other than these two
-No host is indexed (when you don't have a production domain yet)
  • Matching is exact (listing www.example.com does not allow a.www.example.com). Case-insensitive
  • Only HTML gets the header (not images or CSS)
  • If the site already sends X-Robots-Tag (via _headers or similar), it is not overwritten

Propagation time

The public JSON is cached for 60 seconds, and HTML returned by the relay also has max-age=60. Once an editor clicks "Publish", every page is updated within 1 minute. No site redeploy is needed.

When the CMS is down

The relay returns the static HTML as is. Whether the fetch fails, times out (5 seconds), or returns broken JSON, the relay just serves the page without embedding, and the site stays up.

Article pages are the only exception. If the fetch succeeds but the ID isn't found, the relay returns 404. If the fetch fails, it returns the template as is.

Checking that it works

When the relay embeds values, the response gets an x-sitekit-rendered: 1 header. Check with GET. curl -I (HEAD) won't show it.

curl -sD - -o /dev/null https://example.com/ | grep -i sitekit
curl -s https://example.com/ | grep -o 'data-sk-rendered="1"'

If it's missing, an environment variable isn't set, the relay can't reach the CMS, or the page isn't HTML (200).

Other hosting (data-embed)

On hosts other than Cloudflare Pages (such as static hosting that can't run functions), add data-embed to the script tag.

<script src="https://cdn.siteon.dev/sitekit.js" data-site="hanaya" data-embed defer></script>
  • After the page renders, the script fetches the public JSON and fills values into the data-cms markers
  • It replaces the entire contents of document.body. Event listeners attached to elements inside body are lost (forms are re-bound automatically). Avoid it if your site has its own JavaScript
  • If the fetch fails, it does nothing and the static HTML remains
  • Per-article pages (/news/<id>), tag pages, and article url are relay features and are not available

On sites that use the relay, don't add data-embed. Embedding can't run twice (the second pass overwrites the first). Relayed pages carry data-sk-rendered="1" and the script skips them, but don't combine the two in the first place. The script without data-embed is meant to be used together with the relay (it's needed for forms and traffic-source recording).

This page is also available as Markdown. For AI agents, pass llms.txt (contents) or llms-full.txt (full text).