A domain opens a site only when four things are true. Each has its own owner and its own failure:
| # | What | Where it lives | Set by | When it's missing |
|---|---|---|---|---|
| 1 | DNS sends the name to the platform | The owner's DNS host (or Home › Domains, for a domain bought there) | The owner | dig shows another address, or nothing. The browser can't connect, or opens the old host. |
| 2 | A route and a certificate for the name on the platform's edge | A Kubernetes ingress named <domain-with-dashes>-ingress | Connect (and Repair) | The browser warns about the certificate (TRAEFIK DEFAULT CERT) and the page is a bare 404 page not found. |
| 3 | A mapping from the name to the site | A site_domain record in the platform's root org | Connect (and Repair) | The site's own not-found screen for a different site, or a 404 for every path. |
| 4 | The site's domain field, data.domain | The site record | Connect | The site opens, but emails, canonical tags and the sitemap still use the platform address. |
Connect (Site Config › Address) does 2, 3 and 4 in one call. 1 is always the owner's job. This page connects, checks, changes, removes and repairs. For what a URL path shows once the domain works, see How a site's addresses route. The site record's fields are in Sites.
How a request finds its site
Tested on the local engine and on live sites, read-only.
- DNS sends
example.comto the platform's edge (today38.107.233.133; every*.site.appmint.appname resolves there too). - The edge (Traefik) looks for an ingress whose host is
example.comorwww.example.com. None: it answers with its own default certificate and a plain404 page not found. The request never reaches a site. (appengine resources/deploy-template/domain-ingress.tp; tested on live with an unmapped name.) - The site renderer asks the engine for the site:
GET /site/get-site/<renderer's site>/<Host header>(base-app src/lib/appmint-client.ts286–289, called fromlib/get-page-data.ts149). - The engine strips a leading
www.and looks up asite_domainrecord whosedomainNameequals the host (appengine src/site/site.service.ts467–480,src/repositories/repository.org.service.ts280–289). Upper case still matches. The port is part of the host, so a local mapping looks likephasite-phazzer.localhost:3200. - No mapping: the engine falls back to the renderer's own site name, then to its org's Default site (
site.service.ts102–117). On live, an unmapped platform address shows the renderer org's not-found screen ("Welcome to Wemeant") with a 404. Tested.
So www.example.com and example.com open the same site from one mapping. Tested: GET /site/get-site-by-hostname/www.phasite-phazzer.localhost:3200 returns phasite, and the rendered home page is identical on both names.
Which address links use
| What | Uses | Source |
|---|---|---|
A link the engine builds while serving a visitor (the site's /api calls) | The host the visitor is on. The site's /api proxy sends it as x-client-host, from the browser's Origin or Referer. | base-app app/api/[...slug]/route.ts 82–92, lib/appmint-client.ts 100; site.service.ts 1210–1216 |
| A link in an email or a job with no visitor | data.domain, else data.hostName, else <site>-<org>.site.appmint.app. https, except localhost hosts. | site.service.ts 1210–1216, 1518–1520, 1526–1560 |
| The page's canonical tag and Open Graph URL | The host asked for, except www.<domain> and the site's hostName, which point at https://<domain>. Any other name, aliases included, is its own canonical. | base-app src/lib/site-url.ts 51–63; app/[[...slug]]/page.tsx 230, 332 |
| The sitemap | data.domain, else a mapped domain that isn't a platform address, else the host asked for | site.service.ts 1626–1636, 1681–1688 |
| Studio's "View site" | data.domain, else data.hostName, else the convention | websitemint modern-site-app.tsx 413 |
Tested on live: https://reesite-reelmoment.site.appmint.app/ and https://www.reelmoment.io/ both carry <link rel="canonical" href="https://reelmoment.io"/>. Neither redirects; they serve the same page.
Ask first
- Which site? By its name (
data.name, e.g.phasite), not its id. The platform address is built from the name you send. - The exact domain, without
https://, without a path, and withoutwww.:example.com, or a subdomain likeshop.example.com.www.is served automatically. - Does the person already own it? If not, do they want to buy one here (step 2)? That costs money.
- Who runs its DNS? Their registrar, Cloudflare, or Home › Domains for a domain bought here. Can they log in there today?
- Is the domain serving something now (an old website, a shop, email)? Moving its address records takes that old site offline. Don't touch MX or TXT records: email lives there.
- Is another Appmint site already on it? One domain belongs to one site.
Steps
1. Site Config: what's there now
Read before changing anything.
- The site:
call_appengineGET /site/get-site/<site name>. Notedata.domain,data.hostName,data.aliases,data.isDefault, anddomainRecord(the site's mappings, attached bygetSite,site.service.ts119–124). - Its mappings:
call_appengineGET /site/get-site-host-mappings/<site name>. Each row is asite_domain:domainName,orgId,siteId,siteName,status. A healthy site with a domain has two: the platform address and the domain. - The address links use:
GET /site/base-url?siteName=<site name>→{ "url": "https://example.com" }. - Who owns a host:
GET /site/get-site-by-hostname/<host>returns the site that answers it, or an empty body. - DNS today, from any shell:
dig +short example.com A,dig +short www.example.com,dig +short NS example.com.
repo_find on site_domain finds nothing: the mappings live in the platform's root org, not the business's. Use the site/… routes above.
Tested on local (phazzer): get-site/phasite shows domain: null, hostName: "phasite-phazzer.localhost:3200", isDefault: true, one mapping; base-url answers http://phasite-phazzer.localhost:3200.
Hands on: the site's name, its current domain and hostName, and its mappings.
Check: Site Config › Address shows the same domain (with Edit Domain, Show DNS Settings, Remove Domain) and the Platform host.
2. Domains: buy a domain (only if they don't own one)
Home › Domains (/domains, admin users only, websitemint ui/sidebars/links.ts 21) lists the org's domains and buys new ones. Site Config's Buy Domain opens the same app on its Buy a domain drawer (site-domain-connect.tsx 131–141, components/domain/app.tsx 12–17).
Stop: buying charges the org's card and registers a domain for a year in the owner's name. Show the domain, the price and the registrant details, and wait for yes. Don't buy through the API yourself: the payment is taken by the screen, not by POST /tools/domain/buy, which registers the domain at once (appengine src/tools/tools.domain.service.ts domainBuy), and call_appengine doesn't ask for confirmation on it.
The person's path:
- Buy Domain › search a name. Search (
GET /tools/domain/search/<name>?suggest=true&provider=cloudflare, public) checks.com .net .org .io .cowhen no ending is given. The suggestions are always shown as Taken, and a name with no price from the registrar shows $0.00. - Registration Info: first and last name, email, phone, address line 1, city, state, postal code and country (one of US, CA, GB, AU, DE, FR, IN) are required; organization and address line 2 are optional. It is filled from the org's locations and saved back as the business address.
- Review & Pay: tick the registration terms, Proceed to Payment (Stripe), then the domain is registered.
What it really does (modern-domain-purchase.tsx 170–261, org-management.service.ts 470–573, tools.domain.service.ts 176–249):
- The card payment is recorded as a credit top-up to the org, not as a domain purchase. The registrar fee is paid from the platform's registrar account.
- The Cloudflare registrar path sends only the user's name and the org's system email as the registrant; the address and phone typed in step 2 aren't sent.
- On success it writes a
domain_registrationrecord:domain,registrar,status: "active",purchasedThroughUs: true,autoRenew: true,expiresAt(a year),nameservers,orderId. NomonthlyPrice, so renewal billing never picks it up. - It creates no DNS records and connects no site. Do step 3, then add the records of step 4.
Other parts of the app, and what they do:
| Where | What it does |
|---|---|
| Add Domain ("Add External Domain") | Saves a domain_registration with registrar: "external", status: "pending". Nothing else: no mapping, route or check. The window that follows lists email records only (SPF, DKIM, DMARC, with a Google SPF), not website records (domain-add-modal.tsx 47–54, dns-instructions-modal.tsx 64–96). |
| A domain › DNS Setup › Connect to site | The same POST /site/update-domain as step 3, for the site picked (domain-details.tsx 124–142). It also ignores deployError. |
| A domain › DNS Setup › Test configuration | Reads the domain's Cloudflare zone status (GET /tools/domain/cloudflare/zone/<domain>/activation), not its DNS. An outside domain always shows "Not detected yet (no_zone)", and "Confirmed" doesn't mean it resolves (domain-details.tsx 100–110). Use step 5. |
| A domain › DNS Records (bought domains only) | Add, edit and delete records at the registrar: POST /tools/domain/dns/add, /dns/modify, /dns/delete. On the Cloudflare path the screen reads the wrong record fields, so edit and delete fail, add can be sent without the domain, and TTL and priority are never sent. A failure can come back as success. Check every change with dig. |
| Link to Site (row action) | Opens the domain's drawer. It links nothing. |
There is no screen for transfers in or out, nameservers or SSL modes; their API routes exist under /tools/domain/cloudflare/* and nothing calls them. A site's certificate comes from the platform's edge, never from these.
Every tools/domain/* route except search needs someone signed in to the business, of any staff role; the site can't call them (tools.controller.ts 18–20). The registrar, DNS and Cloudflare routes act only on a domain this org bought or connected to one of its sites; any other answers 403 This domain is not connected to your organization (tools.domain.service.ts assertDomainAccess). Change DNS only on the person's yes.
Hands on: the domain, now owned, and who runs its DNS (Home › Domains for a domain bought here). Check: it appears in Home › Domains with "Ours".
3. Site Config: connect the domain
Stop: this changes the live site. It creates the domain's route and certificate request on the platform, and from then on the site's emails, canonical tags and sitemap use the new domain, even before DNS points there. If the site already had a domain, the old one stops working at once. Say exactly that, and wait for yes.
In Studio: Site Config › Address › Custom domain › Connect (websitemint components/welcome-screens/site-domain-connect.tsx 25–57). The same box is on the welcome screen. By API, the call Studio makes:
POST /site/update-domain
{
"siteOrgId": "<org id>",
"siteName": "phasite",
"domainName": "example.com"
}With call_appengine, orgid and the token are added; send only the body.
| Field | Rule |
|---|---|
siteName | Required (400 site name is required). The site's name. It is also looked up by id, but the platform address is built as <siteName>-<siteOrgId>.site.appmint.app, so an id here gives the site a wrong platform address. |
siteOrgId | Defaults to the orgid header. Another org's id is refused with 403 Cannot change a site in another organization, except from the platform's root org (site.service.ts 2133–2137). |
domainName | Trimmed and lower-cased. At least two labels, each a-z 0-9 -, not starting or ending with -, at most 63 characters; at most 253 in all; not an IPv4 address. Else 400 Enter a valid domain name without a protocol or path (2138–2145). |
hostOrgId, hostDNS | Accepted and not used. |
/:domain in the path | Accepted and ignored. |
What it does, in order (site.service.ts 2132–2210):
- Checks the org may use custom domains (
validateCustomDomainUsage,plan-validation.service.ts90–101). Every plan allows them today (usage/pricing-plan.ts;allowCustomDomains: trueon Free, Starter, Professional, Business, Enterprise and every BusinessMade plan). What really fails here is an org that is not active (403Organization <org> is not active) or missing (404). - Finds the site (404
site <name> not found in org <org>). - 409
Domain <name> is already registered to another sitewhen a mapping for that name belongs to any other site, in any org. - If the site had a different domain: deletes that domain's ingress and its mappings. The old domain stops working now.
- Makes sure the platform mapping
<siteName>-<org>.site.appmint.appexists and writes it todata.hostName. - Saves
data.domain, creates the domain's mapping (status: "active"), and deploys the ingress fromresources/deploy-template/domain-ingress.tp.
The answer:
{
"sharedMapping": { "…": "the platform address's site_domain" },
"shareHost": "phasite-phazzer.site.appmint.app",
"siteMapping": { "…": "the domain's site_domain" },
"site": { "…": "the site record" },
"domainName": "example.com",
"siteName": "phasite",
"deploy": { "…": "present when the ingress was created" },
"deployError": "present when it was not"
}A failed route still answers 200. If the ingress could not be created, the answer carries deployError and the domain and mapping are saved anyway (site.service.ts 2198–2208). Studio ignores the answer and shows the DNS window as if all went well (site-domain-connect.tsx 36–48). Always read deployError. If it is there, the domain will show the certificate warning and a bare 404 however the DNS is set; run step 6 once, then tell the platform's support.
Enter the bare domain, never www.example.com. The engine stores what it is given, but strips www. when it looks a host up, so a site saved as www.example.com is found under neither www.example.com nor example.com. The route would be made for www.example.com and www.www.example.com. Studio's box doesn't stop it.
Connect is the only step that does 2, 3 and 4 together. Writing data.domain with repo_update or Site Config's generic fields saves the field and nothing else: no mapping, no route, no certificate.
What the ingress is (resources/deploy-template/domain-ingress.tp): name <domain with dots as dashes>-ingress, namespace base-app, Traefik on the web and websecure entry points, with both example.com and www.example.com sent to the site renderer, and the certificate resolver le-http. That resolver is Let's Encrypt's HTTP challenge: Let's Encrypt must reach the name over plain HTTP on port 80 at the platform before it issues. Each name gets its own certificate. Tested on live: reelmoment.io and www.luxesaver.com each carry a Let's Encrypt certificate for that one name. Deploying again deletes the ingress and creates it fresh (k8s-management.service.ts 197–215).
Hands on: shareHost (the platform address, for the DNS record) and whether deployError is empty.
Check: GET /site/get-site-host-mappings/<site name> lists both the platform address and the domain. Site Config › Address shows the domain as a link.
4. DNS: point the domain at the platform (the person's part)
Stop: DNS is the owner's, at their DNS host. Give them the records; don't log in for them. For a domain bought in Home › Domains, its DNS can be edited there (step 2), still only on their yes.
Use these records. They are what working live sites use (dig on reelmoment.io, www.reelmoment.io, www.luxesaver.com, all A 38.107.233.133):
| For | Type | Name | Value |
|---|---|---|---|
| The bare domain | A | @ | The address the site's platform host resolves to. Read it: dig +short <hostName>. Today 38.107.233.133. |
www | CNAME | www | The site's hostName, e.g. phasite-phazzer.site.appmint.app (or an A record with the same address as above) |
A subdomain site, e.g. shop.example.com | CNAME | shop | The site's hostName |
Then:
- Delete any other
A,AAAAorCNAMErecord on@andwww(the old host, a parking page). TwoArecords means half the visitors reach the old one, and Let's Encrypt can fail. - Leave
MX,TXT(SPF, DKIM, verification) and other names alone. - Cloudflare: set these records to DNS only (grey cloud) until the site opens on https. A proxied record hides the platform from Let's Encrypt's HTTP check.
Don't use the IP addresses Studio shows. Site Config's Show DNS Settings window gives A 38.234.23.56 for @ and www (dns-instructions-popup.tsx 4, 69, 87). Home › Domains › DNS Setup gives the same 38.234.23.56 (domain-details.tsx 17), and its CNAME falls back to <site>.site.appmint.app without the org when the site has no hostName (638). Sites & Dev Environments' window gives A 104.21.94.214 (modern-site-app.tsx 1612). Neither address answers for sites; tested with curl --resolve against both. The CNAME these windows show (the site's hostName) is right. The Site Config window's CNAME option covers only www, so the bare domain needs the A record above.
Right after the first Connect, Site Config's DNS window opens empty. It is handed the site as it was before the connect, when it had no domain, and it draws nothing without one (site-domain-connect.tsx 42–47, dns-instructions-popup.tsx 12). Close it and use Show DNS Settings, or this table. After an Edit Domain it shows the old domain's name.
How long:
- DNS: as long as the old record's TTL, then the time the DNS host takes to publish. Minutes to an hour is usual; Studio says "15 minutes to 48 hours" (
dns-instructions-popup.tsx99). A brand-new domain can take a few hours to resolve at all. - Certificate: Let's Encrypt can only issue once DNS points at the platform. Usually within a few minutes after that. If DNS was wrong when the route was made and the browser still warns an hour after DNS is right, re-make the route once with step 6. Let's Encrypt blocks a name after 5 failed checks in an hour, so don't repeat it.
Hands on: the records the person added, and when. Check: step 5.
5. Check it, live
From any shell (read-only):
dig +short example.com A # the platform's address, e.g. 38.107.233.133, and nothing else
dig +short www.example.com # the hostName then the address, or the address
dig +short phasite-phazzer.site.appmint.app # the address to compare against
curl -sI https://example.com/ | head -1 # HTTP/2 200 (or 404 if the home page is missing — see site routing)
curl -s https://www.example.com/ | grep -o '<link rel="canonical"[^>]*>' # href="https://example.com"
echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null \
| openssl x509 -noout -issuer -subject # issuer: Let's Encrypt; subject: CN = example.comRead the result:
| You see | It means |
|---|---|
dig shows another address, or two | DNS isn't pointed yet, or the old record is still there (step 4) |
subject=CN = TRAEFIK DEFAULT CERT and 404 page not found | DNS is right but the platform has no route for this name: the connect didn't make one (deployError), it was removed, or the name was entered with www. |
| A Let's Encrypt certificate, and the page is "Welcome to …" another site, 404 | The route is there but no mapping: step 6 |
200 and the site's own page | Done |
Type https://. On live, every plain http:// request to a site — custom domain or platform address — answers 301/308 to https://<host>:8443/, and port 8443 doesn't answer. Tested on reelmoment.io, www.luxesaver.com and a .site.appmint.app address. Browsers that try https first hide this; a printed or typed http:// link fails. Put https:// on every link and QR code.
Hands on: the working https:// address.
Check: open it in a browser. Studio's Site Config › Address shows the domain as a link that opens it.
6. Site Config: Repair Site Domain
Studio: Site Config › Address › Repair Site Domain ("Point this site's domain at BusinessMade again. Use it if the site stops opening.", site-action-buttons.tsx 156). Older docs and the API call it fix domains.
POST /site/fix-domains
{ "siteName": "phasite" }siteOrgId defaults to the orgid header; another org's is refused with 403 Cannot change a site in another organization. It is staff only, with the ConfigAdmin or ContentAdmin role and the content.update permission (site.controller.ts fix-domains).
Stop: it briefly takes every name the site answers on offline while it remakes them. Ask first.
What it does (site.service.ts 1942–2034):
- Finds the site.
- Deletes every mapping the site has: the platform address, the domain, and any extra name attached by a platform operator.
- Re-creates the platform mapping
<siteName>-<org>.<SITE_HOST_NAME>(site.appmint.appon live) and writes it todata.hostName. - If
data.domainis set, re-creates its mapping and deploys its ingress again (throughregisterSiteDomainName, 859–902). This is how it fixes a missing route or a stuck certificate. - Answers
{ success, message, site, removedDomains, addedDomains, cloudflareResults, errors }.
What it doesn't do:
- Change DNS. The Cloudflare step is commented out (2002–2005);
cloudflareResultsis always empty. Its API description still says it syncs with Cloudflare. - Report the route. An ingress that fails to deploy is logged only; the answer still says
success: trueand lists the domain inaddedDomains. - Keep extra names. Anything not the platform address or
data.domainis gone after it. Aliases were never mapped, so they lose nothing. - Touch
data.domain. A domain removed in Sites & Dev Environments (which leavesdata.domain) comes back, route and all.
Send the exact site name. Repair looks the site up by its exact name; a name no site has answers 404 Site <name> not found in organization <org> and changes nothing (site.service.ts fixSiteDomains, findSiteByName).
When to use it: the domain shows the certificate warning or the wrong site while DNS is right; the platform address stopped opening; data.hostName is empty (Studio's DNS window then shows your-site.websitemint.com). Not for DNS problems, a 409, or "it still shows my old host".
Hands on: addedDomains and errors.
Check: step 5 again, and get-site-host-mappings shows the platform address and the domain.
7. Site Config: change or remove the domain
Stop for either: the old domain stops working the moment the call returns.
Change: Site Config › Address › Edit Domain › type the new one › Connect is the same POST /site/update-domain with the new domainName. It deletes the old domain's route and mappings, then does all of step 3 for the new one. Then the owner points the new domain's DNS (step 4). Studio does nothing if the new name equals the current one (site-domain-connect.tsx 32).
Remove: Site Config › Address › Remove Domain:
POST /site/remove-domain
{ "siteOrgId": "<org id>", "siteId": "<site sk>" }It deletes the domain's ingress and mapping, and sets data.domain to "" (site.service.ts removeSiteDomain). The platform address and hostName stay, so the site still opens there, and links fall back to it. { "domainName": "example.com" } instead of siteId works only for a domain connected to one of this org's sites (else 404 Domain … is not connected to a site in this organization); a domain another org also holds is refused with 403 Domain … belongs to another organization. It is staff only (ConfigAdmin or ContentAdmin). Answer: { message, domain, results: { ingress, database, site }, site }.
call_appengine treats /site/remove-domain as a delete: the first call comes back asking for the person's confirmation; repeat it with "confirmed": true after their yes (ai/agents/base-agent.ts 914–935).
Tell the person to remove or repoint the DNS records afterwards. Left pointing at the platform, the bare name just shows the certificate warning.
Remove from Site Config, not from Sites & Dev Environments. That app's Remove Domain calls DELETE /site/unregister-site-domain/<site sk>/<domain> (modern-site-app.tsx 298–316 → site.controller.ts 274–278 → site.service.ts 905–914). It deletes the mapping only. The route stays, data.domain stays, so the card still shows the domain, emails and the sitemap still use it, and the next Repair Site Domain puts it back. Site Config's Remove Domain also clears the box even when the call failed (site-domain-connect.tsx 83–97): reload Site Config to see the truth.
Hands on: the site's new data.domain (or none) and its mappings.
Check: get-site-host-mappings and get-site/<site name>; for a change, step 5 on the new domain.
Aliases
Site Config › Address › Aliases ("Other hostnames that should serve this site", site-config-form.tsx 799) saves a list to data.aliases. Nothing routes with it. No code registers a mapping or route for an alias, and the renderer doesn't read it; the only reader is analytics, which counts visits on those names as the site's (appengine src/analytics/analytics-site.ts 44).
What actually serves another name:
| The other name | How |
|---|---|
www. + the site's domain | Already served by the domain's route and mapping. Just the DNS record (step 4). |
| A subdomain or another domain instead of the current one | Change the domain (step 7). |
A second domain as well, e.g. example.co.uk beside example.com | Not possible from Studio for the owner. Only a platform operator can attach a second name (Configuration › System Operations, POST /site/attach-domain, which maps it and makes its route but doesn't set data.domain), and the site's next Repair deletes it. Otherwise, forward it to the main domain at the owner's registrar (a URL redirect), which needs nothing here. |
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
dig shows no address, another address, or two | DNS not pointed, not yet published, or the old record is still there | Step 4. Wait out the old TTL. Nothing on the platform side helps. |
Browser: "not secure" / certificate for TRAEFIK DEFAULT CERT; page 404 page not found | No route for the name on the platform: the connect's deployError, the domain removed, or entered with www. | Read data.domain (must be the bare name). Then step 6 once. Still the same: platform support (the ingress could not be created). |
Certificate warning that names the right domain but is expired or wrong, or a pending certificate an hour after DNS is right | Let's Encrypt couldn't reach the name over HTTP when the route was made (DNS was elsewhere, Cloudflare proxy on, an AAAA record to the old host) | Fix DNS (step 4: grey cloud, no stray AAAA), then step 6 once. Don't repeat it more than a few times an hour. |
| The domain opens "Welcome to …" another business's or the platform's site, 404 | Route there, no mapping to this site | Step 6. |
409 Domain … is already registered to another site | Another site has a mapping for that name, in this org or any other | Find which with GET /site/get-site-by-hostname/<domain>. If it's this org's other site, remove it there first (step 7). If it's another org's, the owner must prove the domain is theirs to platform support. |
403 … plan does not support custom domains / Organization … is not active | The org's plan or account | All current plans include custom domains; an inactive org is a billing matter. Not something to work around. |
400 Enter a valid domain name… | https://, a path, a space, an IP, or a bad label | Send only example.com. |
| An alias doesn't open | Aliases are never mapped or routed | See Aliases. |
| Still the old site after connecting | DNS still points at the old host (dig); the browser's cache; or only one of @ and www was changed | dig both names. Open in a private window. Change both records. |
| Still the old site after moving the domain between two Appmint orgs | The engine remembers which org a host belongs to, with no expiry, and forgets only the exact name that was removed, never its www. form (repository.org.service.ts 250–273) | Pages come from the right site; forms, cart and sign-in calls on www. can still go to the old org. Platform support must clear the cache entry www.<domain>-org-id. |
| Emails and Studio still link the platform address | data.domain empty: the domain was attached some other way, or removed | Connect it in Site Config (step 3). |
| Emails link the removed domain | Removed in Sites & Dev Environments, which leaves data.domain | Remove it in Site Config (step 7). |
http://example.com doesn't open | The platform's HTTP redirect goes to port 8443 | Use https:// (step 5 warning). |
When it's done
Tell the person, in a few lines:
- the domain and the site it opens, and the
https://address that now works (or what's still pending: DNS, certificate); - the DNS records they added, and any old ones they should delete;
- that
www.works too, and that links, emails, the canonical tag and the sitemap now use the domain; - for a change or removal, what the old domain does now.
Open Site Config › Address and the domain in a browser.
What it can't do yet
- No status for the route or certificate. Nothing in Studio or the API says whether the ingress exists or the certificate issued.
site_domain.statusisactivethe moment it's created. Check withcurlandopenssl(step 5). - No DNS check. Connect doesn't look at DNS; it succeeds for a domain nobody pointed.
- Studio's DNS windows give wrong IP addresses (step 4) and the first one opens empty.
- Plain
http://is broken on live (redirect to port 8443). - One domain per site. Aliases do nothing; a second domain is operator-only and lost on Repair.
- Repair doesn't sync Cloudflare and can't report a failed route.
- Dev environments: Sites & Dev Environments › Attach Domain on a dev environment only adds the name to its
domainslist. The route is commented out (dev-environment.service.ts399–413) and removal says "not yet implemented". - Connect may leave
hostNameas it was. It writesdata.hostName, then saves the whole site from a copy read before that write (site.service.ts2151, 2181, 2195). A site with nohostNamekeeps none; Repair sets it.
Don'ts
- Don't call Connect, Repair or Remove without the person's yes. Each changes what the live site answers on at once.
- Don't write
data.domainwithrepo_update. It routes nothing. - Don't enter
www.,https://or a path. - Don't give the IP addresses from Studio's DNS windows. Read the platform host's address with
dig. - Don't touch the owner's MX, TXT or other records, and don't log in to their DNS host for them.
- Don't use Sites & Dev Environments' Remove Domain for a site.
- Don't run Repair over and over. A few times an hour at most, and only once DNS is right.
- Don't promise an alias will open the site.
- Don't call
site/fix-domainswith a site name you haven't read back fromget-site.