Use of switch_to_blog() and restore_current_blog() in WordPress Multisite
Last edited on February 18, 2025

WordPress Multisite is a powerful feature that allows developers to operate multiple websites from a single installation. However, working with Multisite introduces unique challenges, especially when interacting with data across different subsites. Two critical functionsโ€”switch_to_blog() and restore_current_blog()โ€”are essential for navigating this environment, but their misuse can lead to significant performance bottlenecks.

This comprehensive guide explores the purpose of these functions, their ideal use cases, and strategies to mitigate their performance costs. By the end, you’ll understand how to leverage them effectively while maintaining optimal efficiency in your Multisite network.

Understanding WordPress Multisite Fundamentals

Before diving into switch_to_blog(), let’s clarify how Multisite works:

  • Network: A collection of subsites (blogs) under a single WordPress installation.
  • Shared Database: All subsites share the same database but use unique table prefixes (e.g., wp_2_posts for site ID 2).
  • Global vs. Local Data: Some data (users, network plugins) is global, while other data (posts, options) is local to each subsite.

This structure allows centralized management but requires careful handling when accessing subsite-specific data.

Understanding WordPress Multisite Fundamentals

What Do switch_to_blog() and restore_current_blog() Do?

switch_to_blog( $blog_id )

Switches the execution context to a specific subsite, allowing you to interact with its data as if you were on that site. Internally, it:

restore_current_blog()

Reverts the context to the original subsite, undoing the changes made by switch_to_blog().

When to Use switch_to_blog()

1. Network-Wide Dashboards

Example: A custom network dashboard displaying recent posts from all subsites.

// PHP Code

$sites = get_sites(['number' => 50]);ย ย 

foreach ($sites as $site) {ย ย 

ย  ย  switch_to_blog($site->blog_id);ย ย 

ย  ย  $recent_posts = wp_get_recent_posts(['numberposts' => 3]);ย ย 

ย  ย  // Display postsย ย 

ย  ย  restore_current_blog();ย ย 

}

2. Data Synchronization

Example: Copy default settings from the primary site to new subsites.

// PHP Code

function sync_default_settings($new_blog_id) {ย ย 

ย  ย  switch_to_blog(1); // Primary siteย ย 

ย  ย  $default_options = get_option('my_plugin_settings');ย ย 

ย  ย  restore_current_blog();ย ย 

ย  ย  switch_to_blog($new_blog_id);ย ย 

ย  ย  update_option('my_plugin_settings', $default_options);ย ย 

ย  ย  restore_current_blog();ย ย 

}ย ย 

add_action('wp_initialize_site', 'sync_default_settings');

3. Batch Processing Across Subsites

Example: Update a plugin option for all subsites.

// PHP Code

$sites = get_sites(['fields' => 'ids']);ย ย 

foreach ($sites as $blog_id) {ย ย 

ย  ย  switch_to_blog($blog_id);ย ย 

ย  ย  update_option('my_plugin_option', 'new_value');ย ย 

ย  ย  restore_current_blog();ย ย 

}

4. Cross-Site Content Aggregation

Example: Display a global activity feed combining comments from all subsites.

// PHP Code

$sites = get_sites(['number' => 20]);ย ย 

$activity = [];ย ย 

foreach ($sites as $site) {ย ย 

ย  ย  switch_to_blog($site->blog_id);ย ย 

ย  ย  $comments = get_comments(['number' => 5]);ย ย 

ย  ย  $activity[$site->blog_id] = $comments;ย ย 

ย  ย  restore_current_blog();ย ย 

}

5. User Role Management

Example: Grant a user privileges across multiple subsites.

// PHP Code

$user_id = 5;ย ย 

$target_blogs = [1, 3, 7];ย ย 

foreach ($target_blogs as $blog_id) {ย ย 

ย  ย  switch_to_blog($blog_id);ย ย 

ย  ย  add_user_to_blog($blog_id, $user_id, 'editor');ย ย 

ย  ย  restore_current_blog();ย ย 

}

Performance Implications of Switching Blogs

While switch_to_blog() is indispensable, it’s resource-intensive. Here’s why:

1. Database Overhead

  • Table Prefix Changes: Each switch updates $wpdb prefixes, forcing new SQL queries.
  • Option Cache Flush: WordPress reloads the target site’s options (e.g., get_option()) into memory, which can be slow if options are autoloaded.

2. Object Cache Reset

Switching blogs clears the current object cache, requiring re-fetching data like taxonomies and post types.

3. Hook Re-Initialization

Plugins and themes may hook into switch_blog or restore_current_blog, adding latency.

Benchmark Example

A test switching 100 subsites took 2.1 seconds without caching. With 1,000 subsites, this could exceed 20 seconds, making the process impractical for real-time use.

Optimizing Performance When Switching Blogs

1. Minimize Context Switches

Inefficient Approach:

// PHP Code

foreach ($sites as $site) {ย ย 

ย  ย  switch_to_blog($site->blog_id);ย ย 

ย  ย  $option = get_option('my_option');ย ย 

ย  ย  restore_current_blog();ย ย 

}

Optimized Approach: Fetch data in bulk using direct SQL:

// PHP Code

global $wpdb;ย ย 

$site_ids = implode(',', array_column($sites, 'blog_id'));ย ย 

$results = $wpdb->get_results("ย ย 

ย  ย  SELECT blog_id, meta_valueย ย 

ย  ย  FROM {$wpdb->base_prefix}blogsย ย 

ย  ย  JOIN {$wpdb->base_prefix}{$blog_id}_optionsย ย 

ย  ย  WHERE option_name = 'my_option'ย ย 

ย  ย  AND blog_id IN ($site_ids)ย ย 

");

2. Cache Aggressively

Cache subsite data to avoid repeated switches:

// PHP Code 

$cache_key = 'network_wide_posts';ย ย 

$data = wp_cache_get($cache_key);ย ย 

if (false === $data) {ย ย 

ย  ย  $data = [];ย ย 

ย  ย  foreach ($sites as $site) {ย ย 

ย  ย  ย  ย  switch_to_blog($site->blog_id);ย ย 

ย  ย  ย  ย  $data[$site->blog_id] = get_posts(['numberposts' => 5]);ย ย 

ย  ย  ย  ย  restore_current_blog();ย ย 

ย  ย  }ย ย 

ย  ย  wp_cache_set($cache_key, $data, 'network', 3600);ย ย 

}

3. Use WP-CLI for Batch Jobs

Run heavy operations via WP-CLI to avoid PHP timeouts:

// PHP Code

class Batch_Processor {ย ย 

ย  ย  public function update_all_posts() {ย ย 

ย  ย  ย  ย  $sites = get_sites(['fields' => 'ids']);ย ย 

  ย  ย  ย  foreach ($sites as $blog_id) {ย ย 

ย  ย  ย  ย  ย  ย  WP_CLI::log("Processing blog $blog_id");ย ย 

ย  ย  ย  ย  ย  ย  switch_to_blog($blog_id);ย ย 

ย  ย  ย  ย  ย  ย  // Batch process postsย ย 

ย  ย  ย  ย  ย  ย  restore_current_blog();ย ย 

ย  ย  ย  ย  }ย ย 

ย  ย  }ย ย 

}ย ย 

WP_CLI::add_command('batch', 'Batch_Processor');

Run with:

wp batch update_all_postsย 

4. Limit the Scope of Queries

Use get_sites() parameters to narrow down subsites:

// PHP Code

// Target only active sites in a specific networkย ย 

$sites = get_sites([ย ย 

ย  ย  'archived' => 0,ย ย 

ย  ย  'spam' ย  ย  => 0,ย ย 

ย  ย  'deleted'ย  => 0,ย ย 

ย  ย  'network_id' => 1,ย ย 

]);

5. Avoid Nested Switches

Nesting switch_to_blog() calls can cause context mismatches. Always restore before switching again:

// PHP Code

// โŒ Dangerousย ย 

switch_to_blog(1);ย ย 

switch_to_blog(2);ย ย 

restore_current_blog(); // Restores to 1ย ย 

restore_current_blog();ย ย 

// โœ… Safeย ย 

switch_to_blog(1);ย ย 

// Do workย ย 

restore_current_blog();ย ย 

switch_to_blog(2);ย ย 

// Do workย ย 

restore_current_blog();

Alternatives to switch_to_blog()

1. Direct Database Queries

Use $wpdb to query subsite tables directly:

// PHP Code

global $wpdb;ย ย 

$posts = $wpdb->get_results("ย ย 

ย  ย  SELECT * FROM {$wpdb->base_prefix}2_postsย ย 

ย  ย  WHERE post_type = 'post'ย ย 

");

2. REST API Endpoints

Expose subsite data via REST API and fetch it remotely:

// PHP Code

// On subsite 1ย ย 

register_rest_route('my-plugin/v1', '/data', [ย ย 

ย  ย  'callback' => 'get_subsites_data',ย ย 

]);ย ย 

// On the main siteย ย 

$response = wp_remote_get("https://subsite1.com/wp-json/my-plugin/v1/data");

3. Custom Database Caching

Store subsite data in a network-wide table for quick access:

// PHP Code

// On subsite switchย ย 

update_network_option(null, "site_{$blog_id}_data", $data);

Real-World Case Study: Optimizing a Multisite Dashboard

Problem: A network dashboard displaying user activity across 500 subsites took 12 seconds to load due to excessive switch_to_blog() calls.

Solution:

  • Batch Data Fetching: Replaced per-site switches with a single SQL query.
  • Caching: Stored results in a transient updated hourly via WP-CLI cron.
  • Lazy Loading: Fetched data asynchronously via AJAX after the initial page load.

Results:

  • Load time reduced to 1.2 seconds.
  • Server CPU usage dropped by 40%.

Common Pitfalls and How to Avoid Them

  • Unrestored Switches
    • Risk: Context leaks, causing data corruption.
    • Fix: Always pair switch_to_blog() with restore_current_blog().
  • Assumed Global State
    • Risk: Using globals like get_current_blog_id() without restoring context.
    • Fix: Verify the current blog ID after restoration.
  • Ignoring Caching
    • Risk: Redundant queries slow down performance.
    • Fix: Cache subsite data network-wide.

Frequently Asked Questions

A: Yes, but ensure the plugin is network-activated or handle site IDs carefully.

A: Use get_current_blog_id() or the global $blog_id.

A: No, user roles are site-specific. Use add_user_to_blog() to assign roles.

A: Avoid switch_to_blog() on the front end. Use APIs or direct queries instead.

A: Log the current blog ID before/after switches:

// PHP Code

1error_log("Switched to blog: " . get_current_blog_id());

Conclusion

switch_to_blog() and restore_current_blog() are powerful tools in a WordPress Multisite developer’s arsenal, enabling cross-site data management and global operations. However, their performance costs demand careful optimization. By minimizing switches, leveraging caching, and using direct database queries, you can maintain efficiency even in large networks.

About the writer

Hassan Tahir Author

Hassan Tahir wrote this article, drawing on his experience to clarify WordPress concepts and enhance developer understanding. Through his work, he aims to help both beginners and professionals refine their skills and tackle WordPress projects with greater confidence.

Leave a Reply

Your email address will not be published. Required fields are marked *

Lifetime Solutions:

VPS SSD

Lifetime Hosting

Lifetime Dedicated Servers