Bojan Josifoski < wp developer />

Building 24 Pages That Don’t Exist in the Database

November 19, 2025 • Bojan

Yes, you read that correctly.

Everyone “knows” WordPress stores pages in wp_posts.
Everyone also accepts the slow queries, the meta bloat, the accidental deletions, the import/export nightmares.

I don’t.

For a SaaS dashboard with 24 different admin screens, relying on database pages is basically architectural self-harm.

So I stopped doing it.


The Trick WordPress Never Told You About

Instead of creating pages in the database…
I just don’t.

I intercept routing before WordPress does anything:

add_action('template_redirect', function() {
    $path = trim(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH), '/');

    $virtual_pages = [
        'app/orders' => 'virtual-pages/all-orders.php',
        'app/samples' => 'virtual-pages/all-samples.php',
        // ... 20 more
    ];

    if (isset($virtual_pages[$path])) {
        global $wp_query;
        $wp_query->is_404 = false;
        $wp_query->is_page = true;

        include get_template_directory() . '/' . $virtual_pages[$path];
        exit;
    }
});

No posts table.
No queries.
No revisions.
No “who deleted the page?” mysteries.

Just pure, clean routing.


What This Unlocks

1. Pure Performance

Database pages:

SELECT * FROM wp_posts...
SELECT * FROM wp_postmeta...

Virtual pages:

0 queries.
Just vibes.

2. Actual Version Control

virtual-pages/
├── all-orders.php
├── order-details.php
└── edit-sample.php

You edit, commit, deploy.
Your UI is in Git, where it belongs.

3. Zero user screwups

Users can’t delete a page that doesn’t exist.
End of problem.

4. Real SaaS structure

You’re no longer “using WordPress.”
You’re building an app that happens to run on WordPress.


The Full Pattern as Reusable Code

<?php
/**
 * Virtual pages loader for WordPress
 *
 * - Treats PHP files in a folder (e.g. /virtual-pages) as "pages"
 * - No entries in wp_posts
 * - Works on subdirectory installs (https://example.com/subsite/app/orders)
 *
 * Usage:
 * 1. Put your virtual page templates in:  wp-content/themes/your-theme/virtual-pages/
 * 2. Create a wrapper template:          wp-content/themes/your-theme/virtual-page-wrapper.php
 * 3. This class will route /slug to virtual-pages/slug.php
 */

if ( ! class_exists( 'BV_Virtual_Pages_Loader' ) ) {

	class BV_Virtual_Pages_Loader {

		/**
		 * Folder (inside the theme) that contains virtual page templates.
		 *
		 * @var string
		 */
		protected $folder;

		/**
		 * Map of slug => file path.
		 *
		 * @var array
		 */
		protected $slugs = [];

		/**
		 * Currently requested virtual page slug (if any).
		 *
		 * @var string|null
		 */
		protected $current_slug = null;

		/**
		 * @param string $folder Relative folder inside the theme (no leading slash).
		 */
		public function __construct( $folder = 'virtual-pages' ) {
			$this->folder = trim( $folder, '/' );

			add_action( 'init', [ $this, 'scan_files' ] );
			add_filter( 'template_include', [ $this, 'intercept_request' ], 99 );

			// Keep titles consistent for virtual pages.
			add_filter( 'the_title', [ $this, 'maybe_override_title' ], 10, 2 );
			add_filter( 'get_the_title', [ $this, 'maybe_override_title' ], 10, 2 );
		}

		/**
		 * Scan the virtual pages folder and register all *.php files as slugs.
		 */
		public function scan_files() {
			$directory = trailingslashit( get_stylesheet_directory() ) . $this->folder;

			if ( ! is_dir( $directory ) ) {
				return;
			}

			foreach ( glob( $directory . '/*.php' ) as $file ) {
				$slug                 = basename( $file, '.php' );
				$this->slugs[ $slug ] = $file;
			}
		}

		/**
		 * Get the current request path converted to a slug.
		 *
		 * Handles subdirectory installs like /my-site/app/dashboard.
		 *
		 * @return string
		 */
		protected function get_requested_slug() {
			$request_uri = parse_url( $_SERVER['REQUEST_URI'] ?? '', PHP_URL_PATH );
			$site_path   = parse_url( home_url(), PHP_URL_PATH );

			if ( $request_uri === null ) {
				$request_uri = '';
			}
			if ( $site_path === null ) {
				$site_path = '';
			}

			// Strip the site base path (for subdirectory installs).
			if ( $site_path !== '/' && $site_path !== '' && $request_uri !== '' && strpos( $request_uri, $site_path ) === 0 ) {
				$request_uri = substr( $request_uri, strlen( $site_path ) );
			}

			return trim( $request_uri, '/' );
		}

		/**
		 * Intercept the template resolution and route to a virtual page if it matches.
		 *
		 * @param string $template Default template path.
		 * @return string
		 */
		public function intercept_request( $template ) {
			$requested_slug       = $this->get_requested_slug();
			$this->current_slug = $requested_slug;

			if ( isset( $this->slugs[ $requested_slug ] ) ) {
				// Mark as a valid page, not a 404.
				status_header( 200 );

				global $wp_query;
				$wp_query->is_404  = false;
				$wp_query->is_page = true;

				// Browser <title>.
				add_filter(
					'document_title_parts',
					function ( $parts ) use ( $requested_slug ) {
						$parts['title'] = $this->humanize_slug( $requested_slug );
						return $parts;
					}
				);

				// Let title functions know we're on a virtual page.
				$GLOBALS['virtual_page_slug'] = $requested_slug;

				// Wrapper template: can include header/footer and then load the actual page.
				$wrapper = trailingslashit( get_stylesheet_directory() ) . 'virtual-page-wrapper.php';

				if ( file_exists( $wrapper ) ) {
					return $wrapper;
				}

				// Fallback: load the virtual page template directly.
				return $this->slugs[ $requested_slug ];
			}

			return $template;
		}

		/**
		 * Override the_title() / get_the_title() for virtual pages.
		 *
		 * @param string     $title
		 * @param int|string $post_id
		 * @return string
		 */
		public function maybe_override_title( $title, $post_id = null ) {
			if ( isset( $GLOBALS['virtual_page_slug'] ) && ! is_admin() ) {
				return $this->humanize_slug( $GLOBALS['virtual_page_slug'] );
			}

			return $title;
		}

		/**
		 * Get the current virtual slug (optional helper for wrappers).
		 *
		 * @return string|null
		 */
		public function get_current_slug() {
			return $this->current_slug;
		}

		/**
		 * Convert a slug like "app-orders" to "App Orders".
		 *
		 * @param string $slug
		 * @return string
		 */
		protected function humanize_slug( $slug ) {
			return ucwords( str_replace( '-', ' ', $slug ) );
		}
	}

	// Bootstrap: create loader instance once the theme is ready.
	add_action(
		'after_setup_theme',
		function () {
			new BV_Virtual_Pages_Loader( 'virtual-pages' );
		}
	);
}

You can pair this with a simple virtual-page-wrapper.php like:

<?php
// virtual-page-wrapper.php
get_header();

$slug = isset( $GLOBALS['virtual_page_slug'] ) ? $GLOBALS['virtual_page_slug'] : '';
if ( $slug ) {
    $file = trailingslashit( get_stylesheet_directory() ) . 'virtual-pages/' . $slug . '.php';
    if ( file_exists( $file ) ) {
        include $file;
    } else {
        echo '<p>Virtual page not found.</p>';
    }
}

get_footer();

Real Example: My 24-Page Dashboard

My system has:

All inside one folder:

virtual-pages/
├── scan.php
├── manager-dashboard.php
├── create-order.php
└── system-settings.php

Not a single database page.
Deployment is just Git push.


When to Use This

Use it when:
You’re building a SaaS or dashboard
Pages are interface, not “content”
You want Git over database
You want speed
You hate WordPress menus

Avoid it when:
You need the WP editor
Clients manage content
SEO matters
Comments/revisions matter


The Reality Check

WordPress is not an app framework.
But you can bend it.
Hard.

Application pages don’t belong in wp_posts.
They belong in code.

24 pages.
0 posts.
0 database problems.
Maximum control.

Use this pattern and you stop fighting WordPress – you make it work for you.

About the Author

About the Author

I’m Bojan Josifoski - I’m a WordPress systems engineer who developed and maintained a proprietary WordPress-based framework used by U.S. financial institutions between 2016 and 2025.

← Back to Blog