To put an Astro site on a custom domain, add the domain in your host’s dashboard (Cloudflare Pages, Netlify, Vercel or GitHub Pages), create the DNS records it shows you at your registrar, and wait for the HTTPS certificate. Then set site in astro.config.mjs to the new domain and redeploy, so canonical URLs, the sitemap and RSS use it. The Astro side is one line; the rest is DNS.
Key Takeaways
- Choose one canonical host,
www.example.comorexample.com, and redirect the other.- Apex domains need A/ALIAS/flattened CNAME records;
wwwuses a CNAME.- Hosts issue HTTPS certificates automatically once DNS resolves.
- Update
sitein config and redeploy, or your canonicals still point at the old URL.
1. Pick www or apex
Decide which version is canonical before you touch DNS:
www.example.com: a plain CNAME works with every DNS provider, and it’s easy to move between hosts.example.com(apex): shorter, but the root of a domain can’t be a standard CNAME, so you need your DNS provider’s ALIAS/ANAME, CNAME flattening, or the host’s A records.
Either is fine for SEO. What matters is that the other one redirects to it with a 301.
2. Add the domain in your host
| Host | Where |
|---|---|
| Cloudflare Pages | Project → Custom domains → Set up a domain |
| Netlify | Site configuration → Domain management → Add a domain |
| Vercel | Project → Settings → Domains |
| GitHub Pages | Repository → Settings → Pages → Custom domain |
The host then shows the exact records to create. Use those values, not ones copied from a tutorial, since they differ per host and sometimes per project.
3. Create the DNS records
Typical shapes:
# www subdomain
www CNAME your-project.pages.dev. # or your-site.netlify.app, cname.vercel-dns.com, username.github.io
# apex domain
@ A <IP addresses from your host>
# or, if your DNS supports it:
@ ALIAS <hostname from your host>
If your domain’s DNS is already on Cloudflare and the site is on Cloudflare Pages, adding the custom domain creates the record for you.
DNS changes usually show up within minutes and occasionally take longer. Check from your terminal:
dig +short www.example.com
dig +short example.com
4. Redirect the other host
Add both example.com and www.example.com to the project. Most hosts then let you mark one as primary and redirect the other automatically. On Cloudflare, you can also use a Redirect Rule at the zone level. Confirm it works:
curl -sI https://example.com | grep -i -E '^(HTTP|location)'
# HTTP/2 301
# location: https://www.example.com/
5. GitHub Pages: the CNAME file
GitHub Pages stores the custom domain in a CNAME file in the published output. When you deploy with Actions, add it to public/ so every build includes it:
# public/CNAME
www.example.com
With a custom domain you also no longer need base in your config; the site is served from the root.
6. Update Astro and redeploy
// astro.config.mjs
export default defineConfig({
site: 'https://www.example.com',
});
Everything built from site changes with it: canonical URLs, Open Graph URLs, the sitemap, RSS links and anything in robots.txt generated from config. Redeploy, then spot-check a page’s <link rel="canonical"> and /sitemap-index.xml.
7. After the switch
- Add the new domain as a property in Google Search Console and submit the sitemap.
- If the site previously lived on another domain, keep that domain and redirect every path to the same path on the new one. Use Search Console’s Change of Address tool.
- Make sure HTTPS is enforced (HTTP redirects to HTTPS). All four hosts do this by default or with one setting.
The custom domains docs show host-by-host screenshots for Komal themes. For choosing a host, see the Astro deploy guide, and for why the domain in site matters to SEO, canonical URLs in Astro. Cloudflare’s custom domains documentation covers Pages-specific details.
FAQ
Do I need to change anything in Astro for a custom domain?
Only site in astro.config.mjs, and remove base if you were on a GitHub Pages project URL. Redeploy afterwards.
Why does my custom domain show a certificate error?
The host hasn’t issued the certificate yet, usually because DNS isn’t pointing at it. Check the records with dig and wait for the host’s dashboard to show the domain as active.