The Sitemap Said /blog. The Site Did Not.
The sitemap advertised `/blog/:slug` while Astro generated root-level article routes. Both now consume the same normalized permalink.
- Project
- Dakic website
- Role
- Technical diagnosis, implementation, and release verification
- Timeframe
- August 2026
- Status
- Completed
Evidence from the work
- Fault
- Sitemap hard-coded `/blog/:slug` URLs
- Verification
- Generated sitemap now matches live article routes
What this is
The Dakic blog router generated articles at root-level permalinks. The custom sitemap built a second version of each address under /blog.
The site could publish an article that worked in the browser and advertise a different, nonexistent address to search engines.
The friction
The sitemap loaded raw content entries and reconstructed article URLs itself. It removed the filename extension, sanitized the remaining text, and prefixed the result with /blog/.
The blog system already had permalink code. That code read the configured permalink pattern and normalized each post into the same route used during the Astro build. The sitemap ignored it.
Both versions looked plausible in code. Comparing the generated sitemap with the generated pages exposed the mismatch.
The senior decision
We removed the sitemap’s private permalink implementation.
The sitemap now calls the same fetchPosts() function used by the blog routes. Each normalized post already contains its permalink, category, tags, publication date, and draft state. The sitemap uses those values instead of rebuilding them.
The change removed a second source of truth and reduced the amount of sitemap code. Draft filtering and permalink configuration now flow through the existing blog contract.
What became real
The sitemap now emits /${post.permalink} for each published post. A generated entry such as /2026-08-23-the-next-release-should-answer-a-question matches the page Astro creates.
The same change replaced raw category and tag sanitization with the normalized slugs supplied by the blog utilities.
Evidence
The implementation diff removed the hard-coded /blog/${slug} construction and imported fetchPosts from the shared blog utility. A production build generated the article routes and sitemap from the same post set.
After deployment, the three new AI work articles returned HTTP 200 at their root-level URLs. The live sitemap contained the same addresses.
What we learned
A route rule belongs in one module. Sitemaps, RSS feeds, navigation, and static paths should consume that module rather than approximating its behavior.
A successful page build does not validate discovery metadata. Route verification needs to compare the generated page, canonical URL, and sitemap entry.
Current state
The correction is live. The sitemap and blog router now share normalized post data and permalink behavior.