How to Mass Delete Custom Fields in WordPress

For whatever reason, you may have a ton of custom fields in your database, hogging up space, and now you want them gone.

Maybe you created them on purpose, but no longer want them. Maybe you accidentally installed some bunk ass theme or plugin that created a trillion of them. There are plugins and plenty of other posts telling you how to do this, but the only plugin I found is 3 years old (though it still works) and won’t show all custom fields if you have too many (it runs on a dropdown that apparently has a finite amount it can list.)

Other suggestions were to go into your database, and while this works, it’s a bit risky (not to mention difficult) if you don’t know what you’re doing. It’s also a pain in the patoot if you have hundreds or thousands of records you want to delete. So, I came up with the following.

Seriously, back up your database, especially if — but even if you don’t — alter this code in any way. You can destroy your entire website and I am in no way responsible for you doing something, even if you use my code exactly as displayed, that you’ll regret come five minutes from now (or any other timeline.)

I put this code into one of my page templates, then load a page with that template on it. For example, if you place this into your page.php file in your theme, and then load any page that uses the default Page Template, this code will run. The full code is listed here, with little comments in there, and then I’ll explain it piece by piece below.

Are you about to add this to your site and run it? Did you backup your database first? If not, Mr. T pities you, and like the A-Team always says, “Don’t blame Nathan, he flippin’ told you so!

<?php $the_current_meta_to_delete = $_GET['the_custom_meta_field']; // you can set this to whatever, but it assumes you'll have a URL like http://mysite.com/?the_custom_meta_field=NAME_OF_YOUR_META_FIELD
$args = array(
'posts_per_page' => -1, // this gets all posts, you may only want to get a few at a time
'post_type' => 'post', // this can be changed to any post type, or array('post', 'page', 'somecustomposttype')
'meta_key' => $the_current_meta_to_delete
);
$the_query = new WP_Query( $args );
if ($the_query->have_posts()) {
while ( $the_query->have_posts() ) :
$the_query->the_post();

$custom_fields = get_post_custom();

foreach ( $custom_fields as $field_key => $field_values ) {
foreach ( $field_values as $key => $value )
if ($field_key == $the_current_meta_to_delete) {

delete_post_meta(get_the_ID(), $the_current_meta_to_delete); echo $field_key.' deleted!
'; // this allows us to see if anything was deleted, if nothing shows up, no more posts with that meta_key were found

}
}

endwhile;

}
wp_reset_postdata(); ?>

Okay now let’s walk through it all piece by piece.

<?php $the_current_meta_to_delete = $_GET['the_custom_meta_field'];

You could change this to $the_current_meta_to_delete = 'my_custom_field_key'; where “my_custom_field_key” is the key (as opposed to the value – see more on Custom Fields if you’re not familiar with these terms) for your custom field. I use the $_GET[] solution to make things easier. That way, I can go to a web address like https://mywebsite.com/?the_custom_meta_field=my_custom_field_key and it’ll run based on the URL, so I don’t have to keep editing my page.php file every time. Questions on that? Drop a line in the comments.

$args = array(
'posts_per_page' => -1, // this gets all posts, you may only want to get a few at a time
'post_type' => 'post', // this can be changed to any post type, or array('post', 'page', 'somecustomposttype')
'meta_key' => $the_current_meta_to_delete
);
$the_query = new WP_Query( $args );
if ($the_query->have_posts()) {
while ( $the_query->have_posts() ) :
$the_query->the_post();

This is our WP_Query, i.e. how we tell WordPress to go and grab the posts we want to work with. If you’re not familiar with this, and plan on working in WP often, I’d learn how it works in and out as it’s the meat and potatos and ketchup of developing for WordPress.

This WP_Query says “Go and get all posts that are the default post_type “post” but also only those that have the “meta_key” we’re looking for.” The meta_key is essential later in the process.

Since we’re running through all of them, what happens in the next chunk of code happens individually to each post, one at a time as it runs through them all.

$custom_fields = get_post_custom();
foreach ( $custom_fields as $field_key => $field_values ) {
foreach ( $field_values as $key => $value )
if ($field_key == $the_current_meta_to_delete) {
delete_post_meta(get_the_ID(), $the_current_meta_to_delete); echo $field_key.' deleted!
';
}
}

Here we go and grab every custom field associated with each post, one at a time as WP_Query goes through them all. For each custom field, we then check to see if the field’s key matches up with the key we’re looking for, and if it does, we delete it’s post meta altogether.

echo $field_key.' deleted!<br>' is just there to put out a visual indicator on the page saying the field has been deleted. If nothing shows on the page, then that key didn’t exist and you can move on (deleting all of this code from your page.php or wherever you put it of course.)

endwhile;

}
wp_reset_postdata(); ?>

This bit simply wraps up our WP_Query and wp_reset_postdata() is just good practice in general after a WP_Query, but probably doesn’t really need to be here for this particular use.

Note! When you’re done, delete this code from the page template you added it to!

Up Next: Why Your WordPress Theme (and all of those Plugins) is Ruining Your Website