AboutBlogContact
Backend EngineeringAugust 19, 2003 6 min read 180Updated: June 22, 2026

WordPress 0.7: The Day Blogging Became Accessible to Everyone (2003)

AunimedaAunimeda
📋 Table of Contents

Before WordPress, publishing to the web meant one of three things: writing HTML by hand, paying for proprietary CMS software, or wrestling with Movable Type (Perl, complex) or b2/cafelog (PHP, abandoned by its original developer).

Matt Mullenweg was using b2/cafelog. In January 2003, b2's author, Michel Valdrighi, had seemingly disappeared. Mullenweg wrote a blog post: "Fortunately, b2/cafelog is GPL, which means that I could use the existing codebase to create a fork." Mike Little replied in the comments that he was interested in collaborating. On May 27, 2003, WordPress 0.7 was released.

I installed it that August. It was 2,500 lines of PHP. Installation: unzip, upload via FTP, create one MySQL database, visit install.php. Five minutes. For comparison, Movable Type's installation required configuring Perl modules, setting file permissions on a dozen directories, and editing a config file by hand. The difference in friction was categorical.


What WordPress 0.7 Actually Was

WordPress 0.7 was a single PHP application, not a framework. There were no plugins. There was one "theme" - a default template that you edited directly. The admin panel was basic HTML forms. But the core loop worked:

<?php
// The WordPress Loop - 2003 version
// This is the actual logic from WordPress 0.7
// query_posts() + the_post() pattern that defined a decade of templating

// In the index.php template:
<?php query_posts('showposts=10'); ?>
<?php while (have_posts()) : the_post(); ?>
  <div class="post">
    <h2>
      <a href="<?php the_permalink(); ?>">
        <?php the_title(); ?>
      </a>
    </h2>
    <div class="postmetadata">
      Posted on <?php the_time('F j, Y'); ?>
      by <?php the_author(); ?>
      in <?php the_category(', '); ?>
      | <?php comments_popup_link('No Comments', '1 Comment', '% Comments'); ?>
    </div>
    <div class="entry">
      <?php the_content('Read the rest of this entry &raquo;'); ?>
    </div>
  </div>
<?php endwhile; ?>

This loop - while (have_posts()) : the_post() - became the most-copied PHP code pattern in the history of the web. By 2010, it ran on roughly 13% of all websites.


The Database Schema (WordPress 0.7 / 0.71)

-- WordPress 0.7 original schema - MySQL 3.23+
-- Still recognizable in WordPress today, 20 years later

CREATE TABLE wp_posts (
  ID           bigint(20) unsigned NOT NULL auto_increment,
  post_author  bigint(20) unsigned NOT NULL default '0',
  post_date    datetime NOT NULL default '0000-00-00 00:00:00',
  post_content longtext NOT NULL,
  post_title   text NOT NULL,
  post_category int(4) NOT NULL default '0',
  post_status  varchar(10) NOT NULL default 'publish',
  comment_status varchar(15) NOT NULL default 'open',
  post_name    varchar(200) NOT NULL default '',  -- the URL slug
  post_modified datetime NOT NULL default '0000-00-00 00:00:00',
  PRIMARY KEY  (ID),
  KEY post_name (post_name),
  KEY post_status (post_status),
  KEY post_date (post_date)
) TYPE=MyISAM;

CREATE TABLE wp_comments (
  comment_ID        bigint(20) unsigned NOT NULL auto_increment,
  comment_post_ID   bigint(20) unsigned NOT NULL default '0',
  comment_author    tinytext NOT NULL,
  comment_author_email varchar(100) NOT NULL default '',
  comment_author_url varchar(200) NOT NULL default '',
  comment_author_IP varchar(100) NOT NULL default '',
  comment_date      datetime NOT NULL default '0000-00-00 00:00:00',
  comment_content   text NOT NULL,
  comment_approved  enum('0','1','spam') NOT NULL default '1',
  PRIMARY KEY (comment_ID),
  KEY comment_post_ID (comment_post_ID),
  KEY comment_approved (comment_approved)
) TYPE=MyISAM;

CREATE TABLE wp_options (
  option_id    bigint(20) unsigned NOT NULL auto_increment,
  blog_id      int(11) NOT NULL default '0',
  option_name  varchar(64) NOT NULL default '',
  option_value longtext NOT NULL,
  autoload     varchar(20) NOT NULL default 'yes',
  PRIMARY KEY (option_id),
  UNIQUE KEY option_name (option_name)
) TYPE=MyISAM;

The wp_options table was a key-value store for all configuration. siteurl, blogname, admin_email, active_plugins - all in one table. This design was called "the options table pattern." It was flexible, required no schema migrations when adding new settings, and was later criticized as a performance bottleneck. In 2003, it was the right call.


Customizing WordPress 0.7: Direct Template Editing

There were no plugins yet. Customization meant editing index.php, wp-comments.php, and the handful of included files directly:

<?php
// Adding a custom "Recent Posts" sidebar - 2003 method
// No widgets. No plugins. Direct PHP in the sidebar template.

// sidebar.php
?>
<div id="sidebar">
  <h2>Recent Posts</h2>
  <ul>
  <?php
  // Direct database query - no WP_Query yet
  global $wpdb;
  $recent = $wpdb->get_results(
      "SELECT ID, post_title, post_date
       FROM $wpdb->posts
       WHERE post_status = 'publish'
       ORDER BY post_date DESC
       LIMIT 5"
  );
  foreach ($recent as $post) :
  ?>
    <li>
      <a href="<?php echo get_permalink($post->ID); ?>">
        <?php echo htmlspecialchars($post->post_title); ?>
      </a>
    </li>
  <?php endforeach; ?>
  </ul>

  <h2>Archives</h2>
  <ul>
    <?php wp_get_archives('type=monthly'); ?>
  </ul>
</div>

Direct SQL against $wpdb in templates became a pattern that developers used for years. It was the only way. The plugin API came in WordPress 1.2 (May 2004). Until then, everything was direct template modification.


The Permalink Revolution

One feature WordPress 0.7 implemented better than anything before it: clean URLs. The default was ?p=42. But with mod_rewrite enabled:

# .htaccess generated by WordPress
RewriteEngine On
RewriteBase /

# Rewrite everything that isn't a file or directory
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]

This let you set permalink structures like /%year%/%monthnum%/%postname%/ - clean, readable URLs that worked as links, were human-memorable, and ranked better in the search engines that were beginning to weight URL structure.

The combination of clean URLs + easy publishing + comments built-in was the package. No other tool in 2003 offered all three without configuration complexity that blocked non-developers.


Why This Was the Most Important Software of 2003

Every year in tech produces tools that seem important at launch and fade. WordPress 0.7 seemed unimportant at launch - 2,500 lines of PHP, no plugins, one template - and became the infrastructure of the web.

What made it survive when Movable Type, b2, and dozens of other early blog tools did not:

GPL license. Anyone could fork it, modify it, redistribute it. The community could fix bugs without waiting for a company. This is what allowed the plugin ecosystem to explode after 1.2.

The update path was never broken. From 0.7 to today, upgrading WordPress never required manual database migration or template rewriting. The schema evolved backward-compatibly. This meant users stayed on WordPress instead of migrating to whatever was newer.

It ran on shared hosting. PHP + MySQL + .htaccess - the LAMP stack. The same $8/month hosting plan that ran WordPress 0.7 would run WordPress 6.x today. The addressable market was everyone with a hosting plan.

By 2008, WordPress was the dominant blogging platform. By 2012, it was the dominant CMS. By 2023, 43% of all websites ran WordPress. That story started with a 2,500-line PHP script uploaded to SourceForge on May 27, 2003.


Aunimeda builds production-grade backend systems - APIs, microservices, real-time applications, and system integrations.

Contact us for backend engineering services. See also: Custom Software Development, Web Development

Read Also

How to Use Redis for Caching in PHP: Cutting Response Times from 800ms to 40ms (2015)aunimeda
Backend Engineering

How to Use Redis for Caching in PHP: Cutting Response Times from 800ms to 40ms (2015)

Redis caching reduced our PHP app's average response time from 800ms to 40ms on product listing pages. The pattern: cache database query results with TTL, invalidate on write. Here's the exact implementation with cache key strategy, stampede prevention, and cache warming.

How to Build a REST API with Laravel 5.0 (2015)aunimeda
Backend Engineering

How to Build a REST API with Laravel 5.0 (2015)

Laravel 5.0 launched in February 2015 with a redesigned folder structure and form requests. It became the fastest way to build a REST API in PHP. Here's the full pattern: routes, controllers, Eloquent serialization, authentication middleware, and consistent error responses.

Building a Proper REST API in PHP: Lessons From Our First Mobile Backendaunimeda
Backend Engineering

Building a Proper REST API in PHP: Lessons From Our First Mobile Backend

When we built our first iOS app in 2013, we designed the backend API by feel. Here's what we got wrong, what we fixed, and the principles that still hold up today.

Need IT development for your business?

We build websites, mobile apps and AI solutions. Free consultation.

Get Consultation All articles