Skip to main content
✨ Run your entire business in one platform — CRM, HR, Accounting, Projects & more. Start Free Trial →

How We Built a Programmatic SEO Engine Serving 80K+ Pages on WordPress (Without Using wp_posts)

How We Built a Programmatic SEO Engine Serving 80K+ Pages on WordPress (Without Using wp_posts)
By: Dev.to Top Posted On: March 24, 2026 View: 4
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'
Share:

Tags:
#0 

Read this on Dev.to Top Header Banner

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 →

Comments

Power your business with Mewayz ERP

All-in-one platform: CRM, HR, Accounting, Project Management, eCommerce & more. 14-day free trial.

Start Your Free Trial →

No credit card required · Cancel anytime · 131+ modules

Contact Us
  Follow Us
Site Map
Get Site Map
About

Mewayz News brings you the latest breaking news, in-depth analysis, and trending stories from around the world. Covering politics, technology, business, sports, entertainment, and more — updated every hour, 24/7.

Mewayz Network

Mewayz App Stream Watch TV Music Games Tools Calculators Dictionary Books Quotes Recipes Photos Fonts Icons Study Papers Resume Templates Compare Reviews Weather Trading Docs Draw Paste Sign eBooks AI Learn Currency Convert Translate Search QR Code Timer Typing Colors Fitness Invoice Directory Social Seemless