Custom Code Snippets for WordPress Multisite in 2026: Patterns, Tools, and Network-Aware Deployment

Custom Code Snippets for Multisite: Best Practices

Custom Code Snippets for WordPress Multisite in 2026: Patterns, Tools, and Network-Aware Deployment

Managing custom code on a single WordPress site is straightforward β€” add a snippet, test it, ship it. Managing custom code across a multisite network is a different problem. The same snippet might apply to every site, only to specific sites, only to the network admin, or only when a particular condition is true on a particular site. Choose the wrong deployment approach and your “small customization” becomes a maintenance burden across dozens of sites.

This guide covers the decision framework for where to put multisite code, the realistic plugin and code-based approaches, and the multisite-specific patterns that resolve the common problems before they happen.

Where should multisite code actually live?

Five places, each appropriate for different cases.

Must-Use Plugins (mu-plugins). Code in wp-content/mu-plugins/ runs on every site in the network, automatically activated, and not removable through the admin UI. Best fit for code that must run everywhere and shouldn’t be disabled accidentally β€” security headers, custom error handlers, core integrations that the rest of the network depends on.

Network-activated plugins. A regular plugin activated at the network level runs on every site. Best fit for shared functionality where you want plugin-management UI (activation, deactivation) at the network admin level, and where you might want to roll back via deactivation.

Per-site activated plugins. A regular plugin activated on individual sites only. Best fit for functionality that only some sites need β€” a payment integration on commerce sites, a course platform on training sites.

Theme functions.php. Code in a theme that’s used across the network. Best fit when the code is theme-related and survives because the theme is used. Less appropriate for general utility code because the code disappears if a site switches themes.

Snippet management plugins (WPCode, Code Snippets, Woody Code Snippets, etc.). Plugins that store and execute code snippets through their own UI. Best fit for code that needs admin-UI management, conditional execution, or per-site customization without touching files.

The right choice depends on the answer to three questions: who needs to manage this code (developers vs. site admins), does it need to run on every site or some sites, and does it need to survive admin actions like theme changes or plugin deactivation?

A decision framework for multisite code placement

Work through these questions in order. The answer determines the right location.

1. Must this code run on every site, every time, regardless of what admins do?

If yes β†’ MU-plugin. This is the only location that runs automatically without admin action and can’t be disabled through the admin UI. Use for security-critical code, infrastructure integrations, custom debugging hooks that the rest of the network depends on.

If no β†’ continue.

2. Should the code be available to all sites but optionally activatable by each?

If yes β†’ Network-activated plugin (or regular plugin available in the network plugins list). Site admins activate as needed.

If no β†’ continue.

3. Is the code site-specific (only some sites need it)?

If yes β†’ Per-site activated plugin or a snippet stored via a snippet management plugin running on that site only.

4. Is this snippet small, easily reversible, and likely to be edited by non-developers?

If yes β†’ Snippet management plugin. Admin UI for adding, editing, conditional logic, and quick rollback without file system access.

If no β†’ File-based code (plugin, mu-plugin, or theme functions.php). Version control, code review, and the engineering discipline that goes with code files.

For most multisite networks, you’ll have a mix of all five. The decision framework matters most when you’re adding a new piece of code and choosing where to put it.

MU-plugins for multisite β€” the underused pattern

Must-Use plugins are a single file or directory in wp-content/mu-plugins/ that WordPress loads automatically on every request, on every site, with no admin UI to deactivate. For multisite networks, this is often the right answer for code that “should just run.”

Creating a multisite mu-plugin:

<?php
/**
 * Plugin Name: Network Security Headers
 * Description: Adds security headers to all responses across the network
 */

add_action( 'send_headers', function() {
    header( 'X-Content-Type-Options: nosniff' );
    header( 'X-Frame-Options: SAMEORIGIN' );
    header( 'Referrer-Policy: strict-origin-when-cross-origin' );
} );

Save as wp-content/mu-plugins/network-security-headers.php. The code runs on every page load on every site in the network. No activation needed.

MU-plugin gotchas:

  • Subdirectory files don’t auto-load. Only files directly in wp-content/mu-plugins/ are loaded β€” files in subdirectories require a loader file in the root that includes them
  • No activation hooks. register_activation_hook() doesn’t fire because the plugin is always “active”
  • No deactivation hooks. If you need cleanup behavior, build it explicitly
  • Site admins can’t disable. This is the point β€” but means a buggy mu-plugin can break the entire network until the file is removed from the file system
  • Version updates require file replacement. There’s no WordPress.org update mechanism for mu-plugins

For the network-wide cases where the trade-offs work β€” security code, infrastructure integrations, code that should never be turned off β€” mu-plugins are usually cleaner than network-activated plugins.

Network-activated plugins for shared functionality

A regular plugin activated through the network admin’s Plugins page runs on every site. This is the standard approach for shared functionality that benefits from plugin-management UI.

Common patterns:

  • Network-wide settings storage: use the sitemeta table via get_site_option() / update_site_option() rather than get_option() (which is per-site)
  • Per-site conditional logic: within a network-activated plugin, branch on get_current_blog_id() or get_current_site() to apply different behavior on different sites
  • Network admin pages: hook into network_admin_menu rather than admin_menu to add management UI accessible only to super admins
// Read a network-wide setting
$api_key = get_site_option( 'myplugin_api_key' );

// Read a per-site setting
$site_specific = get_option( 'myplugin_site_setting' );

// Conditional behavior per site
if ( get_current_blog_id() === 2 ) {
    // Site 2 specific behavior
}

For migration from a single-site plugin to a network-activated one, the most common pitfall is options storage β€” get_option() reads per-site, so settings that should be network-wide need to move to get_site_option().

Per-site snippets via WPCode, Code Snippets, and similar plugins

For per-site customization that doesn’t need to be a full plugin β€” small CSS tweaks, conditional logic, third-party script injection β€” snippet management plugins are the right size of tool.

WPCode (formerly Insert Headers and Footers Code)

The most widely adopted snippet management plugin in 2026. Strong fit for multisite because it allows network-level snippet libraries plus per-site snippet customization. Free tier covers basic snippet management; paid tiers add conditional logic, scheduling, version history.

Multisite-specific features in WPCode:

  • Network-wide library of “approved” snippets that individual sites can enable
  • Per-site snippet overrides β€” a site admin can enable a snippet from the library without modifying the snippet itself
  • Conditional logic that can reference the current site (is_main_site(), current blog ID) to apply selectively
  • Snippet preview and validation before activation, reducing the risk of breaking sites

The typical multisite workflow: network admin maintains a library of approved snippets; site admins enable the snippets they need; conditional logic in snippets handles per-site variations.

Code Snippets (the original)

Older plugin with a strong user base. Less actively maintained than WPCode in recent versions; the multisite features are basic compared to WPCode’s network library pattern.

Where it fits: Networks with simpler snippet needs or that don’t want WPCode’s full feature set. The free version is sufficient for most basic cases.

Woody Code Snippets / Woody Ad Snippets

Geared toward ad injection and tracking script management more than general-purpose snippets. Strong fit if your snippet need is primarily inserting third-party scripts.

Choosing between snippet plugins

For a multisite network:

  • WPCode if you need a network-level snippet library with per-site activation. The most multisite-friendly option in 2026.
  • Code Snippets if your needs are simple per-site snippet management without network-level coordination
  • Woody if your primary use case is ad/tracking script injection

For technical teams that prefer code over UI, the file-based approaches (mu-plugins, network-activated plugins) often work better than any snippet plugin β€” version control, code review, and CI/CD apply naturally.

Snippet patterns specific to multisite

Several patterns come up repeatedly in multisite code and benefit from being written once carefully.

The switch_to_blog() pattern

When code on one site needs to interact with data on another site, use switch_to_blog() and restore_current_blog():

$current_blog = get_current_blog_id();
$target_blog  = 5;

switch_to_blog( $target_blog );

// Now reading and writing happens against site 5's tables
$other_site_posts = get_posts( [ 'numberposts' => 10 ] );

restore_current_blog();

// Back to current site context

Forgetting restore_current_blog() is a common bug β€” subsequent code runs in the wrong site’s context. Wrap the switched code in a try/finally if there’s any chance of exceptions:

switch_to_blog( $target_blog );
try {
    // ... do work in target site
} finally {
    restore_current_blog();
}

Network admin only code

For functionality that should only run when a super admin is in the network admin:

add_action( 'network_admin_menu', function() {
    add_menu_page(
        'Network Settings',
        'My Network Tool',
        'manage_network',
        'my-network-tool',
        'my_network_tool_page'
    );
} );

function my_network_tool_page() {
    if ( ! current_user_can( 'manage_network' ) ) {
        wp_die( 'You do not have permission' );
    }
    // ... render the page
}

The manage_network capability is super-admin-specific; the network admin menu and page only appear for that capability.

Per-site customization injection

When a network-activated plugin needs to apply different behavior on different sites without hardcoding site IDs in code:

add_filter( 'myplugin_settings', function( $settings ) {
    $site_overrides = get_option( 'myplugin_site_overrides', [] );
    return array_merge( $settings, $site_overrides );
} );

Each site stores its own myplugin_site_overrides option (per-site, via get_option()); the network plugin reads it and merges into the base settings. Site admins can customize without touching plugin code.

Network-wide cache invalidation

When data changes on one site that affects content on other sites (or on the network admin), you need to invalidate caches across the network:

function myplugin_invalidate_network_cache() {
    if ( ! is_multisite() ) {
        return;
    }

    $sites = get_sites( [ 'number' => 0 ] );
    foreach ( $sites as $site ) {
        switch_to_blog( $site->blog_id );
        wp_cache_delete( 'myplugin_data', 'myplugin' );
        restore_current_blog();
    }
}

Use this sparingly β€” invalidating every site’s cache is expensive on large networks. If only certain sites are affected, scope the loop accordingly.

Conditional execution based on site capabilities

When a snippet should only run on sites that have a specific plugin or theme active:

add_action( 'init', function() {
    if ( ! class_exists( 'WooCommerce' ) ) {
        return;    // skip on sites without WooCommerce
    }
    // ... your WooCommerce-dependent code
} );

This is cleaner than blacklisting specific site IDs because it adapts as sites install or remove plugins.

WP-CLI commands for multisite development and testing

WP-CLI’s multisite commands handle most multisite-specific operations without requiring you to click through the admin UI. For developers writing or testing snippets, these are the productivity tools.

# List all sites in the network
wp site list

# List with specific fields
wp site list --fields=blog_id,url,registered,last_updated

# Get details for a specific site
wp site list --site__in=2,3 --format=json

# Run a script against a specific subsite
wp eval-file my-test-snippet.php --url=subsite.example.com

# Run a one-off PHP command against a specific subsite
wp eval 'echo get_option("blogname");' --url=subsite.example.com

# Loop a command across all sites in the network
for url in $(wp site list --field=url); do
    wp eval "echo get_option('blogname');" --url=$url
done

# Add/remove super admin
wp super-admin add username
wp super-admin remove username
wp super-admin list

# Network activate / deactivate a plugin
wp plugin activate myplugin --network
wp plugin deactivate myplugin --network

# Per-site plugin activation
wp plugin activate myplugin --url=subsite.example.com

# Run a database query against the main blogs table
wp db query "SELECT blog_id, domain, path, last_updated FROM ${TABLE_PREFIX}blogs LIMIT 10"

The --url=subsite.example.com flag is the most useful single feature for multisite snippet testing β€” it runs the WP-CLI command in the context of that specific subsite, so all per-site options, plugins, and theme code load correctly. For sites where you want to test a snippet before deploying it network-wide, the pattern is to save the snippet as a file and run wp eval-file against a single subsite first.

For longer development sessions, drop into a WP-CLI shell for a specific site:

wp shell --url=subsite.example.com
> get_current_blog_id()
2
> get_option('siteurl')
'https://subsite.example.com'

This is an interactive PHP REPL with WordPress fully loaded for the specified site.

Multisite-specific WordPress functions you’ll actually use

A handful of multisite functions come up constantly in real code. Knowing what each one returns saves significant debug time.

// Is the install actually multisite?
is_multisite();                    // bool

// Current site / blog ID
get_current_blog_id();             // int (1+)

// Main site (typically blog ID 1)
is_main_site();                    // bool
is_main_site( $site_id );          // bool, for a specific site

// Network admin context
is_network_admin();                // bool, true on /wp-admin/network/...

// All sites in the network
$sites = get_sites();                                            // WP_Site[]
$sites = get_sites( [ 'number' => 0 ] );                         // all sites, no limit
$sites = get_sites( [ 'public' => 1, 'archived' => 0 ] );        // only public, non-archived
$sites = get_sites( [ 'site__in' => [ 2, 3, 5 ] ] );             // specific IDs
$sites = get_sites( [ 'meta_key' => 'language', 'meta_value' => 'pl' ] );  // by site meta

// Specific site object
$site = get_site( 2 );             // WP_Site|null

// Network info
$network = get_network();          // WP_Network for current request
$network_id = get_current_network_id();

// Switch context for cross-site operations
switch_to_blog( $other_blog_id );
// ... do work
restore_current_blog();

// Hooks specific to multisite
do_action( 'wp_initialize_site', $new_site, $args );    // fires when a new site is created
do_action( 'wp_uninitialize_site', $old_site );          // fires when a site is deleted
do_action( 'wpmu_new_user', $user_id );                  // network-level user created

get_sites() is particularly useful and worth knowing well. The number parameter defaults to 100 β€” for large networks, set number => 0 to get all sites, or paginate explicitly. The count argument turns the function into a counter:

$total_sites = get_sites( [ 'count' => true ] );    // returns int count, not array

For cross-site queries, the $wpdb->blogs table holds the master site list. Direct queries are sometimes faster than get_sites() for specific filtering:

global $wpdb;
$recent = $wpdb->get_results(
    "SELECT blog_id, domain, path FROM {$wpdb->blogs}
     WHERE deleted = 0 AND archived = 0
     ORDER BY last_updated DESC LIMIT 10"
);

Performance considerations for code running on many sites

A snippet that runs on every page load on every site in a 50-site network runs 50Γ— more often than the same snippet on a single site. The cost adds up. For network-activated plugins and mu-plugins, performance discipline matters more than on a single site.

Cache object lookups per request

If your snippet looks up the same data multiple times per request, cache it within the request:

function myplugin_get_config() {
    static $config = null;
    if ( $config === null ) {
        $config = get_option( 'myplugin_config' );
    }
    return $config;
}

This is in-memory only β€” within the request β€” but eliminates repeated database hits within a single page load.

Use persistent object caching with appropriate groups

When you cache data that’s the same across the network, use the global cache group so the cache is shared across all sites:

// Set: cached globally, shared across all sites in the network
wp_cache_set( 'shared_key', $value, 'myplugin-global', HOUR_IN_SECONDS );

// Get: same cache value regardless of current site
$value = wp_cache_get( 'shared_key', 'myplugin-global' );

// Tell WordPress the group is global (multisite-safe)
add_action( 'init', function() {
    wp_cache_add_global_groups( [ 'myplugin-global' ] );
} );

Per-site cache is the default behavior β€” calling wp_cache_set( 'key', $value, 'group' ) stores per-blog by default in multisite. Use wp_cache_add_global_groups() to mark a group as network-wide.

For data that genuinely varies per site (post counts, site-specific user lists), keep the default per-site cache behavior.

Avoid loops that scale with network size

A particularly expensive pattern: iterating over every site in the network on every request. If you must loop sites, cache the result heavily and don’t repeat the loop within a request:

// Expensive β€” runs on every page load
foreach ( get_sites( [ 'number' => 0 ] ) as $site ) {
    switch_to_blog( $site->blog_id );
    // ... do something
    restore_current_blog();
}

// Better β€” cache the result, only re-run when invalidated
function myplugin_aggregate_data() {
    $cached = wp_cache_get( 'aggregate', 'myplugin-global' );
    if ( $cached !== false ) {
        return $cached;
    }

    $result = [];
    foreach ( get_sites( [ 'number' => 0 ] ) as $site ) {
        switch_to_blog( $site->blog_id );
        $result[ $site->blog_id ] = get_some_data();
        restore_current_blog();
    }

    wp_cache_set( 'aggregate', $result, 'myplugin-global', HOUR_IN_SECONDS );
    return $result;
}

For aggregations that need to be exact in real time (rather than cached), consider running them via WP-Cron rather than on user requests:

add_action( 'init', function() {
    if ( ! wp_next_scheduled( 'myplugin_aggregate' ) ) {
        wp_schedule_event( time(), 'hourly', 'myplugin_aggregate' );
    }
} );

add_action( 'myplugin_aggregate', 'myplugin_aggregate_data' );

Skip work on sites that don’t need it

A network-activated plugin can early-return on sites that don’t use the feature:

add_action( 'init', function() {
    if ( ! get_option( 'myplugin_enabled' ) ) {
        return;    // skip on sites without this option
    }
    // ... full plugin initialization
} );

This pattern lets site admins opt in via the per-site option without the plugin doing any work on sites that haven’t enabled it.

Network admin UI for snippet plugins (WPCode walkthrough)

For WPCode users on multisite, the network-level UI is where you create snippets available to all sites. The workflow:

  1. Network activate WPCode at Network Admin β†’ Plugins
  2. Open Network Admin β†’ WPCode β†’ Code Snippets β€” this is the network-level library
  3. Add a new snippet with the code, snippet type (PHP, CSS, JS, HTML), execution conditions
  4. Set availability β€” WPCode lets you choose whether the snippet is available to all sites in the network, or limited to specific sites
  5. Save the snippet to the network library
  6. From individual subsites β€” site admins see the network-level snippets in their WPCode interface and can activate the ones they want

For network-only snippets (run everywhere, not optional), mark the snippet as “Always Active” in the network UI rather than relying on per-site activation.

The conditional logic in WPCode supports multisite-specific conditions: “Run on main site only”, “Run on subsites only”, “Run on specific site IDs”. Use these to scope a network-deployed snippet to the sites where it should apply, without writing the conditional logic in PHP yourself.

For Code Snippets (the original plugin), the multisite scope is more limited β€” snippets are typically per-site rather than network-distributed. If you need a network library pattern, WPCode is the cleaner choice in 2026.

Snippet lifecycle in multisite β€” dev to production

Code that ships to a multisite network has higher stakes than single-site code β€” a bad snippet affects every site simultaneously. The pattern that prevents most production incidents:

1. Local development. Write the code in your local WordPress install (or development environment matching the production multisite configuration). Test on multiple sites within the local network to catch site-specific issues.

2. Staging. Deploy to a staging environment that mirrors production. For multisite, this means the same network configuration (subdomain vs subdirectory, same site count, same approximate user permissions). Snippets that work fine on a small staging network can fail at scale.

3. Network admin review. For network-activated plugins or mu-plugins, the network admin should review the code before deployment. For snippet plugins with conditional logic, the network admin should review the conditions to confirm they match expected behavior.

4. Gradual production rollout. For network-activated plugins, activate on a subset of sites first if your snippet plugin supports it (WPCode has this). For mu-plugins, deploy at a time when you can monitor for issues. For per-site snippets, the site admin owns the rollout decision.

5. Monitoring after deployment. Check error logs across the network. Network-wide errors that affect all sites need immediate rollback; per-site errors can usually be diagnosed in place.

6. Documentation. Keep a record of what was deployed when, where, and why. The multisite “what changed yesterday” question is harder to answer than on a single site.

Snippet conflict resolution

Conflicts in multisite snippet code come in several flavors:

Same hook fired by network + site code. If a network-activated plugin and a site-level snippet both hook into init, both run. The order is determined by registration order and priority β€” explicitly set priorities to control which runs first:

add_action( 'init', 'network_handler', 10 );      // runs first
add_action( 'init', 'site_handler', 20 );          // runs after

Network setting overridden per site. When network-wide settings and per-site settings conflict, decide which wins and document the rule. Common patterns: site overrides network (per-site control), network overrides site (enforced standards), or merging both (with documented merge rules).

Snippet plugin conflict with network-activated plugin. WPCode-managed snippet hooks into the same filter as a network plugin. The order is unpredictable without explicit priority. For critical hooks, use file-based code with controlled priorities rather than snippet plugins.

Frequently asked questions

Should I use mu-plugins or network-activated plugins for code that runs everywhere?

MU-plugins for code that should never be disabled (security, infrastructure). Network-activated plugins for code that benefits from the plugin-management UI and where rollback via deactivation is useful. Both produce the same runtime behavior (code on every site) β€” the difference is in the management workflow.

Can I network-activate a plugin that wasn’t designed for multisite?

Often yes, but verify the plugin handles multisite contexts correctly. Common problems: the plugin uses get_option() for settings that should be network-wide; the plugin writes files into wp-content/uploads/ without site awareness; the plugin assumes a single user role hierarchy. Test on a staging network before network-activating in production.

How do I version-control snippet plugin content?

Snippet plugins typically store snippet content in the database, which doesn’t naturally version-control. Options: export snippets to files periodically (WPCode and some others support export to PHP), maintain snippet content in code-based plugins for everything where version control matters, or use a CI/CD process that exports and stores snippet content in git.

Can I use Code Snippets and WPCode together?

Technically yes, but it’s rarely a good idea. Snippets that hook into the same WordPress hooks will both run, with unpredictable order. Choose one snippet plugin and consolidate.

How do I roll back a bad mu-plugin?

Delete the file from wp-content/mu-plugins/. WordPress will stop loading it on the next request. For sites where the bad mu-plugin caused an admin-side error preventing access to the WordPress dashboard, you need file-system access (SFTP, SSH, hosting control panel) to remove the file.

What’s the difference between is_main_site() and is_network_admin()?

is_main_site() checks whether the current request is for the network’s main site (typically site ID 1). is_network_admin() checks whether the current request is in the network admin pages. They’re independent β€” the main site can have regular admin pages, and the network admin is accessed at a network-specific URL.

How do I share a custom post type across all multisite network sites?

Custom post types are registered per site. To make a CPT available on every site, register it from a network-activated plugin or mu-plugin (which runs on every site). The post type then exists on each site; the actual posts are still per-site.

How do I safely test a snippet on one subsite before deploying network-wide?

Use wp eval-file with the --url flag:

wp eval-file my-snippet.php --url=test-subsite.example.com

This runs the snippet in the context of just that subsite. If the snippet hooks into WordPress runtime (add_action, add_filter), the hooks only register for the duration of that single command β€” perfect for testing without committing the snippet to the codebase. After validating, deploy to a network plugin or WPCode network library.

What hooks fire at network activation vs site activation?

Network activating a plugin fires activated_plugin and the plugin’s own register_activation_hook callback in the network admin context (is_network_admin() is true). Site activation (per-site) fires the same hooks but in the site admin context. For plugins that need different behavior at each, branch on is_network_admin() in the activation handler. For plugins that need to do work on every site when network-activated, loop sites in the activation handler with switch_to_blog().

How do I run a WP-Cron job per site vs network-wide?

wp_schedule_event() schedules per-site by default β€” the job runs once per cron tick on the site where it was scheduled. For a network-wide cron job (runs once across the network, not once per site), schedule it from the main site only, or use a network-level cron implementation:

add_action( 'init', function() {
    if ( ! is_main_site() ) {
        return;    // only main site schedules the cron
    }
    if ( ! wp_next_scheduled( 'myplugin_network_task' ) ) {
        wp_schedule_event( time(), 'hourly', 'myplugin_network_task' );
    }
} );

For tasks that need to run on every site, schedule per-site and use switch_to_blog() in the handler if cross-site data is needed.

Can WPCode snippets call functions defined in other plugins?

Yes, as long as the other plugin has loaded by the time the snippet runs. For snippets hooked to early actions (like plugins_loaded), the target plugin might not be ready yet. For most use cases (hooks fired during normal page rendering), function calls work as expected.

What to do next

If you’re starting a new multisite network and need to decide where custom code lives, work through the decision framework section above. For most networks, the mix that works: critical infrastructure as mu-plugins, shared functionality as network-activated plugins, per-site customization via a snippet management plugin (WPCode if you want a network library; Code Snippets for simpler per-site needs), with documented patterns for switch_to_blog() and per-site setting overrides.

If you’re inheriting an existing multisite network with snippets scattered across multiple locations, the audit pattern: list all mu-plugins, all network-activated plugins, all per-site activated plugins, all snippets in any snippet management plugin. For each, document what it does, where the code lives, and who owns maintenance. The mapping exercise often reveals duplicated functionality, abandoned snippets, and code that should be consolidated.

Multisite custom code is harder to manage than single-site code because the failure modes are network-wide rather than site-local. The discipline that single-site projects often skip β€” staging environments, gradual rollouts, code review, version control β€” becomes essential at multisite scale. The plugin and pattern choices above support that discipline rather than fighting it.


Discover more from WP Winners πŸ†

Subscribe to get the latest posts sent to your email.

More WorDPRESS Tips, tutorials and Guides

Discover more from WP Winners πŸ†

Subscribe now to keep reading and get access to the full archive.

Continue reading