Short answer: a taxonomy in WordPress is a way to group content. The two you already use are Categories and Tags, both of which attach to blog Posts. WordPress also ships with three you rarely see (
post_format
,
nav_menu
,
link_category
) and lets you create your own with
register_taxonomy()
– for example, “Project type” on a
portfolio
post type or “Cuisine” on a
recipe
post type. Each taxonomy is either hierarchical (parent/child like Categories) or flat (no hierarchy like Tags). Each gets its own archive page, its own admin screen, and its own permalink base.
The rest of this guide explains what a taxonomy actually is inside WordPress, the five that ship by default, how to create a custom one, hierarchical vs non-hierarchical, the database model behind it all, and how taxonomies affect SEO.
What a taxonomy is (and isn't)#
A taxonomy is just a system for classifying things. In WordPress it has three pieces:
- The taxonomy itself – the label for the classification system. Category and Tag are taxonomies.
- The terms – the actual values inside the taxonomy. Inside the Category taxonomy you might have terms like Tutorials, News, Reviews. Inside the Tag taxonomy you might have WordPress, Performance, Security.
- The relationships – which posts are attached to which terms.
The taxonomy is the rule. The terms are the values. The relationships are the data. WordPress stores all three in separate database tables so a single term can attach to many posts and a single post can attach to many terms.
What a taxonomy is not: it is not a custom field. Custom fields (post meta) attach a key/value pair to a single post. A taxonomy is shared – a term lives independently of any post, can be reused on as many posts as you want, gets its own URL, and gets its own admin screen.
The five built-in taxonomies#
WordPress registers five taxonomies out of the box. You see two of them every day. The other three exist quietly.
| Taxonomy | Key | Attached to | Hierarchical? | Visible in admin? |
|---|---|---|---|---|
| Category |
category
| Posts | Yes | Yes (Posts > Categories) |
| Tag |
post_tag
| Posts | No | Yes (Posts > Tags) |
| Post Format |
post_format
| Posts | No | Sometimes (theme-dependent) |
| Navigation Menu |
nav_menu
| Menu items | No | No (hidden behind the Menus screen) |
| Link Category |
link_category
| Blogroll links | No | No (links feature retired) |
Category is the classic hierarchical taxonomy. Parent and child terms. A post can be in multiple categories. Every post must be in at least one (WordPress assigns “Uncategorized” if you do not pick one).
Tag is the classic flat taxonomy. No parent/child. A post can have any number of tags, including zero.
Post Format is the taxonomy nobody asked about. It lets a post be marked as Aside, Gallery, Link, Image, Quote, Status, Video, Audio, or Chat, and themes can render each one differently. Most modern themes ignore it.
Navigation Menu and Link Category are taxonomies in name only. WordPress uses the taxonomy infrastructure to store menu items and blogroll links, but you never see them as taxonomies in the admin. They are an implementation detail.
The two you actually manage editorially are Category and Tag. Tags and categories for SEO covers when to use each and how to keep them from hurting search rankings.
Hierarchical vs non-hierarchical#
The single most important property of a taxonomy is whether it is hierarchical.
Hierarchical taxonomies allow parent/child relationships. Categories is hierarchical, so you can have a Tutorials category with Beginner and Advanced children. The admin UI shows a tree of checkboxes when you create or edit a term, and the term archive at
/category/tutorials/beginner/
reflects the hierarchy in the URL.
Non-hierarchical (flat) taxonomies have no nesting. Tags is flat. Every tag is at the same level, and the admin UI for adding tags to a post is a text input with autocomplete instead of a checkbox tree.
Two practical consequences:
- Editorial UI. Hierarchical taxonomies surface checkboxes in the editor sidebar so writers pick from a known list. Flat taxonomies use free-text input, which is faster but also how you end up with seventeen typo variants of the same tag.
- Permalink shape. Hierarchical taxonomies can have multi-level URLs (
/category/parent/child/). Flat taxonomies cannot.
A useful heuristic: if the dimension has natural categories that contain sub-categories (Food > Italian > Pasta), it is hierarchical. If the dimension is a flat set of attributes that overlap with each other (vegan, gluten-free, quick), it is flat.
How to create a custom taxonomy#
If the two built-ins do not match what you need – for example, a
portfolio
custom post type that should be grouped by Client and Project type – you register a custom taxonomy.
With a plugin#
The easiest route is Custom Post Type UI. Install it, open CPT UI > Add/Edit Taxonomies, fill in:
- Taxonomy slug – the internal name (lowercase, no spaces). Used in URLs and in code.
- Plural label and singular label – what appears in the admin.
- Attach to post type(s) – which content types this taxonomy applies to.
- Hierarchical – yes for category-style, no for tag-style.
- Show in REST – yes if you want the block editor sidebar to show it.
Save, and the taxonomy is registered. The same plugin also creates custom post types, so if you are setting up
portfolio
and a custom taxonomy at the same time, it does both in one place. How to create custom post types in WordPress walks through the full plugin-vs-code decision for post types – the same logic applies to taxonomies.
With code#
The PHP version is a single
register_taxonomy()
call hooked to the
init
action:
add_action( 'init', 'register_project_type_taxonomy' );
function register_project_type_taxonomy() {
register_taxonomy(
'project_type',
'portfolio',
array(
'label' => 'Project types',
'hierarchical' => true,
'show_in_rest' => true,
'show_admin_column' => true,
'rewrite' => array( 'slug' => 'project-type' ),
)
);
}
Drop that in a small site-specific plugin in
wp-content/plugins/
, activate it, then visit Settings > Permalinks and click Save Changes once to flush the rewrite rules. Without that flush, the archive URL
/project-type/web-design/
returns a 404 even though everything else is wired up correctly.
The arguments worth knowing:
-
hierarchical– true for category-style, false for tag-style. Decides admin UI and URL shape. -
show_in_rest– true if you want the block editor to surface the taxonomy in its sidebar. Required for Gutenberg. -
show_admin_column– adds a column for this taxonomy to the post list screen. Convenient for editorial overview. -
rewrite– controls the URL slug. Default is the taxonomy key; override if you want the URL to differ from the internal name. -
public– set to false for taxonomies that should not have a front-end archive (rare but valid).
A custom taxonomy needs a post type to attach to. You can attach one taxonomy to multiple post types (
array( 'portfolio', 'case_study' )
) so the same term list applies to both.
How WordPress stores taxonomy data#
Under the hood, taxonomies live in four database tables:
| Table | What it stores |
|---|---|
wp_terms
| The actual term names and slugs (e.g. Tutorials, tutorials) |
wp_term_taxonomy
| Which taxonomy each term belongs to, plus the term description and the parent ID for hierarchical taxonomies |
wp_term_relationships
| The many-to-many link between posts and terms |
wp_termmeta
| Custom fields on individual terms (added in WP 4.4) |
A term in
wp_terms
can theoretically belong to multiple taxonomies (the same slug could be a category and a tag), but
wp_term_taxonomy
is what actually pins a term to one taxonomy. The split exists for historical reasons – WordPress used to store terms and taxonomies in a single row, and the schema kept the separation when taxonomies became proper first-class objects.
Why this matters: when you delete a term in the admin, WordPress removes the row from
wp_term_taxonomy
and clears the relationships in
wp_term_relationships
. The
wp_terms
row often stays behind because another taxonomy might be using the same term. Over years this builds up orphaned
wp_terms
rows. A database cleanup tool can prune them, but the cost is usually small enough to ignore.
Taxonomy archives and the template hierarchy#
Every public taxonomy term gets its own archive page automatically. The URL pattern is:
/[taxonomy_base]/[term-slug]/
For Categories, the default base is
category
, so the Tutorials category archives at
/category/tutorials/
. For Tags, the default base is
tag
. For a custom taxonomy
project_type
, the base defaults to
project-type
unless you override it with the
rewrite
argument.
WordPress picks which template to use for the archive in this order (the template hierarchy for taxonomies):
-
taxonomy-{taxonomy}-{term}.php– the most specific (e.g.taxonomy-project_type-web-design.php) -
taxonomy-{taxonomy}.php– all terms of a custom taxonomy -
taxonomy.php– any custom taxonomy -
category.phportag.phpfor the built-ins -
archive.php– any archive -
index.php– the fallback
Most themes only define
category.php
(sometimes) and
archive.php
. If you want a custom taxonomy to render with its own look, drop a
taxonomy-project_type.php
file into the theme.
The query that runs on a taxonomy archive returns all posts attached to that term, paginated. The template can override
posts_per_page
, change the order, or pull additional data with a custom query – but the core behavior (term -> list of attached posts) is the same for every taxonomy.
Real-world use cases#
A few patterns where a custom taxonomy is the right tool:
- Product attributes. An e-commerce site with a
productpost type adds taxonomies like Brand, Size, Color. Each one is filterable, each one gets a permalink archive (/brand/levis/), each one is independent from the post Categories the blog uses. WooCommerce does exactly this with its product attribute taxonomies. - Genres for a media site. A book review site with a
bookCPT registers a Genre taxonomy (hierarchical: Fiction > Science Fiction > Hard SF) and an Author taxonomy (flat). Readers land on/genre/hard-sf/or/author/le-guin/. Both URLs render the same template but list different posts. - Locations for a directory. A restaurant directory uses a
restaurantCPT and a hierarchical Location taxonomy (Continent > Country > City). Each city gets a landing page automatically. - Project types for an agency portfolio. The
portfolioexample from earlier. Project type sits separate from blog categories so the agency can talk about Web Design as a service in the blog and Web Design as a portfolio filter without the two taxonomies stepping on each other. - Editorial workflow. A multi-author site registers a
editorial_statusflat taxonomy with terms like needs-edit, ready-to-publish, evergreen. The terms never appear publicly (setpublic => false), but Editors filter the post list by status from the admin column. This is taxonomy used as metadata.
The pattern in all five: a dimension that needs to be searchable, filterable, or browsable as a group. If the value is unique per post and never reused, it is a custom field. If the value groups many posts together, it is a taxonomy.
Taxonomies and SEO#
Taxonomy archives are double-edged. They give search engines hub pages that aggregate every post on a topic. They also generate a lot of thin, near-duplicate pages if you let them run unchecked.
The three SEO levers worth knowing:
- Indexing. By default, every taxonomy archive is indexable. For Categories that is usually fine. For Tags it is often a mistake – a tag with one post is a duplicate of that post with extra navigation chrome around it. SEO plugins let you
noindextaxonomy archives globally or per-taxonomy. Tags and categories for SEO covers the exact toggle paths in Yoast, Rank Math, and SEOPress. - URL structure. The default
/category/tutorials/works, but many sites remove the/category/base for cleaner URLs. There is a trap: removing the base creates URL collisions with pages and post slugs. WordPress permalinks explains why the base exists and when removing it is safe. - Descriptions. Every taxonomy term has a description field, and most SEO plugins expose a separate meta description and title for the archive. Filling these in once turns a generic auto-generated archive into a page that can actually rank for the term name.
Custom taxonomies inherit the same trade-offs. If you register a Brand taxonomy and let WordPress generate one archive per brand, every page needs either real editorial content (a paragraph at the top of the archive) or a
noindex
so it does not dilute the rest of the site in search.
Common mistakes#
- Using a taxonomy where a custom field would be cleaner. Publication date, price, page count – these are unique values per post, not shared groupings. They belong in post meta (custom fields), not in a taxonomy.
- Forgetting the permalink flush. A freshly registered taxonomy returns 404 on its archive URLs until you visit Settings > Permalinks and click Save. The flush rewrites the rewrite rules; WordPress does not do it automatically when a taxonomy is registered.
- Setting
public => trueon an internal taxonomy. If a taxonomy is for editorial workflow (editorial_status,assignee), setpublicto false. Otherwise WordPress creates an archive page for each term and search engines start indexing/editorial-status/needs-edit/. - Attaching one taxonomy to too many post types. It is tempting to register a single Topic taxonomy and attach it to Posts, Pages, Portfolio, and Case Studies. The result is a term list cluttered with values that only make sense for one type. Per-type taxonomies are usually cleaner.
- Setting
hierarchical => trueon a tag-style taxonomy. Hierarchical taxonomies show as checkboxes in the editor. If editors are typing free-form values, checkboxes are wrong. Pick the right flag at registration time – changing it later means rebuilding the admin UI for that taxonomy. - Treating tags as keywords. The single most common WordPress taxonomy mistake. Tags are taxonomies, not metadata. They should group posts that share a real topic, not pad a post with keyword variations.
Common questions#
Can a post have terms from multiple taxonomies? Yes. A post can be in two categories, three tags, one Project type, and one Client all at once. The four taxonomies are independent.
Can I rename a taxonomy after creating it? You can rename the labels freely. You should not rename the taxonomy key (the first argument to
register_taxonomy()
) – it is referenced in URLs, in the database, and in any code that queries the taxonomy. If you really need to change it, you have to migrate the term relationships in the database and add redirects for the old archive URLs.
What happens if I deactivate a plugin that registered a custom taxonomy? The terms and relationships stay in the database, but the taxonomy is no longer registered, so the archive URLs 404 and the admin screen disappears. Reactivating the plugin restores everything. Deleting the plugin is the destructive step.
Can a taxonomy be hierarchical without being shown as a tree in the admin? No. The
hierarchical
flag controls both the data model (parent/child possible) and the editor UI (checkbox tree vs free-text). They are the same setting.
Do taxonomies count toward the WordPress object limit? There is no hard limit. Sites have hundreds of taxonomies and thousands of terms without issue. The practical limit is editorial – the more taxonomies you register, the more cluttered the editor sidebar becomes.
Summary#
A taxonomy is WordPress’s system for grouping content. The two you manage editorially – Categories and Tags – are just two instances of a generic infrastructure that also powers Post Formats, Navigation Menus, and any custom taxonomy you register with
register_taxonomy()
. Hierarchical taxonomies allow parent/child terms and render as a tree in the admin. Flat taxonomies are a single level with free-text input. Each taxonomy gets its own database storage, its own archive URL, its own template, and its own SEO surface. The work is mostly in knowing when a dimension is a taxonomy (shared, reused, browsable) and when it is a custom field (unique per post), and in being deliberate about which terms get indexed by search engines.