1.1 The discovery model

Discovery is the phase where WordPress learns what updates exist. Nothing installs until WordPress writes offers to site transients and the admin interface or automatic updater reads them.

How it works

WordPress separates discovery from application and feedback. The functions wp_version_check(), wp_update_plugins(), and wp_update_themes() check for updates — they do not install anything. Their results live in site transients. Application relies on the upgrader stack (see §2.1). Feedback uses notices, badges, skins, or email depending on the entry path (see §4.1§4.2 and Part 5).

Process flow

  1. An HTTP client contacts WordPress.org or another host for third-party metadata.
  2. WordPress decodes the response into offer objects.
  3. Core merges offers into transient payloads.
  4. Admin screens and cron read the transients to decide what to display or attempt next.
  5. If discovery fails, transients remain stale or empty and the site shows no updates until a later check succeeds.

Reference

ConceptRole
Discovery functionsPopulate the update_core, update_plugins, and update_themes site transients
ApplicationWP_Upgrader subclasses; discovery alone does not invoke them
FeedbackTransients drive the in-admin interface; background email sends only after automatic updates (see §4.1)

Developer resources


1.2 Site transients: the update data layer

Site transients hold the latest discovery results. When you alter update behavior, you typically interact with these structures or the filters that wrap them.

How it works

Core caches discovery results in site transients, which are shared network-wide on multisite. wp_version_check() sets the update_core transient. That transient holds the offers array in updates, plus last_checked, version_checked, and optional translation payloads. Checksum maps are not stored here; core fetches them on demand through get_core_checksums() (see §1.9).

wp_update_plugins() sets the update_plugins transient with response, no_update, checked versions, and translation lists. wp_update_themes() sets the update_themes transient following the same pattern for themes.

Dynamic filters such as pre_set_site_transient_{$transient} run before save. This filter is the primary integration point for adjusting or clearing structures before an update runs.

Process flow

  1. A check function runs and builds or merges a payload.
  2. Filters may alter the payload before storage.
  3. WordPress stores the payload as a site transient.
  4. Readers — including the Updates screen, wp_get_update_data(), and WP_Automatic_Updater — consume the transient.
  5. Stale transients persist until the next successful check or a forced refresh (see §1.6).

Reference

TransientSet byTypical contents
update_corewp_version_check()updates (offers), last_checked, version_checked, translations
update_pluginswp_update_plugins()response, no_update, checked, translations
update_themeswp_update_themes()Same pattern for themes

Developer resources


1.3 Core branch policy options (auto_update_core_*)

Site options control which branches of WordPress core may receive auto-updates when the WP_AUTO_UPDATE_CORE constant is not defined in wp-config.php. These options work together with filters and the per-offer auto_update_core filter (see §3.7).

How it works

When WP_AUTO_UPDATE_CORE is undefined, core reads site options inside branch policy logic in Core_Upgrader::should_update_to_version(). Values are typically enabled, disabled, or unset, with defaults from the schema and admin interface. These options live in the options table (site options on multisite) — they are not stored inside the update transients. For precedence with WP_AUTO_UPDATE_CORE and filters, see §5.10 and §3.7.

Process flow

  1. Core evaluates the running version branch: development, minor, or major.
  2. Core reads the matching auto_update_core_* option unless the constant overrides policy.
  3. Branch filters may change the boolean result.
  4. The result feeds should_update() together with the offer and other guards.

Reference

NameTypeDefaultEffect
auto_update_core_devsite optionPer schemaDevelopment or nightly-style builds on the current branch
auto_update_core_minorsite optionPer schemaMinor releases within the same x.y branch
auto_update_core_majorsite optionPer schemaMajor releases; the WordPress 5.6+ interface maps choices here

Developer resources


1.4 The autoupdate and disable_autoupdate offer flags

API responses attach flags to each offer. These flags determine whether an item is a candidate for unattended updates before per-item filters run.

How it works

When WordPress.org answers a plugin or theme update-check request, each entry in the response map is an offer object. Plugins and themes with an Update URI header can receive offers from update_plugins_{$hostname} or update_themes_{$hostname} instead; those objects may also include autoupdate and disable_autoupdate.

An autoupdate boolean signals that the build is eligible for unattended auto-update from the directory or third-party provider’s perspective. In WP_Automatic_Updater::should_update(), for plugins and themes, core first sets the pending decision from autoupdate, then merges per-item site option opt-ins (auto_update_plugins, auto_update_themes) when auto-updates are enabled for the type. If autoupdate is empty and the site administrator has not opted the item in, the pending decision stays false.

The disable_autoupdate property forces the pending decision to false before apply_filters( "auto_update_{$type}", … ) runs, but the filter may still return true to re-enable the update.

These flags are not durable site settings. They arrive on each HTTP response or from a filter callback. The transient stores whatever the last check returned. Refresh offers with wp_update_plugins() or wp_update_themes() before relying on offer objects for policy.

Process flow

  1. Discovery returns offers, each carrying optional autoupdate and disable_autoupdate flags.
  2. should_update() combines the flags with opt-in arrays and filters.
  3. A false outcome skips automatic installation for that item unless a filter overrides the decision.

Reference

FlagMeaning
autoupdateThe directory, third-party Update URI provider, or author signals eligibility for automatic update
disable_autoupdateVetoes the item before auto_update_{$type} runs; filters may override

Developer resources


1.5 Scheduled checks (WP-Cron)

WordPress schedules recurring update checks so discovery runs without a logged-in user. You need to understand cron behavior when DISABLE_WP_CRON or external schedulers replace the default mechanism.

How it works

wp_schedule_update_checks() runs on init and schedules three twicedaily events: wp_version_check calls wp_version_check(), wp_update_plugins calls wp_update_plugins(), and wp_update_themes calls wp_update_themes().

When DISABLE_WP_CRON is true, WordPress does not spawn the cron runner on normal page loads. Scheduled hooks — including wp_version_check, wp_update_plugins, wp_update_themes, and by chaining wp_maybe_auto_update — do not run unless something invokes wp-cron.php (system cron, monitoring, or WP-CLI). Admin-triggered checks described in §1.6 still run on those requests because they are not cron-based.

Process flow

  1. The init hook schedules events if they are not already scheduled.
  2. On each cron run, due events fire.
  3. Each handler calls the matching update function.
  4. Throttling inside those functions may skip the HTTP request if a recent check is still valid, unless the caller forces a refresh (§1.6).
  5. Failure leaves transients unchanged until the next successful run.

Reference

HookTypeParametersNotes
wp_version_checkcron eventCalls wp_version_check()
wp_update_pluginscron eventCalls wp_update_plugins()
wp_update_themescron eventCalls wp_update_themes()

Developer resources


1.6 Admin-triggered checks and throttle bypass

Loading specific admin screens forces fresher checks with shorter throttles than the default long backoffs. If you add a custom “check for updates” control, mirror these patterns.

How it works

Hooks on admin screens such as load-plugins.php, load-update.php, and load-update-core.php trigger plugin or theme checks with context-dependent timeouts. The Updates screen uses roughly a one-minute backoff, list tables use roughly one hour, and the default backoff is roughly 12 hours.

_maybe_update_plugins() and _maybe_update_themes() on admin_init apply a 12-hour backoff when last_checked is recent. _maybe_update_core() uses the same 12-hour window only when the transient’s version_checked still matches the running WordPress version — if the versions differ, a check runs immediately.

wp_version_check() accepts a second parameter $force_check. When true, the function skips the timeout against the stored update_core transient and issues a fresh HTTP request. wp_update_plugins() and wp_update_themes() accept $extra_stats; when that array is non-empty, the early-return path for “checked recently and nothing changed” is skipped, comparable to a forced check. A non-empty first argument to wp_version_check() can also set $force_check internally per wp-includes/update.php.

Note: Do not force checks on every cron or background request. Reserve forced checks for user-initiated or infrequent jobs to avoid excessive requests to WordPress.org.

Process flow

  1. An admin screen loads and its load hooks run.
  2. Update functions run with short timeouts or with $force_check and non-empty extra stats.
  3. The HTTP request returns new offers.
  4. WordPress refreshes the transients.
  5. The interface reflects new update counts.
  6. If the user lacks sufficient capabilities, WordPress displays limited messaging.

Reference

FunctionFileReturnsNotes
wp_version_check()wp-includes/update.phpvoidPass $force_check as true to skip transient backoff for core
wp_update_plugins()wp-includes/update.phpvoidNon-empty $extra_stats bypasses the “recently checked” path
wp_update_themes()wp-includes/update.phpvoidNon-empty $extra_stats bypasses the “recently checked” path
wp_clean_update_cache()wp-includes/update.phpvoidClears transients; pair with checks when you need a fresh HTTP request

Developer resources


1.7 API endpoints

Discovery depends on HTTP endpoints that return JSON offers. SSL availability affects transport and warnings.

How it works

Core sends POST requests to WordPress.org version and update-check endpoints. The URLs in source use http://; when wp_http_supports( array( 'ssl' ) ) returns true, WordPress upgrades the scheme to HTTPS.

The core version check sends query-string parameters plus a POST body that includes encoded translations. Plugin and theme checks POST JSON bodies listing installed components and locales. A separate checksums endpoint serves integrity verification only, not update offers (see §1.9). When SSL is unavailable, core may fall back to HTTP with user-visible warnings in some failure paths.

Process flow

  1. An update function builds the request body.
  2. WordPress sends a POST request to the endpoint.
  3. WordPress decodes the response.
  4. On error, transients may remain stale or WordPress triggers fallback behavior.
  5. On success, WordPress updates the transient payloads.

Reference

URL path on api.wordpress.orgMethodPurpose
/core/version-check/1.7/POSTCore offers; query arguments plus body (such as translations)
/plugins/update-check/1.1/POSTPer-plugin offers
/themes/update-check/1.1/POSTPer-theme offers
/core/checksums/1.0/GETPath-to-hash map for a version and locale (§1.9)

Developer resources


1.8 Third-party update sources (Update URI)

Plugins and themes can declare an Update URI header so that metadata arrives from a host other than WordPress.org. This extends discovery without replacing the core transient model.

How it works

For plugins and themes that declare Update URI, core applies dynamic filters update_plugins_{$hostname} and update_themes_{$hostname}. This mechanism allows commercial or custom directories to supply update metadata. Per-entity auto-update interface preferences are stored in site options and described in §5.4 and §3.8.

Process flow

  1. Discovery runs.
  2. Core resolves the hostname from the Update URI header.
  3. The matching filter may inject or alter offers.
  4. WordPress merges results into the standard site transients.
  5. Automatic updater logic in Part 3 uses those offers like any other.

Reference

HookTypeParametersNotes
update_plugins_{$hostname}filterVariesThird-party plugin offers
update_themes_{$hostname}filterVariesThird-party theme offers

Developer resources


1.9 Core integrity verification (checksums)

Checksum verification compares installed files to expected hashes for a given WordPress version. This supports diagnostics and CLI tooling. It does not gate the automatic updater before auto-updates run. Package signature verification on downloaded ZIP files before install is a separate mechanism (§2.12).

How it works

The WordPress.org checksums API returns JSON whose checksums entry maps relative file paths to expected hashes for a core build and locale. Core fetches this map through get_core_checksums() in wp-admin/includes/update.php.

Core_Upgrader::check_files() in wp-admin/includes/class-core-upgrader.php loads checksums and compares md5_file() output to each expected hash, skipping paths under wp-content/. Site Health and upgrade paths may use the same API. The update_core discovery transient does not embed the checksum map — core retrieves it separately when needed.

The WP-CLI command wp core verify-checksums performs a similar integrity check from the command line. Verification is an integrity audit. It is not the same as Ed25519 package signature checks on downloaded ZIP files (§2.12) and does not serve as a blanket pre-flight gate for WP_Automatic_Updater.

Process flow

  1. A tool or function requests checksums for a specific version and locale.
  2. Core compares installed files against the hash map.
  3. A mismatch indicates modified or missing core files.
  4. The outcome informs administrators but does not by itself control the automatic updater pipeline.

Reference

FunctionFileReturnsNotes
get_core_checksums()wp-admin/includes/update.phparray<string, string> or falseFetches the path-to-hash map; returns false on failure
Core_Upgrader::check_files()wp-admin/includes/class-core-upgrader.phpboolCompares ABSPATH files to the map

Developer resources


1.10 Exclusions: must-use plugins and drop-ins

Must-use plugins and drop-ins live outside the normal plugin and theme discovery pipelines. They never appear in WordPress.org update check payloads and are not upgraded through the standard update interface or automatic updater paths for ordinary plugins and themes.

How it works

Must-use plugins load from wp-content/mu-plugins/ (and nested PHP files where supported). Core does not enumerate them like regular plugins in wp-content/plugins/, so wp_update_plugins() does not retrieve offers for them from WordPress.org.

Drop-ins (for example advanced-cache.php, db.php, and object-cache.php) are single files or small sets of files placed directly under wp-content/ and registered through get_dropins(). They are not packaged as directory plugins; core has no built-in “update this drop-in from the directory” flow.

Replacing must-use plugins or drop-ins requires manual file deployment, a custom deployment pipeline, or tooling outside the native update mechanisms.

Process flow

  1. Discovery runs for standard plugins and themes.
  2. Must-use plugins and drop-ins are excluded from the offer model.
  3. An administrator or automation tool copies new files into mu-plugins/ or wp-content/ (often through version control or CI/CD).
  4. Core does not record version metadata in the update transients for these components.
  5. Must-use plugins continue loading early in bootstrap; drop-ins take effect when present and valid.

Reference

ArtifactDiscoveryApplication
Must-use pluginsNot listed in the standard plugin update API flowManual or external deployment
Drop-insNot listed as installable packagesManual or external deployment

Developer resources


Author

Photo de Quentin Le Duff

Quentin Le Duff

Quentin Le Duff is a WordPress developer for ten years, Opquast® Expert & ISTQB certified. He works with WordPress every day: developing open source plugins and themes, hosting and maintaining his clients’ websites, and writing about the WP ecosystem.