On a WordPress site http://www.example.com/ (MAIN SITE) there are 540000+ posts.

I want to reduce number of posts to enhance site speed.

I've installed new WordPress at http://www.example.com/business-consultants/ (NEWSITE) and here I moved from MAIN SITE 3135 posts tagged 'Business Consultants'.

Currently, I've set 301 redirect on MAIN SITE to redirect posts tagged 'Business Consultants' to the new WordPress install, that is

{MAIN_SITE}/{slug} => {NEW_SITE}/business-consultants/{slug}

What I do is checking post tag, and if 'business-consultants' is present, redirect request to new the new site using wp_safe_redirect().

The problem is that to make redirect works I need the post are actually available in MAIN SITE, what I'm looking for is a solution to make redirect work even if posts tagged 'Business Consultants' are deleted from main site.


This is a problematic tasks.

What I suggest you is run a one-time task that:

  1. get all the posts tagged 'business-consultants'
  2. run a one-time task* to save in a php file an array of that posts slugs
  3. when a request is done on one of that url, redirect to new site url
  4. delete posts

I'll write all the code as plugin.

I very strongly suggest you to take a full backup of your database before installing this plugin

Save following code in a file called RedirectDeleted.php, put in plugin folder, and activate it.

It will take same seconds before the page is fully loaded, but after that, if you go on your post list page, you should see all the post tagged 'business-consultants' deleted from WordPress, in addition in your uploads folder you should see a php file named redirect_deleted_business-consultants.php.

If ever, just visit an url like http://www.example.com/a-post-slug (where 'a-post-slug' is a real slug of a ost tagged 'business-consultants') and you should be redirected to http://www.example.com/business-consultants/a-post-slug.

Please note that if you ever delete redirect_deleted_business-consultants.php file or you unistall the plugin, the redirect will not work anymore. Install the plugin as a must-use plugin can be an idea to prevent unistall.

The great thing is that you can move to its own WP install any other tag!

E.g. if you have a tag with slug 'business-stuff' and you want to move it to another WP install, just create this install at the url http://www.example.com/business-stuff and add 'business-stuff' to the plugin file in the $target_tags array (see below).

The code:

 * Plugin Name: RedirectDeletedTag
 * Plugin URI: https://wordpress.stackexchange.com/questions/136018/

class RedirectDeletedTag {

  * Adjust this variable according to real tag slug.
  * The key is the tag slug, and the value is the base url for the redirect.
  * If you want, you can add other tag slugs you want ot move to separate sites.
  protected $target_tags = array(
    'business-consultants' => 'http://www.example.com/business-consultants/',

  function cacheUrls() {
    if ( ! is_admin() || ! current_user_can( 'install_plugins' ) ) return;
    $t = new ArrayIterator( $this->target_tags );
    while( $t->valid() ) {
      $tag = $t->key();
      if ( $this->shouldCache( $tag ) ) $this->cacheTagUrls( $tag );

  function redirect() {
    if ( is_admin() ) return;
    $url = $this->getUrl();
    if ( ! empty( $url ) ) {
      $t = new ArrayIterator( $this->target_tags );
      while( $t->valid() ) {
       $this->redirectTag( $t->key(), $url, $t->current() );

  protected function cacheTagUrls ( $tag ) {
    $args = array( 'post_type' => 'post', 'nopaging' => true, 'tag' => $tag );
    $query = new WP_Query( $args );
    if ( $query->have_posts() ) {
      $slugs = wp_list_pluck( $query->posts, 'post_name' );
      $id_list =  implode( ',',  wp_list_pluck( $query->posts, 'ID') );
      $cache = '<?php return array("' . implode('","', $slugs ) . '");';
      if ( @file_put_contents( $this->getCachePath( $tag ), $cache ) !== FALSE ) {
        set_transient( __CLASS__ . $tag, '1' );
        // cache saved, delete all posts and term
        global $wpdb;
        $wpdb->query( "DELETE FROM $wpdb->posts WHERE ID IN ($id_list)" );
        $term = get_term_by('slug', $tag, 'post_tag');
        if ( ! empty( $term ) && isset( $term->term_id ) ) {
          wp_delete_term( $term->term_id, 'post_tag' );

  protected function redirectTag( $tag, $url, $base ) {
    if ( file_exists( $this->getCachePath( $tag ) ) ) {
      $cached = (array) include $this->getCachePath( $tag );
      if ( in_array( $url, $cached ) ) {
        wp_redirect( trailingslashit( $base ) . $url );

  protected function shouldCache( $tag = '' ) { 
    $should = FALSE;
    if ( is_admin() && current_user_can( 'install_plugins' ) ) {
      $transient = get_transient( __CLASS__ . $tag );
      $file = file_exists( $this->getCachePath( $tag ) );
      $should = empty($transient) && ! $file;
    return $should;

  protected function getCachePath( $tag = '' ) {
    $upload_dir = wp_upload_dir();
    $base = trailingslashit( $upload_dir['basedir'] );
    return  $base . 'redirect_deleted_' . $tag . '.php';

  protected function getUrl() {
    $home_path = trim( parse_url( home_url(), PHP_URL_PATH ), '/' );
    $full = trim( str_replace( $home_path, '', add_query_arg( array() ) ), '/' );
    $stripped = explode( '?', $full );
    return trim( $stripped[0], '/' );


$rd = new RedirectDeletedTag();
add_action( 'shutdown', array( $rd, 'cacheUrls' ) );
add_action( 'after_setup_theme', array( $rd, 'redirect' ) );

The plugin is available as Gist here.

* To run a one-time task I'll use a transient plus a check if current user is an administrator, plus the existence of the cache file. If you want, take a look at this question/answers to know different approach to one-time tasks in WordPress.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.