Ghost to git-based blog migration: keep your rankings
A Ghost to git-based blog migration playbook: export the clean JSON, migrate members and Stripe IDs, and protect the rankings your Ghost blog already built.
A Ghost to git-based blog migration playbook: export the clean JSON, migrate members and Stripe IDs, and protect the rankings your Ghost blog already built.

Ghost is the one platform in this migration series that isn't actually broken. Its export is a single clean JSON file, not a CSV of mangled rich-text fields. It ships schema markup, an XML sitemap, and canonical tags by default, no plugin required. If you're moving off Ghost, you're not fixing a platform that failed you, you're solving a different problem: the blog living outside your product's repo and PR review process, and a set of migration risks that are real but much narrower than the ones we've covered for Webflow, HubSpot, or Notion.
Ghost runs 22,422 live websites as of this writing, the largest install base in the Headless CMS category at 28.89% share, ahead of Contentful's 14,817 and Sanity's 14,011. That's a small, focused footprint against the CMS market as a whole, consistent with Ghost's positioning as a newsletter-and-membership platform rather than a general-purpose one. That focus is exactly why teams outgrow it: Ghost is built around one editor and one publishing model, and a growing SaaS blog eventually wants neither.
This post covers why teams leave Ghost even though its content model is clean, what actually needs converting during the export, the redirect checklist that protects your rankings, and the subdomain-to-subdirectory decision hiding inside almost every Ghost migration. Same discipline as the other three posts in this cluster, the platform changes, the risk profile doesn't repeat.
Ghost's own limits are narrow and specific: a hard staff-seat cap tied to your plan, and a reverse-proxy requirement if you want your blog on a subdirectory instead of a subdomain. Neither is what actually pushes most SaaS teams out the door.
Ghost(Pro)'s pricing runs Starter at $18/month with 1 staff user and no paid subscriptions, Publisher at $29/month with 3 staff users and paid subscriptions enabled, and Business at $199/month with 15 staff users and 10,000 members. A team of four writers and editors is already past Publisher's seat count. That's a real ceiling, but it's a pricing-tier problem, one more seat purchase away from solved.
The subdirectory limit bites harder. Running Ghost(Pro) from a subdirectory like example.com/blog instead of a subdomain requires the Business plan, a paid reverse-proxy add-on, and a custom Nginx or Apache configuration Ghost has to approve first. That combination of cost and approval friction is why most Ghost blogs default to a subdomain and never revisit the decision, a choice with its own SEO consequences we come back to below.
Neither limit is usually the trigger. The trigger is that a SaaS company's blog runs on a separate admin panel with its own login, its own publish button, and no connection to the pull requests, code review, and CI checks that gate every other change to the product. A post goes live the moment someone in Ghost Admin clicks publish, with no diff for anyone to review and no automated check for a broken link or a missing canonical. That's fine for a solo writer. It stops being fine the moment a content team wants the same editorial guardrails engineering already has.
Here's the part that separates this migration from the other three in this cluster: Ghost's platform-level SEO defaults are genuinely good, and a git-based rebuild has to match them rather than simply improve on a broken baseline.
Ghost's content export is one JSON file, downloaded from Settings, Advanced, Import/Export, then Export, in Ghost Admin, covering your posts and settings in a single structured document. That's a different starting point than Webflow's CSV, where "your carefully formatted blog posts, with headers, bold text, lists, embedded images, come out as plain text or raw HTML blobs in a single CSV column." Ghost's JSON keeps post structure, fields, and relationships intact, because the format mirrors Ghost's own underlying database structure rather than its public API, specifically so a migration can set fields the API itself would ignore.
The catch is what the export leaves out, and it's a longer list than the clean-JSON reputation suggests. We cover the full gap below, but the short version: comments, images, and member data all live outside that single file.
Ghost includes structured data, automatically generated XML sitemaps, and automatic canonical URLs on every page with no plugin and no manual template work. That's the opposite of the HubSpot story, where "every HubSpot site starts at zero for schema markup," and the opposite of Webflow, where a Collection has no schema field at all. A git-based rebuild isn't adding these features for the first time here, it's matching a baseline Ghost already cleared, and it's worth checking exactly which structured data types are worth carrying forward versus reworking; our schema markup for AI Overviews post covers which types actually earn their keep in 2026.
The honest framing for a Ghost migration: the container is clean, but three things live entirely outside it, and each one needs its own migration plan before you touch a redirect.
Comments are the first gap. As of Ghost v5, comments are not included in the standard content export; preserving them requires a full database export, mysqldump for a MySQL-based install, not the Settings export button.
Images are the second. The content JSON export doesn't include your images or media files at all; they live in the server's content directory and have to be backed up separately, the same "download everything before you cancel the plan" discipline every platform in this cluster needs.
Members and Stripe subscriptions are the gap that matters most if your Ghost blog runs paid newsletters, and they get their own export entirely: a CSV with fields including email, subscribed_to_emails, complimentary_plan, and stripe_customer_id. That stripe_customer_id field is what preserves an active paid subscriber's billing relationship across the move. A git-based static blog has no membership system of its own, so this data has to land somewhere else, a separate subscription or billing tool, not get quietly dropped because it wasn't in the main content export.
Ghost's JSON export is clean at the file level and proprietary at the field level. A post written in Ghost's current Lexical editor exports with lexical, html, and plaintext fields populated, and mobiledoc set to null; an older post created before Ghost adopted Lexical may carry mobiledoc data instead. Neither lexical nor mobiledoc is Markdown, they're Ghost's own internal document formats, so converting a post body for a git-based blog means working from the html or plaintext field and running it through a proper HTML-to-Markdown conversion, then hand-reviewing the result the same way you would for any other platform's export. The container is clean JSON. The content inside it still needs the same conversion pass as every other CMS in this series.
Ghost's newer redirect setup uses a redirects.yaml file, uploaded through Settings, Labs, in Ghost Admin, with older installs still using the JSON format Ghost continues to support. The structure is two top-level keys, 301 for permanent redirects and 302 for temporary ones, each mapping an old path to a new one. That's a genuinely well-designed feature, and it's also completely tied to Ghost's own hosting. The moment your domain points somewhere else, that file stops mattering, exactly the same failure mode as HubSpot's redirect tool or Webflow's Site Settings redirects: a platform-hosted feature that dies the instant you leave the platform.
The redirect type still carries the same weight it does everywhere else. Google's documentation is explicit that for a 301 or 308, "the indexing pipeline uses the redirect as a signal that the redirect target should be canonical," while a 302, 303, or 307 gets no such treatment, because those codes signal a move that might not be permanent. Build your redirect map on the new host or CDN from day one, and keep it live for at least a year, the window Google itself recommends for recrawling the backlinks still pointing at your old Ghost URLs.
Mapping is also simpler here than for Webflow or HubSpot, because Ghost's default routing is already flat: posts publish at /{slug}/, with /tag/{slug}/ and /author/{slug}/ for taxonomy pages, no category or folder segment in the path unless a custom theme added one. If your new site keeps that same flat slug pattern, most of your redirect map is a direct one-to-one, old slug to new slug, with no restructuring in between.
Because subdirectory hosting on Ghost(Pro) is a Business-plan add-on most teams never opt into, the honest default is that most Ghost blogs live on a subdomain, blog.example.com, while the main site sits on the root domain. Moving to git is a natural point to ask whether the new blog should live in a subdirectory of the main site instead, example.com/blog, and for most teams the answer is yes.
The reason is authority, not aesthetics. A subdirectory shares the root domain's existing authority and link equity, while a subdomain is treated as a more separate entity by search engines. G2 felt that gap directly: moving its blog to a new learn.g2.com subdomain, from a domain already generating over 750,000 monthly visits, still cost months of trust-building before the new subdomain gained traction. In Jakub Rudnik's own account as G2's Managing Director of SEO, "it took months for Google to build trust in that subdomain despite it being on a DR 88 website that was already generating 750k+ traffic each month. After the first 3 to 4 months, fresh backlinks, and a ton of new content sending Google signals, we picked up traction quickly." That's the trust lag a subdomain carries even from an already-authoritative root domain. A Ghost blog moving from a subdomain into a subdirectory of the main site is moving in the opposite, favorable direction, but it's still a bigger structural change than a same-path platform swap, and it should be planned and budgeted as its own project inside the migration, not treated as a free side effect of moving to git.
Here's the honest timeline split, and it depends entirely on which move you're actually making.
A same-domain, same-path swap, Ghost's own URLs staying on the same subdomain or subdirectory while only the platform changes underneath, is the fast case. A 10 to 15% organic dip in the first four weeks is normal, and most sites return to their pre-migration baseline within 60 to 90 days when redirects are clean and the launch is a single event, not a staggered rollout. Call that the 4-to-8-week honest recovery window for a straightforward Ghost-to-git swap that keeps the same URL structure.
Consolidating a subdomain into the root domain's subdirectory at the same time is a different, harder case, closer to the G2 experience above than to a simple platform swap. The widest documented benchmark for a genuinely hard migration, a full domain change, comes from a study of 892 domain migrations that found an average of roughly 523 days, about 17 months, to fully recover prior organic traffic, with the fastest recoveries at 19 to 23 days. Treat that figure as the ceiling for a domain-level change, not the expected outcome for most Ghost-to-git moves, since keeping the same root domain (even while changing subdomain to subdirectory) is a narrower change than swapping domains outright. Budget toward the slower end of the range specifically if you're consolidating a subdomain, and toward the faster end if you're keeping the exact same URL host.
Submit the new sitemap the day you launch, then check Coverage daily for the first two weeks and weekly through 90 days. Expect a crawl spike immediately after launch, since Google states plainly that it will crawl a moved site more heavily than usual right after a move, as redirected requests from old URLs stack on top of normal crawl demand. If you're also consolidating subdomain to subdirectory, watch the new subdirectory's individual pages in Coverage separately from the rest of the root domain, since that's the segment carrying the trust-building risk, not the pages that didn't move.
Work through this before you export a single post:
Once your posts are Markdown files in a repo, the publishing question changes from "who has Ghost Admin access" to "what does our git workflow look like for content." A new post is a branch and a file. Review happens as PR comments instead of Ghost's own revision history, which nobody outside the writer ever opens. A merge to main triggers the same build and deploy pipeline the rest of your product already uses, no separate Ghost login and no separate mental model for the content team's tools versus engineering's.
You also get to gate every merge on checks Ghost Admin has no equivalent for, broken links, valid canonicals, valid JSON-LD, before a bad page ever reaches production. Our GitHub Actions SEO checks post walks through four automated jobs that do exactly that.
That handoff is also where a repo-native writing tool fits, instead of a Ghost content export you paste into a script once and never touch again. A git-based AI blog writer reads your repo's conventions, drafts a post as a Markdown file on a branch, and opens a pull request the same way a human contributor would, so nothing auto-publishes and your existing review process still gates every post. If you're building keyword strategy fresh alongside the migration rather than just porting an existing Ghost blog, our guide to SEO for SaaS covers picking winnable keywords and building topic clusters before you're locked into any platform's constraints, and Lyra's plans start at $39 a month if you want to see what running that workflow costs once you're there.
Once your Ghost migration lands on git, Lyra can take over the writing from there, reading your repo's conventions and opening a pull request for every new post, the same review gate you already use for code.
FAQ
Not if you map every indexed URL and redirect it with a permanent 301 before you launch. Google's own site-move guidance treats ranking fluctuation during a migration as expected, and most sites return to their pre-migration baseline within 60 to 90 days when the move is handled cleanly. The actual risk in a Ghost migration isn't the platform swap, it's an unmapped slug or a redirect that never gets rebuilt on the new host.
One JSON file with your posts and settings, downloaded from Settings, Advanced, Import/Export, Export in Ghost Admin. It does not include comments (which need a full mysqldump on MySQL-based installs as of Ghost v5), images (which live in the content directory and have to be backed up separately), or members and Stripe subscriber data, which export as their own CSV.
Yes. Members export as a separate CSV with fields including email, subscribed_to_emails, complimentary_plan, and stripe_customer_id. That last field is what preserves an existing paid subscriber's billing relationship across the move, and it has to be imported into whatever membership or billing system replaces Ghost's, since a git-based static blog has no built-in equivalent.
Because the JSON export wraps the post body in Ghost's own editor format, not plain Markdown. A post written in Ghost's current Lexical editor exports with lexical, html, and plaintext fields (and mobiledoc set to null), while an older post may still carry a mobiledoc field instead. Either way, that's a proprietary document format a git-based blog has to convert, typically from the html or plaintext field, not something you can drop straight into a Markdown file.
Consider it seriously if you're currently on a subdomain, since Ghost(Pro) only supports subdirectory hosting on its $199/month Business plan plus a paid reverse-proxy add-on, and most Ghost blogs run on a subdomain by default. A subdirectory shares your root domain's authority; a subdomain is treated as more of a separate entity. It's a bigger move than a same-path platform swap, with a longer recovery window, but it's also the more valuable one long-term.
Built by the tool you're reading about
Lyra finds the topics worth ranking for, writes them in your repo's voice, fact-checks every claim, and opens a pull request scored and ready to merge. You review and hit merge. Want to see what she'd write for you? Start free with three posts, no card.
Keep reading

A Framer to git-based blog migration playbook: why Framer's CMS lacks native categories, tags, and author pages, and how to redirect without losing rankings.

Bing SEO for AI citations, explained: 87% of ChatGPT citations track Bing's top results and Copilot runs on Bing. The Webmaster Tools checklist to fix today.

How to evaluate an AI blog writer in a free trial: a fair-test methodology with one brief across vendors, a fixed rubric, and integration checks.