Your CMS stops being a website. Stop paying for it like one.
Most people go headless and keep the same hosting plan they had before. That plan was sized for public traffic. Your WordPress install no longer has any, because visitors hit static files on a CDN and never reach PHP at all.
You are now paying a traffic bill for a machine that talks to two things: your build process and a handful of editors.
What changes when you go headless
A normal WordPress site runs PHP and hits MySQL on every request. Ten thousand visitors means ten thousand round trips through that stack, which is why hosting plans are priced by visits and why caching plugins exist at all.
Take the front end away and the shape of the load changes completely.
| Traditional WordPress | Headless WordPress | |
|---|---|---|
| Who calls it | Every visitor | Your build process and your editors |
| Requests per day | Scales with traffic | Roughly fixed, tens to low hundreds |
| Peak concurrency | Whatever a busy hour brings | One build, occasionally two |
| Uptime need | Total. Down means down. | High, but a blip only delays publishing |
| What sizing depends on | Visits and plugins | Post count and build frequency |
That last row deserves a moment. Your CMS falling over is now an editorial inconvenience rather than an outage. Nobody browsing your site notices. You can drop a tier of redundancy you were previously right to pay for.
The options, and what they cost
Shared hosting
Cheap, and better suited to headless than it ever was to a live site. The usual complaint about shared hosting is that your neighbours steal your CPU during traffic spikes. You no longer have traffic spikes.
What you give up is control. Many shared hosts block outbound HTTP requests or run aggressive page caches that interfere with REST responses. Check that wp_remote_post works before you commit, because your publish webhook depends on it.
A small VPS
This is where I land most often. You get root, predictable resources, and a bill that does not move. Two gigabytes of RAM runs WordPress, MySQL and a web server with room to spare when the only caller is a build.
The cost is your attention. Somebody has to apply security updates, renew certificates and watch the disk fill. If that somebody does not exist on your side, pick managed hosting and stop reading this section.
Managed WordPress hosting
More expensive, and still frequently the right answer. You are buying patching, backups, staging and a support line. For a business with no technical staff, that is worth more than the difference in price.
One thing to watch: some managed hosts price by monthly visits. Ask how they count a headless install, because your visit count is about to fall through the floor and you should be paying the lowest tier, not the one you were on.
A container platform
WordPress in a container, on something that handles the orchestration for you. Clean deploys, easy rollback, and configuration that lives in a file rather than in somebody's memory.
It suits teams already running containers. If this would be your only one, the operational overhead outweighs what you gain.
Sizing the box
Forget visitor counts. Two things decide your size now: how many posts a full build has to read, and how often that build runs.
A build with a thousand posts requesting a hundred at a time makes ten requests. Each one returns a few hundred kilobytes of JSON. The whole exercise finishes in seconds and never troubles a modest server.
# Time a realistic build fetch against your current installtime curl -s -o /dev/null \ "https://cms.example.com/wp-json/wp/v2/posts?per_page=100&_fields=slug,title,content,date" # Watch memory while it runs, in another shellwatch -n 1 'free -m'Run that on your existing hosting before you move anything. If a hundred posts come back in under two seconds and memory barely moves, you already know a small box will do.
The costs people forget
- Media bandwidth. Images still get served to real visitors. If they live in
wp-content/uploadson your origin, every image request hits the server you just downsized. - Backups. Often bundled with managed hosting and never included on a bare VPS. Budget a few dollars a month for automated, offsite, tested backups.
- Staging. You want somewhere to test a plugin update before it breaks the API your build depends on. Some hosts include it. Others charge for a second environment.
- Build minutes. Your front end host bills these. Rebuild the entire site on every typo fix and the number climbs quietly.
- Plugin licences. Advanced Custom Fields Pro, a Yoast add-on, whatever else the editorial workflow relies on. These follow you into headless.
Media is the big one, and the easiest to fix. Move uploads to object storage with a CDN in front and your origin stops serving files entirely.
// Point WordPress at the CDN so every stored URL is already correct.// Do this before you migrate media, or you will rewrite thousands of rows later.define( 'WP_CONTENT_URL', 'https://media.example.com/wp-content' );define( 'UPLOADS', 'wp-content/uploads' );A worked example
A marketing site I moved last year. Around four hundred posts, three editors, publishing most weekdays.
| Line item | Before | After |
|---|---|---|
| WordPress hosting | Managed plan sized for traffic | Small VPS, 2 GB RAM |
| Media | Served from origin | Object storage behind a CDN |
| Front end | None, WordPress served it | Static hosting, free tier |
| Backups | Included in the managed plan | Separate automated backups |
| Staging | Included | Second small VPS, off most of the time |
The total came down. What mattered more to them was that the bill stopped moving with traffic, because a busy month no longer touched the origin at all.
Keeping the bill predictable
Four habits, in the order I would apply them.
Cache API responses in your build. Next.js does this for you when you set revalidate, and it means a rebuild does not refetch content that has not changed.
// Tagged fetches: the webhook can invalidate exactly what changedconst res = await fetch(WP + '/wp-json/wp/v2/posts?per_page=100', { next: { revalidate: 3600, tags: ['posts'] },});Rebuild pages, not sites. A publish webhook that revalidates one path costs you almost nothing. A full deploy on every edit costs you build minutes and makes editors wait.
Keep media off the origin, which I covered above. Then turn off what you are not using: cron jobs from plugins that assume a public front end, search indexing, comment processing, anything scheduled that exists to serve visitors you no longer have.
// Stop WordPress running cron on request. Use a real system cron instead,// so a build request never triggers scheduled work it should not be paying for.define( 'DISABLE_WP_CRON', true );# Once every fifteen minutes is plenty for a headless install*/15 * * * * cd /var/www/cms && wp cron event run --due-now --quietWhat I usually pick
If the client has someone technical: a small VPS, media on object storage, automated backups, a staging box that stays powered down between releases. It is the cheapest option that gives you full control, and control is what stops surprises.
If the client has nobody technical: managed WordPress on the lowest tier that fits, media on object storage anyway. You pay more and you buy the thing you actually need, which is not having to think about it.
I would not start on shared hosting. It works, and the money you save is small enough that the first blocked outbound request wipes out the gain.
Picking the box is the easy part once the architecture is settled. If you want the whole thing handled, from the migration through to the headless setup and where it runs, that is what I do.
Related readingKeep the WordPress editor, drop the WordPress front endHosting is the last decision, not the first. Get the architecture right, confirm your editors can still work the way they expect, then size the box around what the build actually does.
Related readingWhy a headless CMS beats building on WordPress or WixResources
- WordPress: hardening WordPress
Baseline server and install security, which matters more once the CMS is infrastructure.
- WordPress: editing wp-config.php
Every constant used here, including DISABLE_WP_CRON and the content URL settings.
- WP-CLI cron command
Running scheduled events from system cron instead of on page requests.
- Next.js: incremental static regeneration
Rebuilding single pages on demand rather than deploying the whole site.
- MDN: HTTP caching
The header behaviour your CDN relies on when it serves media from object storage.