LoadForge LogoLoadForge

Ghost Load Testing: Performance Testing Ghost CMS with LoadForge

Ghost Load Testing: Performance Testing Ghost CMS with LoadForge

Introduction

Ghost is known for being a fast, modern publishing platform, but speed in development does not automatically guarantee speed in production. If your Ghost CMS powers a high-traffic blog, membership site, newsletter archive, or content-driven e-commerce experience, you need to know how it behaves under real user load. A homepage that feels instant for one editor can slow down dramatically when thousands of readers hit featured posts, search pages, tag archives, and member-only content at the same time.

That is why Ghost load testing matters. With proper load testing, performance testing, and stress testing, you can benchmark how Ghost handles concurrent traffic, identify bottlenecks in your theme or infrastructure, and validate that publishing workflows and content delivery remain fast during traffic spikes. This is especially important for media launches, newsletter sends, seasonal campaigns, and viral content events.

In this guide, you will learn how to load test Ghost CMS with LoadForge using realistic Locust scripts. We will cover public content delivery, authenticated member flows, Ghost Admin API publishing actions, and more advanced scenarios that reflect how Ghost is actually used in production. Because LoadForge runs on cloud-based infrastructure with distributed testing, real-time reporting, CI/CD integration, and global test locations, it is well suited for benchmarking Ghost performance from multiple regions and at meaningful scale.

Prerequisites

Before you start load testing Ghost CMS, make sure you have the following:

  • A running Ghost instance, such as:
    • https://your-ghost-site.com
    • https://staging.your-ghost-site.com
  • Access to the parts of Ghost you want to test:
    • Public frontend pages
    • Member login endpoints if memberships are enabled
    • Ghost Content API or Admin API if relevant
  • A staging or pre-production environment that closely matches production
  • A valid Ghost Admin API key if you want to test publishing or admin workflows
  • Optional member test accounts for authenticated user scenarios
  • LoadForge account access to run distributed load testing at scale

You should also know which Ghost features are enabled on your site. For example:

  • Native memberships and subscriptions
  • Search integration
  • Custom theme with heavy images or JavaScript
  • Newsletter archive pages
  • Dynamic routing with tags, authors, and collections
  • External integrations like Stripe, CDN, or image optimization

For safe performance testing, avoid running destructive or high-volume write tests against production unless you fully understand the impact. Publishing, editing, or member management tests should usually run in staging.

Understanding Ghost Under Load

Ghost is built on Node.js and is generally efficient for content delivery, but several layers can influence performance under load.

How Ghost handles concurrent traffic

Most Ghost traffic is read-heavy. Typical requests include:

  • Homepage requests
  • Individual post pages
  • Tag and author archive pages
  • Asset requests for CSS, JavaScript, fonts, and images
  • Member login and account access
  • API requests from the frontend or integrations

Ghost can serve these efficiently, especially when combined with:

  • Reverse proxies like Nginx
  • CDN caching
  • Optimized themes
  • Database tuning
  • Proper image sizing

Common Ghost bottlenecks

When load testing Ghost CMS, these are the most common bottlenecks to watch for:

Theme inefficiencies

A custom Ghost theme may generate excessive API calls, oversized images, render-blocking assets, or expensive dynamic components. Even if Ghost itself is fast, the theme can slow down page delivery.

Database pressure

Ghost relies on a database backend and can become slower if pages trigger expensive queries, especially on large sites with many posts, tags, members, or newsletter records.

Member authentication overhead

Sites using memberships often see added complexity from:

  • Sign-in flows
  • Session creation
  • Member-only content checks
  • Portal or account-related frontend scripts

Admin and publishing operations

Publishing content through the Ghost Admin API is usually less frequent than public traffic, but it matters for editorial teams. Bulk publishing, scheduled content, and media uploads can stress the application and storage stack.

Asset delivery and cache misses

If your CDN is misconfigured or disabled, Ghost may end up serving too many static resources directly. Load testing should help you distinguish between cached and uncached performance.

A good Ghost performance testing strategy should therefore include both anonymous reader traffic and targeted authenticated or admin workflows.

Writing Your First Load Test

The best first test for Ghost is a read-only traffic simulation that reflects real visitor behavior. This means hitting the homepage, a few post pages, and tag pages with realistic wait times.

Below is a basic Locust script for load testing a public Ghost site.

python
from locust import HttpUser, task, between
 
class GhostPublicUser(HttpUser):
    wait_time = between(1, 5)
 
    @task(5)
    def homepage(self):
        self.client.get("/", name="GET /")
 
    @task(3)
    def featured_post(self):
        self.client.get("/welcome/", name="GET /welcome/")
 
    @task(2)
    def tag_page(self):
        self.client.get("/tag/news/", name="GET /tag/news/")
 
    @task(1)
    def author_page(self):
        self.client.get("/author/ghost/", name="GET /author/ghost/")

What this script does

This script simulates anonymous readers browsing your Ghost site:

  • The homepage is the most common request
  • A single post page is frequently visited
  • Tag archives are loaded less often
  • Author pages are visited occasionally

The weighted @task decorators approximate realistic traffic distribution. This is much better than hammering a single URL because it reflects how Ghost traffic is usually spread across multiple content routes.

Why this is useful

This first load test helps answer basic questions:

  • How many concurrent readers can Ghost handle?
  • Does homepage latency increase under load?
  • Are post pages slower than archive pages?
  • Do any routes return errors under higher concurrency?

In LoadForge, you can run this script across multiple cloud regions to simulate geographically distributed readers. That is especially useful for content publishers with global audiences.

Making the test more realistic

For a real Ghost site, replace the sample paths with actual routes from your installation, such as:

  • /
  • /newsletter/
  • /tag/product-updates/
  • /author/editorial-team/
  • /2024/11/holiday-buying-guide/

You should also include enough unique content URLs to avoid unrealistic cache behavior. Testing only one post page may overstate performance if that page is heavily cached.

Advanced Load Testing Scenarios

Once you have a baseline, move on to more realistic Ghost performance testing scenarios. The following examples cover content API access, member login flows, and Ghost Admin API publishing.

Advanced Scenario 1: Testing Ghost Content API traffic

Many Ghost themes and headless implementations use the Content API to fetch posts, tags, and site data. If your frontend depends on these endpoints, you should load test them directly.

python
from locust import HttpUser, task, between
 
CONTENT_API_KEY = "YOUR_CONTENT_API_KEY"
 
class GhostContentApiUser(HttpUser):
    wait_time = between(1, 3)
 
    @task(4)
    def browse_posts(self):
        self.client.get(
            f"/ghost/api/content/posts/?key={CONTENT_API_KEY}&limit=10&include=tags,authors",
            name="GET Content API posts"
        )
 
    @task(2)
    def browse_single_post_by_slug(self):
        self.client.get(
            f"/ghost/api/content/posts/slug/welcome/?key={CONTENT_API_KEY}&include=tags,authors",
            name="GET Content API post by slug"
        )
 
    @task(2)
    def browse_tags(self):
        self.client.get(
            f"/ghost/api/content/tags/?key={CONTENT_API_KEY}&limit=20",
            name="GET Content API tags"
        )
 
    @task(1)
    def site_settings(self):
        self.client.get(
            f"/ghost/api/content/settings/?key={CONTENT_API_KEY}",
            name="GET Content API settings"
        )

When to use this scenario

This test is valuable if:

  • Your Ghost frontend is headless
  • Your theme fetches data client-side
  • You use Ghost as a content backend for another app
  • You want to isolate API performance from HTML rendering

What to look for

Pay attention to:

  • Response times for collection endpoints like /posts/
  • Whether include=tags,authors significantly increases latency
  • Error rates under concurrency
  • Throughput differences between list and single-item queries

If these endpoints slow down under load, the issue may be database-related or caused by excessive query complexity.

Advanced Scenario 2: Testing Ghost member sign-in and account access

If your Ghost site uses memberships, authentication is a critical part of the user experience. Member login flows often involve email-based magic links in real production use, but for load testing you may need a staging-friendly or session-based approach depending on your implementation.

The example below demonstrates testing a member area using an existing authenticated session token. This is often the most practical approach for performance testing member-only content without relying on email delivery during the test.

python
from locust import HttpUser, task, between
 
class GhostMemberUser(HttpUser):
    wait_time = between(2, 6)
 
    def on_start(self):
        # Example: preload a valid member session cookie captured from staging
        self.client.cookies.set(
            "ghost-members-ssr",
            "REPLACE_WITH_VALID_SESSION_COOKIE",
            domain="staging.your-ghost-site.com"
        )
 
    @task(4)
    def view_members_only_post(self):
        self.client.get("/members-only/market-analysis-q4/", name="GET member post")
 
    @task(2)
    def view_account_page(self):
        self.client.get("/account/", name="GET /account/")
 
    @task(2)
    def view_archive(self):
        self.client.get("/archive/", name="GET /archive/")
 
    @task(1)
    def view_premium_tag(self):
        self.client.get("/tag/premium/", name="GET /tag/premium/")

Why this approach works

Ghost member login is commonly based on secure email magic links, which are difficult to automate at scale in a clean load test. For realistic performance testing, many teams preload valid member sessions in staging and focus on the authenticated browsing behavior after login.

What this helps you measure

This scenario helps evaluate:

  • Latency of gated content delivery
  • Account page rendering performance
  • Member session handling under load
  • Whether premium content checks introduce measurable overhead

If your member pages are significantly slower than public pages, inspect theme logic, account widgets, membership integrations, and any custom portal scripts.

Advanced Scenario 3: Testing Ghost Admin API publishing workflows

Editorial performance matters too. If your team publishes frequently, bulk imports content, or runs scheduled workflows, you should test the Ghost Admin API in staging. This scenario uses JWT authentication with a Ghost Admin API key, which is how Ghost actually authenticates admin API requests.

python
import time
import uuid
import jwt
 
from locust import HttpUser, task, between
 
ADMIN_API_KEY = "YOUR_ADMIN_API_KEY"  # format: id:secret
API_VERSION = "v5.0"
 
def build_ghost_admin_jwt(admin_api_key):
    key_id, secret = admin_api_key.split(":")
    iat = int(time.time())
    exp = iat + 300
 
    payload = {
        "iat": iat,
        "exp": exp,
        "aud": f"/admin/"
    }
 
    token = jwt.encode(
        payload,
        bytes.fromhex(secret),
        algorithm="HS256",
        headers={"kid": key_id}
    )
    return token
 
class GhostAdminUser(HttpUser):
    wait_time = between(3, 8)
 
    def on_start(self):
        token = build_ghost_admin_jwt(ADMIN_API_KEY)
        self.headers = {
            "Authorization": f"Ghost {token}",
            "Content-Type": "application/json"
        }
 
    @task(3)
    def list_posts(self):
        self.client.get(
            f"/ghost/api/admin/posts/?limit=15",
            headers=self.headers,
            name="GET Admin posts"
        )
 
    @task(1)
    def create_draft_post(self):
        unique_id = str(uuid.uuid4())[:8]
        payload = {
            "posts": [
                {
                    "title": f"Load Test Draft {unique_id}",
                    "slug": f"load-test-draft-{unique_id}",
                    "status": "draft",
                    "html": "<p>This is a draft created during Ghost Admin API load testing with LoadForge.</p>",
                    "tags": [{"name": "load-testing"}, {"name": "staging"}]
                }
            ]
        }
 
        self.client.post(
            f"/ghost/api/admin/posts/",
            json=payload,
            headers=self.headers,
            name="POST Admin create draft"
        )

Important note

Only run write-heavy admin tests against staging or a disposable Ghost environment. This script creates draft posts and should not be used against production without cleanup controls.

Why test admin workflows

This kind of performance testing is useful when:

  • Editors complain that the admin dashboard feels slow
  • Bulk publishing causes delays
  • Content automation tools interact with Ghost Admin API
  • You want to benchmark publishing throughput before a launch

What to monitor

Watch for:

  • Increased latency on admin listing endpoints
  • Post creation failures or rate-related errors
  • Database or CPU spikes during write activity
  • Slowdowns when reads and writes happen together

A powerful LoadForge strategy is to run public traffic and admin traffic simultaneously from separate user classes. That helps simulate real-world conditions where editors publish while readers browse.

Advanced Scenario 4: Mixed traffic model for realistic Ghost load testing

The most useful Ghost load testing often combines multiple traffic types. Readers browse public pages, members access premium content, and editors use the admin API. The script below models a mixed environment.

python
import time
import jwt
from locust import HttpUser, task, between
 
CONTENT_API_KEY = "YOUR_CONTENT_API_KEY"
ADMIN_API_KEY = "YOUR_ADMIN_API_KEY"
API_VERSION = "v5.0"
 
def build_ghost_admin_jwt(admin_api_key):
    key_id, secret = admin_api_key.split(":")
    now = int(time.time())
    payload = {
        "iat": now,
        "exp": now + 300,
        "aud": "/admin/"
    }
    return jwt.encode(
        payload,
        bytes.fromhex(secret),
        algorithm="HS256",
        headers={"kid": key_id}
    )
 
class GhostReaderUser(HttpUser):
    wait_time = between(1, 4)
 
    @task(5)
    def homepage(self):
        self.client.get("/", name="Reader: /")
 
    @task(3)
    def post_page(self):
        self.client.get("/2024/11/holiday-buying-guide/", name="Reader: post")
 
    @task(2)
    def tag_page(self):
        self.client.get("/tag/reviews/", name="Reader: tag")
 
    @task(2)
    def content_api(self):
        self.client.get(
            f"/ghost/api/content/posts/?key={CONTENT_API_KEY}&limit=5&include=tags,authors",
            name="Reader: Content API posts"
        )
 
class GhostEditorUser(HttpUser):
    wait_time = between(5, 10)
    weight = 1
 
    def on_start(self):
        token = build_ghost_admin_jwt(ADMIN_API_KEY)
        self.headers = {
            "Authorization": f"Ghost {token}",
            "Content-Type": "application/json"
        }
 
    @task
    def admin_dashboard_data(self):
        self.client.get(
            "/ghost/api/admin/posts/?limit=10",
            headers=self.headers,
            name="Editor: Admin posts"
        )

Why this scenario matters

Mixed traffic tests are ideal for:

  • Launch-day simulations
  • Newsletter send traffic spikes
  • Content-heavy commerce experiences
  • Membership sites with simultaneous browsing and editorial activity

With LoadForge, you can scale this scenario across distributed cloud generators and compare how Ghost performs from different global test locations.

Analyzing Your Results

After running your Ghost load test, the next step is interpreting the results correctly. Raw request counts are not enough. Focus on the metrics that reveal real user experience and backend health.

Key metrics to review

Response time percentiles

Do not rely only on average response time. Look at:

  • Median latency
  • 95th percentile
  • 99th percentile

Ghost may appear fast on average while a meaningful percentage of users experience slow page loads.

Requests per second

This tells you how much traffic Ghost can sustain. Compare throughput across:

  • Homepage
  • Post pages
  • Tag pages
  • Content API endpoints
  • Admin API endpoints

Error rate

Any increase in:

  • 500 errors
  • 502/504 gateway errors
  • 429 rate limits
  • Authentication failures

should be investigated immediately.

Behavior under ramp-up

A healthy Ghost deployment should degrade gradually under load. If latency suddenly spikes at a specific concurrency level, that usually indicates a bottleneck in:

  • Database connections
  • Application workers
  • Reverse proxy configuration
  • Caching layer
  • Theme rendering

Route-specific performance

Use named requests in Locust so LoadForge reports are easy to interpret. For Ghost, compare:

  • GET /
  • GET /tag/.../
  • GET member post
  • GET Content API posts
  • POST Admin create draft

That makes it much easier to pinpoint whether issues affect public traffic, members, or editors.

Correlate with infrastructure telemetry

For best results, combine LoadForge test data with server metrics such as:

  • CPU usage
  • Memory utilization
  • Database query time
  • Cache hit ratio
  • CDN response behavior
  • Nginx or proxy queueing

LoadForge’s real-time reporting helps you see exactly when latency and errors begin, making it easier to match those points to backend resource saturation.

Performance Optimization Tips

If your Ghost load testing reveals slowdowns, these optimizations often provide the biggest wins.

Enable strong caching

Ghost sites benefit significantly from:

  • CDN caching for images and static assets
  • Reverse proxy caching where appropriate
  • Browser caching headers for theme assets

This reduces origin load and improves content delivery speed.

Optimize your theme

Audit your Ghost theme for:

  • Large images
  • Excessive JavaScript
  • Too many fonts
  • Blocking third-party scripts
  • Expensive dynamic homepage sections

A lightweight theme can dramatically improve load testing results.

Reduce API overfetching

If your frontend or theme uses the Content API, avoid requesting unnecessary relations or oversized payloads. For example, include=tags,authors is useful, but do not add extra fields unless needed.

Tune the database

Large Ghost sites may need database tuning, indexing review, and query analysis. Slow tag pages, archive pages, or member pages often point to database pressure.

Separate read and write testing

Publishing workflows and public browsing should be measured independently and together. This helps you understand whether write operations degrade reader experience.

Test from multiple regions

If your audience is global, use LoadForge global test locations to measure international latency and CDN effectiveness.

Common Pitfalls to Avoid

Ghost load testing is straightforward, but several mistakes can produce misleading results.

Testing only the homepage

A homepage-only test does not reflect real traffic. Include post pages, tags, archive routes, and APIs.

Ignoring cached versus uncached behavior

If every request hits the same URL, you may mostly measure cache performance. Use varied content paths to understand actual Ghost behavior.

Load testing production write endpoints

Do not test draft creation, publishing, or member management in production unless you have explicit controls and cleanup procedures.

Forgetting assets and frontend dependencies

Ghost performance is not just backend HTML generation. Heavy images, fonts, and scripts can dominate user-perceived latency.

Using unrealistic user pacing

Users do not refresh pages every 100 milliseconds. Set realistic wait times in Locust to simulate actual browsing patterns.

Not testing member-only experiences

If memberships are central to your Ghost business model, public-page testing alone is not enough. Include account pages, premium posts, and authenticated flows.

Overlooking newsletter-driven spikes

Ghost traffic often arrives in bursts after newsletter sends or social promotion. Run stress testing scenarios that model sudden surges, not just gradual ramp-ups.

Conclusion

Ghost is fast by design, but every real-world implementation is different. Your theme, member flows, APIs, infrastructure, and publishing patterns all affect how the platform performs under load. By running targeted load testing, performance testing, and stress testing against realistic Ghost workflows, you can identify bottlenecks before they impact readers, members, or editors.

With LoadForge, you can build realistic Locust-based Ghost tests, run them on distributed cloud infrastructure, monitor results in real time, and integrate performance testing into your CI/CD pipeline. Whether you are benchmarking a content-heavy CMS, validating a premium membership experience, or preparing for a high-traffic campaign, LoadForge gives you the visibility you need to keep Ghost fast and reliable.

If you are ready to benchmark Ghost CMS under real traffic conditions, try LoadForge and start building your first Ghost load test today.

Try LoadForge free for 7 days

Set up your first load test in under 2 minutes. No commitment.