“The Theme & Plugin Seatbelt” – Locking Clients Out of Breaking Things
November 21, 2025 • Bojan
November 21, 2025 • Bojan

If you hand off WordPress sites to clients, you know the move:
WordPress is very democratic: if a user has activate_plugins or switch_themes, they can nuke your setup in two clicks.
Two tiny filters give you a hard seatbelt:
// 1) Force-enable a critical plugin
add_filter('option_active_plugins', function ($plugins) {
return array_unique(array_merge($plugins, [
'my-critical-plugin/plugin.php',
]));
});
// 2) Lock allowed themes to a single one (multisite)
add_filter('allowed_themes', function () {
return ['my-theme']; // stylesheet slug
});
Let’s unpack what these actually do and where to put them.
option_active_plugins doesWordPress stores the list of active plugins in the active_plugins option. Every time it loads plugins, it calls get_option( 'active_plugins' ). That call is filterable via option_active_plugins. (ACF Support)
So this:
add_filter('option_active_plugins', function ($plugins) {
return array_unique(array_merge($plugins, [
'my-critical-plugin/plugin.php',
]));
});
means:
my-critical-plugin/plugin.php,array_unique,Result: even if the option in the DB doesn’t contain your plugin (because the client “deactivated” it), by the time WordPress uses it, your plugin has been put back into the list.
Put the filter in a must-use plugin (recommended):
/wp-content/mu-plugins/lock-critical-plugin.php<?php
/*
Plugin Name: Lock Critical Plugin
*/
add_filter('option_active_plugins', function ($plugins) {
return array_unique(array_merge($plugins, [
'my-critical-plugin/plugin.php',
]));
});
Must-use plugins load automatically and can’t be disabled from the admin UI. That’s the whole point.
Don’t put this in the theme – if someone switches the theme, the “lock” disappears.
You should obviously tell them you’re doing this – it’s a safety feature, not a secret trap.
allowed_themesOn multisite, WordPress has the concept of “network-allowed” themes. Internally, WP_Theme::get_allowed_on_network() loads the allowedthemes site option and then passes it through the allowed_themes filter. (Simon Fraser University)
Filter signature (simplified):
/**
* @param string[] $allowed_themes Array of theme stylesheet names.
*/
apply_filters( 'allowed_themes', $allowed_themes );
So this:
add_filter('allowed_themes', function () {
return ['my-theme']; // stylesheet slug, e.g. 'twentytwentyfive'
});
does:
allowedthemes,my-theme.”On a multisite network:
Important details:
pre_option_template / pre_option_stylesheet if you want to go full dictatorship.Again: mu-plugin is best, so nobody can disable it.
<?php
/*
Plugin Name: Lock Network Theme
*/
add_filter('allowed_themes', function () {
return ['my-theme']; // e.g. 'samplehq-theme'
});
If you want different themes per site in a network, you’d use site_allowed_themes instead and return different arrays based on $blog_id, but that’s another article. (Simon Fraser University)
Use this pattern when:
But:
If you want, next step we can:
$locked_plugins = ['my-critical-plugin/plugin.php', 'another/plugin.php'];
$locked_themes = ['my-theme'];

I’m Bojan Josifoski - I’m a WordPress systems engineer who developed and maintained a proprietary WordPress-based framework used by U.S. financial institutions between 2016 and 2025.