Disable All WordPress Updates and Update Notices

Some WordPress sites must not change unless you change them: a client site under a maintenance contract that you update on staging first, a site deployed from Git, or a locked-down kiosk. On those sites the automatic updates, the update checks and the red update counts in wp-admin only get in the way. This snippet disables all WordPress updates (core, plugins, themes and translations) and the update notices.

Read this first: it also stops security updates. Only use it when you update the site yourself, for example with wp core update and wp plugin update --all in WP-CLI or in your deployment. If you only want to stop the automatic updates and keep security fixes, use Disable WordPress Automatic Updates but Keep Security Fixes instead.

The snippet

<?php
/**
 * Disable WordPress core, plugin, theme and translation updates,
 * the update checks behind them and the update notices in wp-admin.
 *
 * Warning: this also stops security updates. Update by hand or with WP-CLI.
 */

// Automatic (background) updates.
add_filter( 'automatic_updater_disabled', '__return_true' );
add_filter( 'auto_update_core', '__return_false' );
add_filter( 'auto_update_plugin', '__return_false' );
add_filter( 'auto_update_theme', '__return_false' );
add_filter( 'auto_update_translation', '__return_false' );

// Hide the "Enable auto-updates" links on the Plugins and Themes screens.
add_filter( 'plugins_auto_update_enabled', '__return_false' );
add_filter( 'themes_auto_update_enabled', '__return_false' );

// Update checks against WordPress.org: the twice-daily cron jobs and the checks on admin screens.
remove_action( 'init', 'wp_schedule_update_checks' );
remove_action( 'wp_version_check', 'wp_version_check' );
remove_action( 'wp_update_plugins', 'wp_update_plugins' );
remove_action( 'wp_update_themes', 'wp_update_themes' );
remove_action( 'wp_maybe_auto_update', 'wp_maybe_auto_update' );
remove_action( 'admin_init', '_maybe_update_core' );
remove_action( 'admin_init', '_maybe_update_plugins' );
remove_action( 'admin_init', '_maybe_update_themes' );
remove_action( 'load-plugins.php', 'wp_update_plugins' );
remove_action( 'load-update.php', 'wp_update_plugins' );
remove_action( 'load-update-core.php', 'wp_update_plugins' );
remove_action( 'load-themes.php', 'wp_update_themes' );
remove_action( 'load-update.php', 'wp_update_themes' );
remove_action( 'load-update-core.php', 'wp_update_themes' );

// Ignore update data that was saved before the snippet ran, so no counts or "update now" links show.
add_filter( 'pre_site_transient_update_core', '__return_null' );
add_filter( 'pre_site_transient_update_plugins', '__return_null' );
add_filter( 'pre_site_transient_update_themes', '__return_null' );

// wp-admin adds these after plugins load, so remove them on admin_init.
add_action( 'admin_init', 'dpc_disable_update_admin_hooks' );

function dpc_disable_update_admin_hooks(): void {
	remove_action( 'admin_notices', 'update_nag', 3 );
	remove_action( 'network_admin_notices', 'update_nag', 3 );
	remove_action( 'admin_notices', 'maintenance_nag', 10 );
	remove_action( 'network_admin_notices', 'maintenance_nag', 10 );
	remove_action( 'upgrader_process_complete', 'wp_version_check', 10 );
	remove_action( 'upgrader_process_complete', 'wp_update_plugins', 10 );
	remove_action( 'upgrader_process_complete', 'wp_update_themes', 10 );
}

Add it with Scripts Organizer

In Scripts Organizer go to Code Blocks → Add New and give the block a name. Set Trigger location to Everywhere, tick PHP under Script location, paste the code into the PHP editor and publish. Leave Action/Hook empty. Everywhere is needed because the update checks also run from WP-Cron, in background requests that are neither wp-admin nor a front-end page.

The older version of this post said to set Action to plugins_loaded. That is not needed: the code only adds and removes hooks, and that works as soon as Scripts Organizer loads it. The same code also works unchanged in an mu-plugin, which is the safer place on client sites because nobody can deactivate it from the Plugins screen.

How it works

  • automatic_updater_disabled switches off background updates completely. The auto_update_* filters return false as well, in case another plugin turns single updates back on. The two *_auto_update_enabled filters hide the Enable auto-updates links on the Plugins and Themes screens.
  • WordPress asks WordPress.org for updates twice a day from WP-Cron and again when you open wp-admin, the Plugins, Themes or Updates screen. The remove_action() calls unhook all of those checks, so the site makes no update requests at all.
  • Update data saved before you added the snippet would keep showing old counts. The pre_site_transient_update_* filters return null, so WordPress acts as if it never checked.
  • The update nag (“WordPress 6.x is available! Please update now.”) and the after-upgrade checks are added by wp-admin itself, after plugins load. dpc_disable_update_admin_hooks() removes them on admin_init, when they exist.

The older version removed the update nag on init. In wp-admin that runs before the nag is added, so the removal did nothing, and the update checks kept running in the background.

Customise it

  • Keep translation updates: delete the auto_update_translation line.
  • Keep plugin updates but block core: delete the plugin lines (auto_update_plugin, the wp_update_plugins and _maybe_update_plugins lines and pre_site_transient_update_plugins).
  • Only hide the notices from clients: wrap the remove_action() calls in dpc_disable_update_admin_hooks() in if ( ! current_user_can( 'manage_options' ) ) and leave the update checks alone.

Gotchas

  • WP-Cron keeps the already scheduled wp_version_check, wp_update_plugins and wp_update_themes events, but nothing runs when they fire. Remove them with wp cron event delete wp_version_check (and the other two) if you want a clean list.
  • Dashboard → Updates still opens and says everything is up to date. That is not true, so check for updates with WP-CLI (wp plugin list --update=available) on a schedule.
  • Plugins with their own updater (many premium plugins) may still show their notices. Their update checks are not part of WordPress core.
  • Defining AUTOMATIC_UPDATER_DISABLED in wp-config.php only stops background updates; it does not stop the checks or the notices.

In the Scripts Organizer library Import it with one click from Scripts Organizer.

// newsletter

Three tested WordPress snippets a week

Blocks, security, performance, Tailwind, PHP and React. Copy, paste, ship. You’ll also get DPlugins product updates. No spam, and you can unsubscribe anytime.

// newsletter

Three tested WordPress snippets a week

Blocks, security, performance, Tailwind, PHP and React. Copy, paste, ship. You'll also get DPlugins product updates. No spam, and you can unsubscribe anytime.