Removing a custom post type from WordPress involves two separate decisions: stop registering the content type, and decide whether its existing records should be preserved, migrated or permanently deleted. Removing the registration code alone does not remove the content from the database.
Important: permanent deletion is destructive. Work on staging first, create a verified database backup and confirm the exact post type key before running any delete command.
Choose the outcome before touching the database
| Goal | Recommended action |
|---|---|
| Temporarily hide the post type | Disable its registration only; keep the data intact |
| Replace the plugin or theme | Migrate the content before disabling the old registration |
| Convert content to posts or pages | Change or migrate the post type with a tested mapping |
| Permanently remove everything | Delete records through WordPress APIs or WP-CLI, then remove registration |
If you are not certain, preserve the records. Hidden custom post type entries can usually be recovered by restoring the original registration, while deleted records require a backup.
Step 1: Find the exact custom post type key
The admin menu label is not necessarily the registered key. “Case Studies” might use case_study, portfolio or another value internally.
List registered post types with WP-CLI:
wp post-type list --fields=name,label,public
Then inspect the candidate type:
wp post-type get case_study
WordPress post type keys may contain lowercase letters, numbers, underscores and dashes and cannot exceed 20 characters. Confirm the key from the plugin or theme code if the type disappears when that component is disabled. See the official register_post_type() reference.
Step 2: Identify what depends on the content
Before removal, check more than the main records:
- public URLs and search-engine traffic;
- navigation links and internal links;
- templates and page-builder widgets;
- custom taxonomies;
- post metadata and custom fields;
- featured images and attached media;
- REST API, feeds and integrations;
- scheduled tasks or code that queries the post type.
If the URLs receive traffic or backlinks, prepare redirects to the closest useful replacements. Redirecting every deleted URL to the homepage is usually less helpful than mapping content individually.
Step 3: Export before deletion
Create a full database backup and, where practical, a content export for the affected records. With WP-CLI:
wp db export before-cpt-removal.sql
wp export --post_type=case_study --dir=/secure/export/path
Store the database export away from the live web root. Confirm that the file exists and that the person performing the work knows how it would be restored.
Step 4: Count and review the records
Use a list command as the dry run:
wp post list --post_type=case_study --post_status=any --fields=ID,post_title,post_status --format=table
Compare the count with the expected number. Include drafts, private records and trash so hidden data is not overlooked. For a large dataset, export the IDs to a file and review them before continuing.
Step 5: Migrate content when it still has value
If the records should become normal posts, pages or another custom type, perform a controlled migration rather than deleting and recreating them manually. Test:
- title, body and excerpt;
- author and publication status;
- custom fields;
- taxonomy mapping;
- featured images;
- permalinks and redirects;
- templates that display the new type.
A direct database update may change the post_type value, but it does not automatically fix templates, taxonomy registrations or URL expectations. Use a migration script or WP-CLI command that can be reviewed and rerun safely.
Step 6: Delete records through WordPress
When permanent removal is confirmed, retrieve the IDs first:
wp post list --post_type=case_study --post_status=any --format=ids
After validating the output on staging, delete those exact IDs:
wp post delete 101 102 103 --force
Using WordPress deletion functions is safer than deleting rows directly from wp_posts. The official wp_delete_post() documentation explains that permanent deletion also removes associated comments, post metadata and term relationships.
For a large batch, process IDs in manageable groups and record the output. Do not copy a command containing command substitution until you have separately reviewed the ID list.
Step 7: Treat taxonomies separately
Deleting posts removes their term relationships, but it does not necessarily mean that the terms themselves should be deleted. A taxonomy may be shared with another post type or contain terms the business wants to preserve.
List the taxonomy and usage first:
wp taxonomy list --object_type=case_study
wp term list project_type --fields=term_id,name,count
Delete terms only when the taxonomy is exclusive to the retired content type and the business has approved permanent removal. Custom plugin tables and options also need plugin-specific review; they are not automatically covered by deleting posts.
Step 8: Remove or unregister the post type
If your own plugin or theme registers the type, remove that registration code after the content decision is complete. If another component registers it and must remain active, WordPress provides unregister_post_type() for non-built-in types:
add_action( 'init', function () {
if ( post_type_exists( 'case_study' ) ) {
unregister_post_type( 'case_study' );
}
}, 100 );
This stops the type from being registered for the current request. It does not delete its database records. When possible, remove the original registration instead of maintaining a permanent override.
Step 9: Refresh rewrite rules once
After removing the registration, refresh rewrite rules:
wp rewrite flush
Do not call flush_rewrite_rules() on every page request. WordPress documents it as an expensive operation that should run only when necessary. See the official rewrite-rule documentation.
Step 10: Verify the site after removal
- The retired admin menu is gone.
- Old archive and single URLs behave as planned.
- Redirects point to useful replacements.
- Navigation, search and XML sitemaps contain no obsolete URLs.
- No template or API query expects the removed type.
- Scheduled jobs and integrations complete without errors.
- Database backups remain available until the change is accepted.
Why direct SQL is usually the wrong first choice
A statement that deletes rows from wp_posts does not by itself express the full WordPress cleanup process. It can leave related data, bypass hooks used by plugins and make it harder to audit exactly what happened. Generic “delete orphan metadata” queries may also remove unrelated data elsewhere on the site.
Use SQL only when the scale or corruption genuinely requires it, with a site-specific query reviewed by someone who understands the schema and plugin dependencies.
Treat removal as change-controlled work
On a business website, a custom post type may feed navigation, search, reporting, APIs or campaign landing pages. Name an approver, record the affected templates and integrations, and define the rollback point before deleting records or unregistering code.
When several client sites share the same theme or plugin, test a representative staging site before repeating the change. A website support SLA can define approval and escalation, while portfolio maintenance gives the cleanup a consistent owner.
Safe removal checklist
- Confirm the post type key.
- Choose preserve, migrate or delete.
- Audit URLs, templates, taxonomies and integrations.
- Create and verify backups.
- Review the complete record list on staging.
- Migrate valuable content.
- Delete approved IDs through WordPress.
- Review exclusive taxonomies and custom tables separately.
- Remove registration and flush rewrites once.
- Test URLs, navigation, search, sitemap and integrations.
Need help with a risky WordPress cleanup?
Database cleanup is a poor place for trial and error. CodaStudio provides WordPress support services for troubleshooting and carefully scoped changes, backed by ongoing WordPress maintenance.
