Removing Orphaned Uploads From Multisite

If you have an active multisite network, chances are you have orphaned content left over from deleted subsites. To clean up the database, there is a fantastic plugin Cleanup Multisite DB Tables which adds a few wp-cli commands to locate and remove any orphaned database tables. To cleanup files, I wasn’t able to find an automated approach. The following are some manual steps I took recently while cleaning up a rather large multisite installation.

In order to figure out which sites are safe to remove from, we first need a list of all subsites. This can gather by running the following wp-cli command wp site list --format=ids which will output something like 1 2 4 7.

Next we need a list of actual upload folders within multisite. On legacy multisite networks this is found under /wp-content/blogs.dir/ for modern networks /wp-content/uploads/sites. From the terminal we can grab a full list by running ls wp-content/blogs.dir/ which will return something like this:

1    2  3  4  5  6  7

These site IDs can be copied into a new bash script we’ll call


# Site IDs taken from `wp site list --format=ids`
sites_to_keep=(1 2 4 7)

# Site IDs from current multisite
#all_site_ids=(`ls wp-content/blogs.dir/`)
all_site_ids=(1    2  3  4  5  6  7)


# Function to check array for match
containsElement () {
  local e
  for e in "${@:2}"; do [[ "$e" == "$1" ]] && return 0; done
  return 1

# Loop through all site IDs upload folders and check if found in active Site ID. Outputs orphaned uploads.
for (( i=0; i<${all_site_ids_count}; i++ ));
  containsElement "${all_site_ids[$i]}" "${sites_to_keep[@]}"
  if [ $? == 1 ]
    printf "${all_site_ids[$i]} "

Running will output the following: 3 5 6 which are orphaned upload directories. These folders are safe to delete.

I recommend backing up these these directories before deleting. That can be accomplished by pasting those resulting site IDs into the following script. This script will move the orphaned directories into a temporary location, generate a backup zip and then remove the directories.

# Site IDs taken from output of
to_remove=(3 5 6)

# Make directory to archive old directories
mkdir -p ~/private/orphaned_directories/

# Change directories
cd wp-content/blogs.dir/

# Move orphaned directories to archive location
for site in ${to_remove[@]}; do
    mv $site ~/private/orphaned_directories/

# Generate zip
cd ~/private
zip_name="$(date +'%Y-%m-%d')"
zip -r $zip_name orphaned_directories/

# Remove orphaned directories
rm -rf ~/private/orphaned_directories/