How To Drip-Publish Blog Posts Without Hurting SEO
A three-part pattern for scheduling future-dated posts on a static site, keeping them out of the index and sitemap, and avoiding the orphan trap.
I wanted to write a batch of posts in one focused weekend and have them go live one at a time over the following weeks, the way a CMS like WordPress lets you schedule a draft for a future date. The problem is that a static site has no scheduler. It builds once, ships a folder of HTML, and then sits there until something rebuilds it. There is no server quietly watching the clock, deciding that 10 AM has arrived and a post is now due. So the naive version of scheduling, writing the post today and trusting it to surface later, simply does not work. Either the post is in the build or it is not.
The trickier half of the problem is search engines. If I drop a finished post into the build but mark it as future, I have to make sure Google never sees it before its date. A post that shows up in the sitemap on Monday but is meant to debut on Friday is a post I have leaked early. It can get crawled, indexed, and cached against my intent, and now the "scheduled" date is a fiction. So the requirement has two faces. The post must be invisible until its date, to humans and crawlers alike, and then it must appear on its own without me touching anything. This is exactly how this blog publishes, and the pattern that makes it work has three parts.
The publishDate Field Is The Single Source Of Truth
Every part of this depends on one piece of data, a publishDate in each post's frontmatter. It is just a timestamp sitting next to the title and the tags, and on its own it does nothing. A static site generator does not read that field and act on it for you. The field only matters because the rest of the pipeline agrees to treat it as the one authority on whether a post is allowed to be public yet.
That agreement is the important part. I am not storing "published" and "draft" as separate states that I flip by hand. I am storing a date, and I let every build compute the state fresh by comparing that date to the moment the build runs. A post is live if and only if its publishDate is in the past at build time. Nothing is toggled. It is published because today caught up to the date I wrote down, and that lets one timestamp drive everything downstream without me coordinating flags that drift out of sync.
The reason I lean on a single field instead of a status enum is that drift is the enemy here. The moment you have two sources of truth, a date and a boolean, you have a way for them to disagree, and the failure is silent. A date compared against the clock cannot disagree with itself. There is one fact, and every consumer derives the same answer from it.
The Build-Time Filter Hides The Future From Everyone
The second part is a filter that runs while the site is being built, before any HTML is written. Its whole job is to drop any post whose publishDate is still in the future, and to do that consistently in two places. It excludes future posts from the index pages, the post listings a human would browse, and it excludes them from the sitemap, the file search engines read to discover what exists.
Both exclusions matter, and missing either one breaks the promise. If a future post is kept out of the index but left in the sitemap, a human never sees it but a crawler does, and the post can get indexed before its date. If it is in the index but out of the sitemap, a reader can stumble onto it directly. The point of filtering at build time is that the future post never makes it into the artifact at all. It is not hidden with CSS, not gated behind a client-side flag, not 404'd by a redirect rule. It is genuinely absent from the files that ship. There is nothing to leak because the bytes were never written.
This is the cleanest property of the whole approach. A static build is a snapshot, and the filter decides what the snapshot contains. If a post is not due, it does not exist as far as the deployed site is concerned, because the server is only handing out files the build already produced. The filter, not runtime logic, is the gatekeeper, which makes the gate impossible to walk around from the outside.
The Daily Rebuild Is What Makes Posts Appear
Here is the part that surprises people new to static sites. The build-time filter, by itself, would hide a scheduled post forever. Picture the timeline. I build the site on the first of the month. A post is dated the tenth. The filter correctly drops it, because on the first the tenth is still the future. Now the tenth arrives. Nothing happens. The deployed site is still the snapshot from the first, and that snapshot does not contain the post. The date passing changes nothing, because no build has run to re-evaluate it.
So the third part is a daily scheduled rebuild. Once a day, on a cron, the site rebuilds from scratch and re-runs the filter against the current clock. Where that cron lives matters more than it looks, and I eventually moved mine to an in-cluster CronJob instead of a CI schedule for exactly the silent-failure reasons below. On the morning the post's date has finally passed, that day's build includes it for the first time, writes it into the index and the sitemap, and ships. The post goes live not because I did anything, but because a build ran on a day when the date was no longer in the future. The rebuild is the heartbeat that keeps re-asking "what is due now?" and acting on the new answer.
This reframes how scheduling works on a static site. There is no scheduler in the traditional sense, no job that wakes up at the exact minute a post is due. There is a filter that knows the rule, and a recurring rebuild that gives the filter a fresh chance to apply it. Daily resolution is plenty for an editorial blog, and every scheduled post surfaces on its date with no manual step. You can see the result of exactly this setup running at solodevstack.com, where posts I wrote earlier go live on their own schedule.
The Orphan Trap Is The Failure Mode To Fear
Now the warning, and it is the most important thing in this whole post. The daily rebuild is not a nicety you can skip. It is load-bearing. The entire mechanism rests on the assumption that something rebuilds the site every day, and if that assumption quietly stops being true, scheduled posts break in a way that is hard to notice.
Call it the orphan trap. Suppose the rebuild cron is missing, or it was set up once and later broke, or a credential expired and the scheduled job has been failing without anyone watching. The last successful build is now frozen in time. A post's date arrives. The post is finished, correct, sitting in the repository, dated for today. And it never appears, because nothing rebuilt the site to re-run the filter and include it. The post is orphaned. It exists in source, it is past due, and it is invisible, because publication was never an event. It was a side effect of a build that no longer happens.
What makes this trap nasty is that it is silent and delayed. The day you set everything up, you test it by hand and the recent post shows up, so it looks fine. The cron's absence only bites weeks later, on a date you have probably forgotten, for a post you wrote long ago. There is no error, no failed deploy on the post itself, no log line that says "a post was due and we skipped it." There is just a gap on the blog that nobody flagged. So the rule I hold onto is simple. The scheduled rebuild is part of the feature, not infrastructure around it. Monitor it the way you would monitor anything else that can fail quietly, and pair it with a liveness audit that catches posts that never went live, because a dead cron does not crash. It just stops the future from ever arriving.
What This Pattern Really Teaches
The lesson underneath the mechanics is that static sites can drip-publish safely as long as you respect where the work actually happens. Keep future posts out of the index and the sitemap at build time so no reader and no crawler ever sees them early, and rebuild on a daily schedule so each one surfaces on its date. Those two ideas, a build-time filter and a recurring rebuild, are the whole thing, and a single publishDate field ties them together.
The part I would not let anyone gloss over is the rebuild. It looks like a small piece of automation, the kind of thing that feels safe to defer or treat as optional. It is the opposite. It is the only moving part that turns a written-but-future post into a live one, and because its failure is silent, it deserves more attention than the flashy parts of the system, not less. Build the filter carefully, but guard the cron like the load-bearing wall it is, because every scheduled post you ever write depends on it firing.
One thing this pattern quietly assumes, which nobody says out loud when they describe it, is that you have a backlog to schedule. A drip publisher with an empty queue is a slower way to publish nothing, and I have shipped that exact setup more than once, admired the cron, and then let it run dry inside a month. Where the backlog comes from matters more than the machinery that releases it. I have since built a whole side project around that problem, which tells you how much of a nuisance I found it. Build the filter, guard the cron, then go solve the part the cron cannot.
I build things like this for clients, full-stack apps, AI agents, and automation pipelines, usually shipped faster than expected because I work with AI tooling every day. If you want something built, book a call.
Like this? You'll like what I'm building too.
Two ways to support and get more of this work.
HEARTH
A privacy-first Life OS for your desktop. Journal, tasks, and notes that stay on your machine. Coming soon, direct download from this site.
Read moreMY TOOLKITS
Receipts-first toolkits for shipping after hours, building Claude agents, publishing on Amazon, and more. The exact methods I used, not theory.
Browse on WhopRelated Articles
Treating Video As Code With Remotion And fal
How I turned a script into a finished video with code instead of a timeline app, and why determinism is the real win for a solo dev.
Build Versus Buy For A Custom AI Feature
How to decide between an off-the-shelf AI tool and a custom AI feature, weighing control, cost, differentiation, and data with a clear framework.
Can One Developer Build Your Whole MVP
An honest look at whether a single full-stack developer can ship your whole MVP, when it works beautifully, and when you should bring in more people.