If you’ve worked with WordPress for a while, you’ve probably heard this sentence more than once:

“Everything is stored in the database.”

That’s true. But what’s usually missing is a clear explanation of how WordPress stores things and why it does it this way.

Posts, pages, users, products, courses, orders — they all look different in the dashboard. But in the database, WordPress treats many of them in surprisingly similar ways.

Once you understand this structure, a lot of things suddenly make sense:

  • Why some queries are slow
  • Why wp_postmeta becomes huge
  • Why developers complain about meta tables
  • Why taxonomies are more powerful than they look

Let’s break it down step by step, in plain language.

How WordPress Thinks About Content

WordPress has a simple philosophy:

Almost everything is a “post”.

That sounds strange at first, but it’s the foundation of the database design.

In WordPress:

  • A blog post is a post
  • A page is a post
  • An attachment is a post
  • A menu item is a post
  • A WooCommerce product is a post
  • A Tutor LMS course is a post
  • An order is a post

They are all stored in the same main table, called wp_posts.

The difference between them is not the table.
The difference is the post type.

The wp_posts Table: The Core of WordPress

This is the most important table in WordPress.

Every row in wp_posts represents one piece of content.

Some important columns you should know:

  • ID
    This is the unique ID of the post. Everything else in WordPress connects to this number.
  • post_type
    This tells WordPress what kind of post it is.
    Examples: post, page, product, attachment, shop_order, tutor_course
  • post_status
    Draft, publish, private, trash, etc.
  • post_title
    The title you see in the dashboard.
  • post_content
    The main content. For block editor, this includes block markup.
  • post_author
    The user ID of the author.
  • post_date
    When it was created.

So when you create a page, WordPress does not create a new table.
It simply inserts a new row into wp_posts with post_type = page.

This design keeps WordPress flexible, but it also creates challenges later.

Why WordPress Uses One Table for Many Things

This design comes from WordPress’s early days. The goal was:

  • Keep things simple
  • Avoid too many tables
  • Make querying content easy

And it works well for basic sites.

But as WordPress evolved into an eCommerce, LMS, and membership platform, the same structure started carrying a lot more weight.

That’s where meta tables come in.

The wp_postmeta Table: Extra Data Storage

The wp_posts table stores only the basic information.

But what about:

  • Custom fields
  • Product prices
  • Course duration
  • Order totals
  • Featured images
  • SEO data

All of this goes into wp_postmeta.

This table works like a key-value store.

Each row has:

  • post_id – links back to wp_posts.ID
  • meta_key – the name of the data
  • meta_value – the value

Example:

  • post_id: 123
  • meta_key: _price
  • meta_value: 49.99

One post can have dozens or even hundreds of meta rows.

This is powerful because you can add unlimited data without changing the database structure.

But it comes at a cost.

Why wp_postmeta Gets So Big

Imagine a WooCommerce store:

  • Each product has price, sale price, stock, SKU, images
  • Each order has totals, taxes, addresses, payment data
  • Each line item adds more meta

All of that goes into wp_postmeta.

For large sites, this table becomes massive.

And here’s the important part:

Meta queries are slow.

WordPress stores meta_value as long text, not as typed columns like integers or decimals.
So the database cannot optimize these queries very well.

This is why experienced developers try to:

  • Reduce unnecessary meta
  • Avoid complex meta queries
  • Use custom tables for large datasets

Relationship Between Posts and Meta

The relationship is simple but critical:

  • wp_posts.IDwp_postmeta.post_id

Everything depends on that connection.

If a post is deleted, its meta should be deleted too.
If meta is orphaned, it stays in the database and causes bloat.

This is also why broken plugins can damage performance.
They add meta but never clean it up.

Taxonomies: Structured Classification

Now let’s talk about taxonomies.

Taxonomies are WordPress’s way of organizing content.

The most common ones:

  • Categories
  • Tags

But WordPress allows unlimited custom taxonomies:

  • Product categories
  • Course levels
  • Locations
  • Difficulty types

Taxonomies are not stored in wp_posts.

They use their own set of tables.

The Taxonomy Tables Explained

There are three main tables involved:

1. wp_terms

This table stores the actual term names.

Example:

  • ID: 7
  • name: Beginner
  • slug: beginner

This table does not know what the term is used for.

2. wp_term_taxonomy

This table gives meaning to the term.

It connects:

  • Term ID
  • Taxonomy type

Example:

  • term_id: 7
  • taxonomy: course_level

The same term name can be used in different taxonomies.

3. wp_term_relationships

This table connects posts to terms.

Example:

  • object_id: 123 (post ID)
  • term_taxonomy_id: 15

This is how WordPress knows:
“This post belongs to this category or taxonomy term.”

Why Taxonomies Are Better Than Meta for Filtering

This is an important concept.

Taxonomies are indexed and relational.
Meta is not.

If you need to:

  • Filter posts
  • Count posts
  • Build archives
  • Create URLs like /category/design/

Taxonomies are the right tool.

Meta works better for:

  • Settings
  • Flags
  • One-off values
  • Non-searchable data

Many performance problems happen because developers misuse meta instead of taxonomies.

Real Example: Courses in an LMS

Let’s say you’re building an LMS.

A course might have:

  • Level: Beginner, Intermediate, Advanced
  • Duration: 6 hours
  • Price: 50 USD
  • Instructor: User ID

Best approach:

  • Level → taxonomy
  • Duration → post meta
  • Price → post meta
  • Instructor → post_author or user meta

Why?
Because users will filter courses by level, but not by exact duration.

Understanding this distinction makes your site faster and cleaner.

Users and User Meta (Quick Overview)

Users are stored in:

  • wp_users

Extra user data is stored in:

  • wp_usermeta

The structure is similar to posts and post meta.

This same pattern repeats across WordPress:

  • Core table for main data
  • Meta table for extra data

Why This Database Design Still Exists

People often ask:
“Why doesn’t WordPress change this?”

The answer is compatibility.

Millions of sites depend on this structure.
Thousands of plugins assume this design.

Changing it would break the ecosystem.

Instead, WordPress slowly adds improvements:

  • Better caching
  • REST API
  • Block editor
  • Performance updates

But the core database philosophy stays the same.

Common Mistakes Developers Make

Here are a few real-world mistakes:

  • Storing filterable data in post meta instead of taxonomies
  • Running meta queries inside loops
  • Not cleaning up unused meta
  • Treating WordPress like a custom framework instead of respecting its design
  • Ignoring database size until the site becomes slow

These mistakes don’t show up on small sites.
They show up when traffic grows.

When You Should Use Custom Tables

Sometimes the WordPress schema is not enough.

You should consider custom tables when:

  • Data volume is very large
  • Data is highly relational
  • You need fast reports
  • Meta queries become unmanageable

WooCommerce itself moved order data to custom tables for this reason.

This doesn’t mean WordPress is bad.
It means WordPress is flexible enough to evolve.

Final Thoughts

Once you understand how posts, meta, and taxonomies work together, WordPress stops feeling mysterious.

You stop guessing.
You start designing properly.
You make better technical decisions.

The WordPress database is not perfect, but it is consistent.
And consistency is powerful when you learn how to use it correctly.

If you master this foundation, everything else — plugins, performance, scaling — becomes much easier.

If you want, next I can:

  • Add real SQL examples
  • Show diagrams in words
  • Explain WooCommerce-specific tables
  • Rewrite this as a tutorial series

Just tell me.