Overview
A static site computes every SEO artifact at build time, so each one is deterministic and reviewable in a diff. The cost is that nothing is added later by a server: the build must emit every file a server-rendered site would. This guide wires a Quartz 4 build to do that; the steps carry over to other generators. For the order of a full launch, see launch-a-new-site.
Prerequisites
- A build that emits to
public/(npx quartz build). - A production domain; see deploy-quartz-site.
Steps
1. Pin baseUrl
Quartz builds the sitemap, RSS feed, and absolute URLs from baseUrl in quartz.config.ts. Use the bare production domain.
configuration: { baseUrl: "yourdomain.com" }2. Emit a self-referential canonical per page
Stock Quartz’s Head component does not write a canonical tag. Add one in a custom head component built from baseUrl plus the page slug, and register it as head in quartz.layout.ts.
npx quartz build
grep -L 'rel="canonical"' public/*.html # lists pages missing the tag; expect none3. Let the build write sitemap.xml
Plugin.ContentIndex({ enableSiteMap: true }) emits public/sitemap.xml. <lastmod> comes from CreatedModifiedDate, which reads frontmatter modified, lastmod, or updated, then git history, in the configured priority order. A custom key such as last_updated is not read.
xmllint --noout public/sitemap.xml && echo valid
grep -c "<url>" public/sitemap.xml4. Place robots.txt in content/
Non-Markdown files in content/ are copied to the output root.
User-agent: *
Allow: /
Sitemap: https://yourdomain.com/sitemap.xml
Add Disallow: lines only for paths that must not be crawled; over-blocking is the common static-site bug.
5. Inject JSON-LD
Emit WebSite on the homepage, Article plus BreadcrumbList on content pages, from a head component. See add-jsonld-to-static-site.
6. Add the agent and IndexNow files
Ship /llms.txt (ship-llms-txt), optionally /ai.txt (ai-txt), and the IndexNow key file /{key}.txt (indexnow). The root-file checklist is discoverability-files.
7. Gate the deploy
The workflow should run the full build, fail on lint, link, or JSON-LD errors, and upload only public/. See deploy-quartz-site or deploy-a-static-site-to-cloudflare-pages.
Verify it worked
curl -sI https://yourdomain.com/sitemap.xml | head -1
curl -s https://yourdomain.com/ | grep -c 'application/ld+json'
curl -s https://yourdomain.com/ | grep -o 'rel="canonical"[^>]*'
curl -sI https://yourdomain.com/llms.txt | head -1Then run the Rich Results Test on one content URL and submit the sitemap in Search Console; see submit-a-new-site-to-search-engines.
Common errors
- Canonical points at
localhostor a staging host.baseUrlis wrong; fix and rebuild. /sitemap.xmlreturns 404.enableSiteMapis off, or a hand-committed copy conflicts with the emitter.- JSON-LD shows as text in the page. It was wrapped in
<pre>or escaped; emit a raw script element, not a Markdown code fence. - Search Console reports “Indexed, though blocked by robots.txt”. A staging
Disallow: /shipped to production.