Back to Blog
Next.js · Note

A scheduled post leaked its full text through next-intl

Simon Doba·August 4, 2026·3 min read

I scheduled a blog post for a week out. The post page checks the publish date and calls notFound(), the sitemap and RSS feed both filter on it, so as far as I was concerned it was hidden.

Then I grepped the build output for a sentence from the unpublished post, and found it on /en/work.

What actually happens

next-intl serialises the entire message catalogue into the RSC payload of every page. My blog content lives in messages/en.json and messages/de.json, so every page of the site shipped the full text of every article — including the one that was not supposed to exist yet.

The route gating works. It returns a 404. It just has nothing to do with whether the text is in the response, because the text arrives through the layout's NextIntlClientProvider, not through the page.

Anyone viewing source could read the whole thing. So could any crawler.

The fix

Filter the messages in i18n/request.ts, before they reach getMessages():

function withoutScheduledPosts(messages: Messages, now: Date): Messages {
  const scheduled = posts
    .filter((post) => new Date(`${post.publishDate}T00:00:00`) > now)
    .map((post) => post.translationKey);

  if (scheduled.length === 0) return messages;

  const articles = { ...messages.blog?.articles };
  for (const key of scheduled) delete articles[key];

  return { ...messages, blog: { ...messages.blog, articles } };
}

After that, zero of 44 built HTML files contained the scheduled post. Published content was untouched.

Two things this does not cover

Static files in public/. I keep an llms-full.txt that lists every post with its URL and a summary. The filter cannot reach it — it is a flat file. I removed the entry by hand and it goes back in on release day.

Static generation. The filter runs at render time, so with SSG the decision is baked at build. A scheduled post needs a deploy on or after its date to appear. That was already true of the publish gate; the filter does not make it worse, but it is worth knowing before you assume a post will show up on its own.

Why I'd check this even if you don't schedule posts

The general shape is: a route guard hides a page, but the data that page renders travels through a provider that does not know about the guard. Anything you keep in your i18n catalogue is on every page, all the time — drafts, internal copy, feature-flagged strings, unreleased product names.

The check is one command against your build output. Grep for a sentence that should not be public and see how many files come back.

I found this while scheduling a post about scored agent loops. The irony of a leak in the scheduling mechanism was not lost on me.

next-intl 4.13.4, Next 16.2.9, App Router.

Share this article

Building something similar?

I write about setups I actually use. If you're working on something comparable, I'd be curious what your workflow looks like.

Get in touch

Cookie Settings

We use cookies for analytics and to improve our website. Privacy policy