Search Console reports a 15% spike in indexed URLs this week. You investigate — they’re articles titled “Untitled”, “[TODO write intro]”, “Draft notes - DON’T PUBLISH”. Your draft articles got built into production. They’re now in Google’s index, ranking for nothing useful, signaling to Google that your site has low-quality content, and presenting placeholder text to anyone who finds them.
Most “draft published” incidents are configuration gaps, not human mistakes. Your build doesn’t respect draft: true, a bulk-edit script flipped flags, or staging got promoted to prod with drafts included. The fix: audit + deindex what leaked, then add CI guards so the build cannot produce a draft in production.
Common causes
Ordered by hit rate, highest first.
1. Build pipeline doesn’t respect draft: true frontmatter
Your content loader includes all .mdx files regardless of draft flag. The flag is decorative; nothing reads it.
How to spot it: Search your codebase for how draft is handled:
grep -rn "draft" src/content/config.ts src/pages/ astro.config.mjs
If nothing references it in the build path, the flag does nothing.
2. A bulk-edit script overwrote draft flags
You ran a script to normalize frontmatter (“add publishedAt to all articles”). The script also wrote draft: false to every file as a default. Drafts went live overnight.
How to spot it: git log --all -p -S "draft: false" near your recent bulk-edit. If the script set draft: false indiscriminately, you found it.
3. Staging deploy got promoted to production with drafts
You build with DRAFTS=true on staging, DRAFTS=false on prod. Someone deployed the staging build to prod (or copied the staging env to prod). Drafts hitchhiked.
How to spot it: Check the production deploy’s build logs. If DRAFTS=true was set, that build wasn’t a prod build.
4. Default frontmatter for new files is draft: false
When you / your CMS create a new article, the template defaults draft: false. New articles ship to production immediately upon any deploy, even if you intended to “save and finish later.”
How to spot it: Look at the article template / generator. If new files default to draft: false, every new article auto-publishes.
5. Multiple agents (or co-authors) flipped flags inconsistently
One author edits with draft: true, another author opens the file and saves without noticing — their save replaces the flag with the editor’s default. Race condition between authors.
How to spot it: git blame on the draft: line of leaked articles. If the flag flip is by someone who didn’t author the content, accidental save.
6. Build cache served the previous “draft: false” version
You set draft: true and pushed; the build cache still served the previous build. Article appears live because cache hasn’t invalidated.
How to spot it: Force a cache purge / rebuild. If the article disappears after a clean rebuild, cache was the issue, not the build itself.
Shortest path to fix
Ordered by urgency. Steps 1 and 2 take 10 minutes; don’t wait.
Step 1: Find and deindex the leaked URLs
# In your CMS / repo: list all current drafts
grep -rln "^draft: true" src/content/articles/
# In Search Console: filter for placeholder titles
# (Performance → Pages → search "untitled" / "todo" / "draft")
For each leaked URL:
- If you want to keep + finish it: update to `draft: false` once it's ready
- If you want to remove it: 410 the URL (return HTTP 410 Gone)
- For temporary hiding: noindex + Search Console removal request
Use 410 for permanent removal — it’s faster than 404 for getting Google to drop.
Step 2: Make your build respect draft: true (if it doesn’t)
In Astro:
// src/content/config.ts
const articles = defineCollection({
type: "content",
schema: z.object({
draft: z.boolean().default(false),
// ... other fields
}),
});
In page loader:
// src/pages/articles/[slug].astro
export async function getStaticPaths() {
const all = await getCollection("articles");
const published = all.filter(a => !a.data.draft || import.meta.env.DEV);
return published.map(a => ({ params: { slug: a.slug }, props: { article: a } }));
}
Drafts now exist in dev (pnpm dev) but never in pnpm build output.
Step 3: Add a CI guard against drafts in sitemap
# .github/workflows/no-drafts-in-prod.yml
on: pull_request
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: pnpm install && pnpm build
- name: Block drafts in production output
run: |
if grep -l "draft.*true\|DRAFT\|TODO" dist/sitemap-index.xml dist/**/*.html 2>/dev/null; then
echo "::error::Draft markers found in production build"
exit 1
fi
Anything containing draft markers in production output fails the PR.
Step 4: Make draft: true the default for new articles
In your CMS / scaffold:
---
title: ""
draft: true ← default
publishedAt:
---
Authors must consciously flip draft: false to publish — opt-in, not opt-out.
Step 5: Force cache purge after fixing
If your CDN cached the leaked URLs:
# Cloudflare full purge
curl -X POST "https://api.cloudflare.com/client/v4/zones/<zone>/purge_cache" \
-H "Authorization: Bearer $CF_API_TOKEN" \
-d '{"purge_everything":true}'
# Vercel: redeploy with no cache
vercel deploy --prod --force
Until cache clears, the deindex isn’t visible to fresh requests.
Step 6: For accidentally indexed pages, request Search Console removal
Search Console → Removals → New Request
- Submit URL of each leaked draft
- Choose "Temporarily hide URL" (90 days)
- During that 90 days, either complete the page or 410 it
Faster than waiting for re-crawl. Don’t skip — leaked drafts hurting site quality is worth the few minutes.
Prevention
- Build must respect
draft: true— assert with a CI check that no drafts reachdist/or the sitemap - New article template defaults to
draft: true— opt-in publishing, not opt-out - Bulk-edit scripts that modify frontmatter must explicitly preserve
draft: trueflags - Staging and prod environments use distinct
DRAFTSenv vars; never swap them - Add a quick
pnpm audit:drafts-publishedscript you can run anytime - After incidents, document the timeline; recurring leaks reveal a systemic gap to close
Related
Tags: #Content ops #Site quality #Site audit #Troubleshooting #Draft published