Deployment
pnpm build writes 109 HTML files and their assets to dist/. There is no adapter, no server output, no runtime — every route is prerendered, including the four generated endpoints. Any static host serves it: Netlify, Vercel, Cloudflare Pages, GitHub Pages, S3, nginx.
pnpm build # -> dist/
pnpm preview # serve dist/ locally exactly as a host would
Two settings shape what lands there. trailingSlash: "always" makes the build emit directory-style URLs (/used-cars/index.html), and canonical, og:url and every internal href agree on that shape — one canonical form, no redirect chain. And vite.build.assetsInlineLimit: 0 stops short scripts being inlined, because inlined scripts break under <ClientRouter /> view transitions. Leave it at zero; the comment in astro.config.mjs says so for the next person.
The production gate
Set SITE_URL in your host’s build environment before the first production deploy. If you forget, the build fails with the reason named:
SITE_URL is unset or still the placeholder. Set it to your production domain
in your host's environment variables before deploying.
The gate reads each host’s own production signal — CONTEXT=production on Netlify, VERCEL_ENV=production on Vercel, or DEPLOY_ENV=production anywhere else. None of those is set by a local pnpm build or a deploy preview, so previews and local work build freely on the placeholder. On a host not in that list, set DEPLOY_ENV yourself; the check is only as good as the signal it can see.
It is worth understanding why this is a hard failure rather than a warning. site feeds canonical, OG, JSON-LD, the sitemap, robots.txt and llms.txt. All six are invisible in review — a page with a canonical pointing at example.com renders perfectly — and by the time a crawler tells you, it has already indexed the wrong domain.
Before you deploy
SITE_URLin the host’s build environment.public/og.jpg— replace the placeholder with a real 1200×630 social image. It is also the JSON-LD logo until you swap that for a brand mark;BaseHeadsays so in aponytail:note.src/config/siteData.json.ts— name, author, and thesameAslist.src/config/legalData.json.ts— the terms and privacy copy are placeholders. Have them reviewed.- Favicons —
public/favicon.svgandpublic/favicon.ico. - The editorial figures in
navDataand the browse headers, if you are wiring a real index feed. - Delete the dev catalog, once you have stopped shopping for primitives.
The dev catalog’s cost
src/components/Sections/UiCatalog/ and src/pages/examples/ emit no pages in a production build — getStaticPaths returns [] under import.meta.env.PROD. But Tailwind scans source, not output, so every demo class in the catalog is compiled into the stylesheet that every page loads.
Measured on this build: deleting both directories takes the shared BaseLayout stylesheet from 113,715 to 95,172 bytes — 18,543 bytes, about 16% — and drops the built @keyframes count from 92 to 23, because the catalog is the only thing referencing most of the motion library.
That is CSS, not JavaScript, and it is per-page on first load only. Keep the catalog while you are still picking primitives; it is the fastest way to see all 37. Delete it before launch.
What a host needs
Nothing beyond static file serving. If your host lets you choose, keep directory-style URLs (/used-cars/ serving /used-cars/index.html) so they match trailingSlash: "always"; most do this by default. The only file worth a cache rule is /rss.xml, and only if you publish often.
Three routes ship noindex in their markup and are excluded from the sitemap by the filter in astro.config.mjs: /sign-in/, /saved/ and the 404. They are static demos of account screens, so they are built but deliberately not indexable — SEO explains the reasoning.