Introduction
WordPress custom post types transform a standard blog platform into a full-fledged content management system. Whether you're building a portfolio site, an e-commerce store, or a membership platform, understanding how to properly register custom post types and configure their permalink structures is fundamental to WordPress development.
This guide covers the register_post_type() function in depth, from basic registration to advanced permalink customization with modern best practices.
What you'll learn:
- The complete
register_post_type()function signature and parameters - How to configure labels for a localized admin interface
- Core arguments that control post type behavior and visibility
- Custom permalink structures using the rewrite parameter
- Production-ready code examples with best practices
- Template file creation and display considerations
Understanding WordPress Custom Post Types
Custom post types extend WordPress beyond the default posts and pages, allowing developers to create distinct content management systems for any use case.
What Are Custom Post Types?
A custom post type is a content type that you define yourself, with its own set of fields, admin interface, and display templates. While WordPress comes with posts, pages, attachments, and other built-in types, custom post types let you model virtually any content structure.
Common use cases for custom post types:
- Products: E-commerce sites using WooCommerce or custom solutions
- Portfolio items: Creative agencies showcasing work
- Testimonials: Customer quotes and reviews
- Events: Calendars, bookings, and scheduling systems
- Case studies: Detailed project breakdowns
- Team members: Staff directories and profiles
- FAQ items: Structured question-and-answer content
How WordPress Stores Custom Post Types
All post types, including custom ones, are stored in the wp_posts database table with a post_type field that identifies the content type. This unified storage means WordPress can query and display any post type using the same core functions, while still maintaining separation through the type identifier.
When to Use Custom Post Types
Before creating a custom post type, consider whether your needs can be met with existing WordPress features:
Create a custom post type when you need:
- A dedicated admin interface for content managers
- Separate archive pages with unique layouts and templates
- Custom meta boxes and fields specific to the content type
- Distinct URL structures for SEO purposes
- Different capability/permission controls for editors
- Integration with Gutenberg blocks and the REST API
Consider using categories/tags instead when:
- Content is fundamentally similar to blog posts
- You only need to organize content within existing types
- The administrative overhead isn't justified for the content volume
Built-in Post Types Overview
Understanding WordPress's default post types helps clarify where custom types fit:
| Post Type | Purpose | Key Characteristics |
|---|---|---|
| post | Blog content | Categories, tags, archives |
| page | Static content | Hierarchical, no categories |
| attachment | Media files | Unique URLs for each file |
| revision | Content history | Auto-saved versions |
| nav_menu_item | Navigation menus | Menu structure data |
The register_post_type() Function
The register_post_type() function is the core API for creating custom post types in WordPress. Understanding its signature and behavior is essential for any WordPress developer.
Function Signature
register_post_type( string $post_type, array $args = array() ): WP_Post_Type|WP_Error
Parameters:
$post_type(string): Maximum 20 characters, no spaces or capital letters$args(array): Configuration array defining the post type's behavior
Returns:
WP_Post_Typeobject on successWP_Errorobject if registration fails
Minimal Registration Example
Here's the simplest working custom post type registration:
function register_book_post_type() {
register_post_type(
'book',
array(
'labels' => array(
'name' => __('Books'),
'singular_name' => __('Book')
),
'public' => true,
'has_archive' => true,
'supports' => array('title', 'editor', 'thumbnail')
)
);
}
add_action('init', 'register_book_post_type');
This example, based on the Kinsta WordPress Custom Post Types Guide, shows the essential components needed for a functional custom post type.
When to Register Post Types
Always hook your registration into the init action with a priority that ensures WordPress core is fully loaded:
// Standard registration on init
add_action('init', 'register_my_post_type');
// For theme-dependent post types, use after_setup_theme
add_action('after_setup_theme', 'register_theme_post_types');
Best Practices for Registration
- Register in a plugin, not a theme -- Post types persist across theme changes
- Prefix your post type name -- Prevents conflicts (myplugin_portfolio, not portfolio)
- Use lowercase only -- WordPress converts internally, but consistency matters
- Keep names under 20 characters -- Prevents database issues with indexes
- Test early and often -- URL structures require flushing after changes
Configuring Labels for the Admin Interface
The labels array defines every text string that appears in the WordPress admin interface for your custom post type. Proper label configuration ensures a polished, localized experience for content managers.
Why Labels Matter
Labels affect:
- Menu names in the admin sidebar
- Button text on listing and editing screens
- Empty state messages and help text
- Search and filter labels
- Bulk action labels
Complete Labels Reference
$labels = array(
'name' => _x('Books', 'Post type general name', 'textdomain'),
'singular_name' => _x('Book', 'Post type singular name', 'textdomain'),
'menu_name' => _x('Books', 'Admin Menu text', 'textdomain'),
'name_admin_bar' => _x('Book', 'Add New on Toolbar', 'textdomain'),
'add_new' => __('Add New', 'textdomain'),
'add_new_item' => __('Add New Book', 'textdomain'),
'new_item' => __('New Book', 'textdomain'),
'edit_item' => __('Edit Book', 'textdomain'),
'view_item' => __('View Book', 'textdomain'),
'all_items' => __('All Books', 'textdomain'),
'search_items' => __('Search Books', 'textdomain'),
'parent_item_colon' => __('Parent Books:', 'textdomain'),
'not_found' => __('No books found.', 'textdomain'),
'not_found_in_trash' => __('No books found in Trash.', 'textdomain'),
'featured_image' => _x('Book Cover Image', 'Overrides the "Featured Image" phrase', 'textdomain'),
'set_featured_image' => __('Set cover image', 'textdomain'),
'remove_featured_image' => __('Remove cover image', 'textdomain'),
'use_featured_image' => __('Use as cover image', 'textdomain'),
'archives' => __('Book archives', 'textdomain'),
'insert_into_item' => __('Insert into book', 'textdomain'),
'uploaded_to_this_item' => __('Uploaded to this book', 'textdomain'),
'filter_items_list' => __('Filter books list', 'textdomain'),
'items_list_navigation' => __('Books list navigation', 'textdomain'),
'items_list' => __('Books list', 'textdomain'),
);
This comprehensive labels array, as documented in the Kinsta WordPress Custom Post Types Guide, covers every text string that appears in the WordPress admin.
Translation Functions Explained
__()-- Simple translation_x()-- Translation with context (useful when same word has different meanings)_n()-- Singular/plural translation_ex()-- Translation with context for echo
Recommended Labels
Include at minimum: name, singular_name, add_new, add_new_item, edit_item, view_item, all_items, search_items, not_found, not_found_in_trash.
Core Arguments Configuration
The arguments array controls every aspect of how your custom post type behaves. These arguments determine visibility, capabilities, URL structure, and feature support.
Public Visibility Controls
'public' => true, // Controls default visibility for most features
'publicly_queryable' => true, // Allow front-end queries via URL parameters
'show_ui' => true, // Display admin interface in wp-admin
'show_in_nav_menus' => true, // Allow inclusion in navigation menus
'show_in_admin_bar' => true, // Show in the WordPress admin toolbar
'show_in_rest' => true, // Enable Gutenberg/Block Editor support
As documented in the WordPress.org register_post_type() reference, these visibility controls determine how your post type behaves across the site.
How public settings interact:
| Setting | Effect when true | Effect when false |
|---|---|---|
| public | Makes post type visible on front-end | Hides from front-end queries |
| publicly_queryable | URLs like ?book=sample work | Queries return no results |
| show_ui | Adds admin menu item | No admin interface |
| show_in_rest | Enables REST API at /wp-json/wp/v2/book | No REST endpoints |
Hierarchical vs Non-Hierarchical
'hierarchical' => false, // Default: linear content like blog posts
'hierarchical' => true, // Page-like: parent-child relationships
Hierarchical post types (like pages):
- Support parent-child relationships
- Use page templates via template hierarchy
- Include menu order field
- URL structure can reflect hierarchy
Non-hierarchical post types (like posts):
- Linear content structure
- No parent assignment
- Categories and tags for organization
- Standard single and archive templates
Capability Configuration
'capability_type' => 'post', // Uses 'post' capabilities by default
// Or define custom capabilities:
'capability_type' => 'book',
'capabilities' => array(
'edit_post' => 'edit_book',
'read_post' => 'read_book',
'delete_post' => 'delete_book',
'edit_posts' => 'edit_books',
'edit_others_posts' => 'edit_others_books',
'publish_posts' => 'publish_books',
'read_private_posts' => 'read_private_books',
),
'map_meta_cap' => true, // Use WordPress's meta capability system
Menu Configuration
'menu_position' => 20, // Position in admin menu (integer)
'menu_icon' => 'dashicons-book', // Dashicon class or custom SVG URL
Menu position values:
- 5-9: Below Posts
- 10-14: Below Media
- 15-19: Below Links
- 20-24: Below Pages
- 25-59: Below first separator
- 60-64: Below Comments
- 65-69: Below second separator
- 70-74: Below Plugins
- 75-79: Below Users
- 80-84: Below Tools
- 85-99: Below Settings
Query Variables
'query_var' => 'book', // Enable with custom query var
'query_var' => true, // Enable with post type name as query var
'query_var' => false, // Disable query var entirely
When enabled, query_var allows front-end queries like ?book=sample-book.
Supporting Features and Functionality
The supports array determines which core WordPress features are available for your custom post type. Each feature adds functionality but also increases database queries and memory usage.
Available Supports
'supports' => array(
'title', // Title input field (REQUIRED for most uses)
'editor', // Content editor (Gutenberg blocks supported)
'author', // Author selection dropdown
'thumbnail', // Featured image support
'excerpt', // Short description field
'trackbacks', // Ping/trackback support
'custom-fields', // Built-in custom fields meta box
'comments', // Comment support
'revisions', // Revision history (auto-save support)
'page-attributes', // Parent selection and menu order (hierarchical only)
'post-formats', // Post format support (aside, gallery, etc.)
)
As recommended in the Kinsta WordPress Custom Post Types Guide, only include features you actually need.
Recommended Feature Sets
Minimal (fastest, lightest):
'supports' => array('title', 'editor'),
Standard content:
'supports' => array('title', 'editor', 'thumbnail', 'excerpt', 'revisions'),
Full-featured:
'supports' => array('title', 'editor', 'thumbnail', 'excerpt', 'author', 'revisions', 'comments'),
Custom Fields vs Meta Boxes
The built-in 'custom-fields' support adds a generic meta box that shows all meta keys. For production sites, consider:
- ACF (Advanced Custom Fields) -- Visual field builder
- Meta Box -- Lightweight alternative with extensive options
- Custom meta boxes -- Build your own with the Settings API
Custom meta boxes provide:
- Better data validation
- Conditional field display
- User-friendly interfaces
- Structured data storage
Custom Taxonomies Integration
Taxonomies provide organizational structure for your custom post type. WordPress supports both existing taxonomies (categories, tags) and custom taxonomies you define.
Associating Existing Taxonomies
Add standard WordPress taxonomies to your custom post type:
'taxonomies' => array('category', 'post_tag'),
This adds categories and tags to your custom post type. Posts using categories won't share those categories with books--they remain separate.
Registering Custom Taxonomies
// Register genre taxonomy for books
register_taxonomy(
'genre',
'book',
array(
'labels' => array(
'name' => __('Genres'),
'singular_name' => __('Genre'),
),
'hierarchical' => true, // Like categories (false = like tags)
'public' => true,
'show_in_rest' => true, // Enable REST API and Gutenberg
'rewrite' => array(
'slug' => 'genre',
'with_front' => false,
),
)
);
Following patterns from the Kinsta WordPress Custom Post Types Guide, this creates a hierarchical taxonomy for organizing books.
Hierarchical vs Non-Hierarchical Taxonomies
Hierarchical (like categories):
- Supports parent-child relationships
- Checkbox interface in admin
- Creates directory-style URLs: /genre/fiction/
Non-hierarchical (like tags):
- Flat structure
- Tag input field in admin
- Creates tag-style URLs: /genre/science-fiction
Benefits of Custom Taxonomies
- Dedicated archive pages for filtering (e.g., /genre/fiction/)
- Better SEO through topic authority
- Faceted navigation and filtering
- Structured content organization
Custom Permalinks: The rewrite Parameter
The rewrite parameter controls your custom post type's URL structure. This is crucial for SEO, user experience, and maintaining clean, logical URLs.
Basic Rewrite Configuration
'rewrite' => array(
'slug' => 'books', // Base URL segment
'with_front' => true, // Prepend front base (/blog/)
'pages' => true, // Enable pagination
'feeds' => true, // Enable feed generation
'ep_mask' => EP_PERMALINK, // Endpoint mask
),
As documented in the CoolPlugins Custom Permalinks Guide, these settings determine your URL structure.
Understanding with_front
The with_front option determines whether your post type respects the front base from Settings > Permalinks.
with_front => true:
- If your site uses /blog/ prefix, URLs become /blog/books/sample-book/
- Maintains consistency with posts and pages
- Good for content that should feel "integrated"
with_front => false:
- URLs are cleaner: /books/sample-book/
- Recommended for most custom post types
- Gives distinct identity to your content type
URL Structure Examples
Pattern 1: Simple slug-based
'rewrite' => array('slug' => 'books'),
// URLs: /books/sample-book/
// Archive: /books/
Pattern 2: With front base
'rewrite' => array('slug' => 'books', 'with_front' => true),
// URLs: /blog/books/sample-book/
Pattern 3: No front base (recommended)
'rewrite' => array('slug' => 'books', 'with_front' => false),
// URLs: /books/sample-book/
Pattern 4: Hierarchical-style URLs
'rewrite' => array('slug' => 'library/books'),
// URLs: /library/books/sample-book/
Different Slug for Archive vs Single
'rewrite' => array(
'slug' => 'book', // Singular for single entries
'with_front' => false,
),
'has_archive' => 'books', // Different archive slug
This gives you:
- Single entries: /book/sample-book/
- Archive: /books/
Flushing Rewrite Rules
Critical: After changing rewrite settings, you MUST flush rewrite rules or single entry pages will return 404 errors.
Method 1: Via WordPress Admin (Simplest)
- Go to Settings > Permalinks
- Click "Save Changes"
- No code required
Method 2: Programmatic Flush
// Expensive operation - use sparingly
flush_rewrite_rules();
Method 3: On Plugin Activation (Recommended)
function myplugin_flush_rewrite_rules() {
register_my_post_type();
flush_rewrite_rules();
}
register_activation_hook(__FILE__, 'myplugin_flush_rewrite_rules');
register_deactivation_hook(__FILE__, 'flush_rewrite_rules');
As noted in the Kinsta WordPress Custom Post Types Guide, flush_rewrite_rules() is an expensive operation that regenerates .htaccess rules. Call it only on activation/deactivation, not on every page load.
Common Permalink Issues and Fixes
Problem: 404 errors on custom post type pages Solution: Flush rewrite rules
Problem: URL conflicts with existing pages Solution: Change the rewrite slug to something unique
Problem: with_front not working as expected Solution: Check your site's permalink structure (Settings > Permalinks)
Advanced Configuration Options
For production environments and complex use cases, these advanced options provide finer control over your custom post type.
REST API Configuration
'show_in_rest' => true, // Enable Gutenberg and REST API
'rest_base' => 'books', // Custom REST API base URL
'rest_controller_class' => 'WP_REST_Posts_Controller', // Custom controller
'rest_namespace' => 'myplugin/v1', // Custom namespace
According to the WordPress.org register_post_type() documentation, REST API integration enables modern WordPress features.
Benefits of REST API integration:
- Gutenberg block editor support
- Headless WordPress configurations
- Mobile app and JavaScript framework integrations
- Custom API endpoints for external applications
Archive Configuration
'has_archive' => true, // Enable archive page
'has_archive' => 'books', // Custom archive slug
'archive' => array(
'wp_html_wrapper' => 'book-wrapper',
'class' => 'book-archive',
),
The archive slug can differ from the rewrite slug, giving you flexibility in URL structure.
Exclusion from Searches
'exclude_from_search' => false, // Include in search results (default for public)
Set to true for internal-only content that shouldn't appear in site searches.
Comment Status
'comments' => true, // Enable comments (requires 'supports' => 'comments')
Note: Comments must also be in the supports array.
Complete Production Example
$args = array(
'labels' => $labels,
'public' => true,
'publicly_queryable' => true,
'show_ui' => true,
'show_in_menu' => true,
'query_var' => true,
'rewrite' => array('slug' => 'books', 'with_front' => false),
'capability_type' => 'post',
'has_archive' => true,
'hierarchical' => false,
'menu_position' => 20,
'menu_icon' => 'dashicons-book',
'supports' => array('title', 'editor', 'thumbnail', 'excerpt', 'revisions'),
'show_in_rest' => true,
'rest_base' => 'books',
);
This production-ready configuration, based on best practices from the Kinsta WordPress Custom Post Types Guide, covers all essential settings for a modern custom post type.
Complete Registration Example
Here's a production-ready plugin that registers a 'book' custom post type with genre taxonomy, complete labels, proper permalinks, and activation hooks.
<?php
/**
* Plugin Name: Custom Book Post Type
* Description: Registers a 'book' custom post type for managing book reviews
* Version: 1.0.0
* Author: Developer Name
* Textdomain: my-book-plugin
*/
if (!defined('ABSPATH')) {
exit;
}
function mbpt_register_book_post_type() {
$labels = array(
'name' => _x('Books', 'Post type general name', 'my-book-plugin'),
'singular_name' => _x('Book', 'Post type singular name', 'my-book-plugin'),
'menu_name' => _x('Books', 'Admin Menu text', 'my-book-plugin'),
'name_admin_bar' => _x('Book', 'Add New on Toolbar', 'my-book-plugin'),
'add_new' => __('Add New', 'my-book-plugin'),
'add_new_item' => __('Add New Book', 'my-book-plugin'),
'new_item' => __('New Book', 'my-book-plugin'),
'edit_item' => __('Edit Book', 'my-book-plugin'),
'view_item' => __('View Book', 'my-book-plugin'),
'all_items' => __('All Books', 'my-book-plugin'),
'search_items' => __('Search Books', 'my-book-plugin'),
'parent_item_colon' => __('Parent Books:', 'my-book-plugin'),
'not_found' => __('No books found.', 'my-book-plugin'),
'not_found_in_trash' => __('No books found in Trash.', 'my-book-plugin'),
'featured_image' => _x('Book Cover Image', 'Overrides the "Featured Image" phrase', 'my-book-plugin'),
'set_featured_image' => __('Set cover image', 'my-book-plugin'),
'remove_featured_image' => __('Remove cover image', 'my-book-plugin'),
'use_featured_image' => __('Use as cover image', 'my-book-plugin'),
'archives' => __('Book archives', 'my-book-plugin'),
'insert_into_item' => __('Insert into book', 'my-book-plugin'),
'uploaded_to_this_item' => __('Uploaded to this book', 'my-book-plugin'),
'filter_items_list' => __('Filter books list', 'my-book-plugin'),
'items_list_navigation' => __('Books list navigation', 'my-book-plugin'),
'items_list' => __('Books list', 'my-book-plugin'),
);
$args = array(
'labels' => $labels,
'public' => true,
'publicly_queryable' => true,
'show_ui' => true,
'show_in_menu' => true,
'query_var' => true,
'rewrite' => array('slug' => 'books', 'with_front' => false),
'capability_type' => 'post',
'has_archive' => true,
'hierarchical' => false,
'menu_position' => 20,
'menu_icon' => 'dashicons-book',
'supports' => array('title', 'editor', 'thumbnail', 'excerpt', 'revisions'),
'show_in_rest' => true,
'rest_base' => 'books',
);
register_post_type('book', $args);
// Register genre taxonomy
register_taxonomy(
'genre',
'book',
array(
'labels' => array(
'name' => __('Genres', 'my-book-plugin'),
'singular_name' => __('Genre', 'my-book-plugin'),
),
'hierarchical' => true,
'public' => true,
'show_in_rest' => true,
'rewrite' => array('slug' => 'genre', 'with_front' => false),
)
);
}
add_action('init', 'mbpt_register_book_post_type');
// Flush rewrite rules on activation
function mbpt_flush_rewrite_rules() {
mbpt_register_book_post_type();
flush_rewrite_rules();
}
register_activation_hook(__FILE__, 'mbpt_flush_rewrite_rules');
register_deactivation_hook(__FILE__, 'flush_rewrite_rules');
What This Example Includes
- Complete labels array for full admin UI localization
- Optimized supports (title, editor, thumbnail, excerpt, revisions)
- Clean permalinks with with_front => false
- REST API support for Gutenberg integration
- Custom taxonomy (genre) with hierarchical structure
- Activation hooks for automatic rewrite flushing
- Proper textdomain for internationalization
- Prefixing (mbpt_) to prevent conflicts
Template Files and Display
WordPress uses a template hierarchy to determine which file displays your custom post type content. Creating appropriate templates gives you full control over the presentation.
Template Hierarchy
Single post type (single-book.php):
- single-{post_type}-{slug}.php (for specific post, e.g., single-book-harry-potter.php)
- single-{post_type}.php (for all posts of this type)
- single.php
- singular.php
Archive (archive-book.php):
- archive-{post_type}.php (post type archive)
- archive.php
- index.php
Taxonomy archives:
- taxonomy-{taxonomy}-{term}.php (e.g., taxonomy-genre-fiction.php)
- taxonomy-{taxonomy}.php (e.g., taxonomy-genre.php)
- archive.php
- index.php
Single Template Example
<?php
// single-book.php
get_header();
while (have_posts()) :
the_post();
$author = get_post_meta(get_the_ID(), 'book_author', true);
$isbn = get_post_meta(get_the_ID(), 'book_isbn', true);
?>
<article <?php post_class('book-single'); ?>>
<header class="book-header">
<?php if (has_post_thumbnail()) : ?>
<div class="book-cover">
<?php the_post_thumbnail('medium_large', array('class' => 'book-thumbnail')); ?>
</div>
<?php endif; ?>
<h1 class="book-title"><?php the_title(); ?></h1>
<?php if ($author) : ?>
<p class="book-author">By <?php echo esc_html($author); ?></p>
<?php endif; ?>
</header>
<div class="book-content">
<?php the_content(); ?>
</div>
<?php if ($isbn) : ?>
<footer class="book-meta">
<p class="book-isbn">ISBN: <?php echo esc_html($isbn); ?></p>
</footer>
<?php endif; ?>
</article>
<?php
endwhile;
get_footer();
Archive Template Example
<?php
// archive-book.php
get_header();
?>
<div class="book-archive">
<header class="archive-header">
<h1 class="archive-title"><?php post_type_archive_title(); ?></h1>
</header>
<div class="book-grid">
<?php if (have_posts()) : ?>
<?php while (have_posts()) : the_post(); ?>
<article <?php post_class('book-card'); ?>>
<?php if (has_post_thumbnail()) : ?>
<div class="book-cover">
<a href="<?php the_permalink(); ?>">
<?php the_post_thumbnail('medium', array('class' => 'book-thumbnail')); ?>
</a>
</div>
<?php endif; ?>
<h2 class="book-title">
<a href="<?php the_permalink(); ?>"><?php the_title(); ?></a>
</h2>
<?php the_excerpt(); ?>
</article>
<?php endwhile; ?>
<?php else : ?>
<p class="no-books">No books found.</p>
<?php endif; ?>
</div>
<?php the_posts_pagination(); ?>
</div>
<?php
get_footer();
These templates demonstrate how to display custom post type content with proper WordPress conventions, including semantic HTML, accessible markup, and proper escaping for security.
Best Practices and Common Pitfalls
Following these guidelines ensures your custom post types are performant, maintainable, and conflict-free.
Best Practices Checklist
Code Organization
- Register custom post types in a plugin, not a theme functions.php
- Prefix all functions and post type names to prevent conflicts
- Use a separate file for post type registration (include it in main plugin file)
- Keep registration logic separate from template/display logic
Configuration
- Include all labels for a complete admin experience
- Use only necessary supports (reduces database queries)
- Set with_front => false for cleaner URLs
- Test permalinks immediately after registration
Performance
- Flush rewrite rules only on activation/deactivation, never on every load
- Use transients for expensive queries on archive pages
- Implement caching for frequently accessed data
- Consider pagination limits for archive queries
URL Structure
- Plan URL structure before launch
- Use plural form for archive slugs (books, not book)
- Keep slugs short and descriptive
- Consider SEO impact when choosing URL patterns
Common Pitfalls and Solutions
Problem: Post type not appearing in admin menu
Possible causes:
- Typo in post type name (spaces, capital letters, too long)
- show_ui or show_in_menu set to false
- PHP error preventing registration
Solution:
// Debug: Check if registration succeeded
add_action('admin_notices', function() {
$registered = get_post_type_object('book');
if (!$registered) {
echo '<div class="error"><p>Book post type not registered!</p></div>';
}
});
Problem: 404 errors on single pages
Cause: Rewrite rules not flushed after registration
Solution:
// Go to Settings > Permalinks and save
// Or programmatically:
flush_rewrite_rules();
Problem: Custom permalinks not working
Causes:
- with_front setting conflicts with site structure
- Rewrite slug conflicts with existing page
- Theme preventing proper routing
Solution:
- Check Settings > Permalinks structure
- Verify rewrite slug is unique
- Test with default theme to isolate issue
Problem: Performance degradation
Causes:
- Too many supports features enabled
- Unlimited revision history
- No pagination on archive pages
Solution:
// Limit revisions
'revisions_to_keep' => 5,
// Add pagination to archives
'posts_per_page' => 12,
These troubleshooting solutions, informed by the WordPress.org register_post_type() documentation and Kinsta's WordPress Custom Post Types Guide, cover the most common issues developers face.