When we set out to build startup-cost.com, we knew traditional WordPress wouldn't cut it. We needed to serve 79,000+ unique pages - one for every combination of 479 cities and 167 business types - with real cost data, real-time calculations, and solid performance.
Most people hear "80K pages on WordPress" and assume we're crazy. WordPress is a blogging platform, right? Well, yes - but under the hood it's a flexible PHP framework with a powerful rewrite engine. We just had to throw away the parts that don't scale and build our own.
Here's the full story of how we did it without a single row in wp_posts.
The Problem with wp_posts at Scale
WordPress stores all content in a single table called wp_posts. For a blog or a small business site with a few hundred pages, this works fine. But when you start pushing tens of thousands of rows into that table, things fall apart quickly:
Query performance degrades - WordPress joins wp_posts with wp_postmeta for almost every query. With 80K posts, each with 10+ meta fields, you're looking at 800K+ rows in postmeta alone. Queries that used to take 5ms now take 500ms.
The admin panel becomes unusable - Try loading the "All Posts" screen with 80K entries. WordPress paginates, sure, but even counting the total takes forever.
Revision history eats disk - WordPress auto-saves revisions. With programmatic content that gets regenerated, you end up with 3-4x the actual content in revision rows.
XML sitemaps choke - Popular sitemap plugins try to query all posts at once. With 80K rows, they either timeout or consume all available memory.
Imports and exports break - WordPress export generates a single XML file. Good luck with an 80K-post WXR file.
We needed a fundamentally different approach.
Why Not Use a Static Site Generator or a Different CMS?
Fair question. We considered Hugo, Next.js, and even a custom Node.js app. But WordPress gave us specific advantages:
Hosting is dirt cheap - Shared WordPress hosting costs a few dollars a month and handles our traffic fine
The plugin ecosystem - We still use Yoast for basic SEO settings, WP Rocket for caching, and other utility plugins
Familiar deployment - Our team knows WordPress inside out. No learning curve, no new CI/CD pipeline needed
PHP is actually fast enough - With opcache and a clean query pattern, PHP 8 serves pages in double-digit milliseconds
The key insight was: we don't have to use WordPress the way it was designed. We can use it as a routing and rendering framework while storing our data however we want.
The Architecture
We built a custom WordPress plugin called sc-engine that bypasses wp_posts entirely. Here's how each piece works:
1. Custom Database Tables
Instead of stuffing everything into posts and postmeta, we created dedicated tables with proper schemas:
-- Cities with cost-of-living metrics
CREATE TABLE sc_cities (
id INT AUTO_INCREMENT PRIMARY KEY,
slug VARCHAR(100) UNIQUE NOT NULL,
name VARCHAR(200) NOT NULL,
country VARCHAR(100) NOT NULL,
country_code CHAR(2) NOT NULL,
region VARCHAR(100),
population INT,
cost_index DECIMAL(5,2) NOT NULL,
rent_per_sqft DECIMAL(8,2),
avg_wage DECIMAL(10,2),
regulatory_score DECIMAL(3,1),
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_country (country_code),
INDEX idx_slug (slug)
);
-- Business types with baseline cost structures
CREATE TABLE sc_businesses (
id INT AUTO_INCREMENT PRIMARY KEY,
slug VARCHAR(100) UNIQUE NOT NULL,
name VARCHAR(200) NOT NULL,
category VARCHAR(100),
base_cost JSON NOT NULL,
sqft_needed INT,
employee_count_min INT,
employee_count_max INT,
license_requirements JSON,
INDEX idx_slug (slug),
INDEX idx_category (category)
);
-- Pre-calculated cost breakdowns for popular combinations
CREATE TABLE sc_calculations_cache (
city_id INT NOT NULL,
business_id INT NOT NULL,
total_cost DECIMAL(12,2),
breakdown JSON,
calculated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (city_id, business_id)
);
This gives us proper data types, proper indexes, and proper normalization. Each query hits exactly the data it needs - no joins with a bloated meta table.
2. Virtual URL Routing
WordPress has a powerful but underused rewrite API. We register custom URL patterns that map to our plugin's rendering logic:
add_action('init', function() {
// Individual city+business pages
// /start-a-business-in-city/
add_rewrite_rule(
'^start-a-([^/]+)-in-([^/]+)/?$',
'index.php?sc_business=$matches[1]&sc_city=$matches[2]',
'top'
);
// City overview pages
// /city/city-slug/
add_rewrite_rule(
'^city/([^/]+)/?$',
'index.php?sc_city=$matches[1]',
'top'
);
// Business type overview pages
// /business/business-slug/
add_rewrite_rule(
'^business/([^/]+)/?$',
'index.php?sc_business=$matches[1]',
'top'
Tags:
#0
Want to run a more efficient business?
Mewayz gives you CRM, HR, Accounting, Projects & eCommerce — all in one workspace. 14-day free trial, no credit card needed.
Try Mewayz Free →