Skip to main content

Stale path cleanup

When you change a post's slug or move it to a different category, its URL changes. The new URL gets a fresh HTML file uploaded; the old URL needs to be deleted — otherwise visitors find the old version still live, search engines see two pages with the same content, and your sitemap references both.

The publisher handles this automatically through a mechanism called stale path cleanup.

The lifecycle of a published path

Each post has three relevant Firestore fields:

{
lastPublishedPath: "news/launch.html", // current live URL
lastPublishedHash: "a1b2c3...", // current live HTML hash
previousPublishedPaths: [ // paths whose deletion failed
"old-news/launch.html" // (rare — see "Retry on failure")
]
}

On every publish, the cleanup pass iterates [lastPublishedPath, ...previousPublishedPaths], attempts deleteFile on each that isn't the new path, and persists any non-404 failures back into previousPublishedPaths so the next publish retries them.

When does cleanup run?

Three triggers:

Slug change

You edit the post's slug from launch to launch-announcement. The new URL is /news/launch-announcement.html; the old /news/launch.html needs to go away.

Category change

You move the post from news category to updates. The new URL is /updates/launch.html; the old /news/launch.html needs to go away.

Unpublish

You take the post offline. Every path in [lastPublishedPath, ...previousPublishedPaths] needs to go away — same helper with keepPath: "".

Delete

Same as unpublish (the helper is shared).

What gets compared

Always compare on the final URL path, never the raw slug. Two slugs may match without colliding because their URLs differ:

  • A post with slug news (URL: /news.html)
  • A category with slug news (URL: /news/index.html)

These don't collide — the URLs are different. The publisher operates on URL paths, not raw slugs.

Retry on failure

If a deleteFile returns a non-404 error (e.g. the Flexweg API returns 503 momentarily), the failed path goes into previousPublishedPaths for retry on the next publish. So a transient hiccup never leaves you with a permanent stale file.

404 is silent — already-gone is desired state. The publisher doesn't complain when a path it tried to delete was already deleted (e.g. by a manual file-manager edit).

What happens if I delete the post directly in Firestore?

Bypassing the admin's delete flow leaves the HTML file orphaned on Flexweg. The cleanup mechanism doesn't fire because no unpublishPost / deletePost call happened.

To recover:

  1. Note the path that was orphaned (check lastPublishedPath of the deleted post if you still have a backup, or browse Flexweg's file manager)
  2. Delete the file via Flexweg's file manager directly

In general, always delete posts via the admin's UI. The cleanup logic relies on it.

What if Flexweg loses a file but the CMS thinks it's still there?

The CMS state (lastPublishedPath, lastPublishedHash) is the source of truth for what's supposed to be live. If Flexweg drops a file (rare — could happen on plan changes, manual edits, API bugs), the CMS still thinks it's live.

To recover:

  • Themes → Regenerate site → All HTML pages re-uploads everything from lastPublishedPath against the current lastPublishedHash. The hash check skips uploads where the file is still in sync; lost files don't match (they have no hash to compare to from Flexweg's side, just a 404), so they get re-uploaded.

Actually no — the hash check only compares against the CMS's stored hash, not Flexweg's actual file. So a lost file would be skipped because the CMS thinks it's still there. To force re-upload, edit the post and re-save (touches updatedAt), then publish.

For bulk recovery: change settings.title (forces a hash mismatch on every page), regenerate, change it back, regenerate again. Heavy-handed but reliable.

Cleanup vs. menu.json

menu.json is always re-uploaded after a publish, regardless of whether the post's URL changed — because menu items might reference the post by slug, and even a non-URL change might require a menu refresh (display name, etc.). Cheap to re-upload (single small JSON), so the publisher doesn't bother optimising.

Why the URL changes warning matters

When you edit a slug in the post editor, an inline warning appears: "This will change the URL. Old links will break." Take it seriously:

  • External links to the old URL break — backlinks from other sites, social media shares, archived emails. There's no automatic redirect (Flexweg is a static host — no redirect rules unless you configure them at the hosting level).
  • Search engines see two pages until they re-crawl: the old URL returns 404, the new URL has the content. Eventually they figure it out, but expect a temporary SEO dip.
  • RSS readers may show the post twice — once with the old URL (still in their cached feed snapshot), once with the new URL (in the regenerated feed).

For high-traffic posts, avoid changing slugs after publishing. Set the slug carefully on first publish.

Continue