WordPress 7.2 Secrets API: Stop Storing API Keys in Plain Text WordPress 7.2 will ship a Secrets API that stores plugin credentials as encrypted, versioned objects instead of plain-text strings in the options table, with release candidate 1 due November 17 and final release December 8, according to the official Make WordPress Core documentation. An audit of one WordPress 7.1 installation with 48 active plugins found 56 API keys and tokens stored as plain text, 36 of them autoloaded on every page request, 48 present in a six-month-old database dump, and 15 AI service keys still active when tested. The API uses XChaCha20-Poly1305 (libsodium) with a four-tier key hierarchy rooted in a WP_SECRETS_KEY constant in wp-config.php, exposes four functions (wp_set_secret, wp_get_secret, wp_delete_secret, wp_import_option_as_secret), and keeps CURRENT and PREVIOUS version slots per secret for rotation without downtime. An audit of a single WordPress 7.1 installation with 48 active plugins turned up 56 API keys and tokens sitting in the options table as plain text. Thirty-six of them were autoloaded on every page request. Forty-eight appeared in a database dump from six months prior — still sitting unencrypted on the server. Fifteen of the AI service keys were still active when tested. That is not a misconfiguration story. That is how WordPress has worked for two decades. WordPress 7.2 ships a Secrets API that replaces this pattern with encrypted, versioned credential storage. RC1 lands November 17. Final release is December 8. If you write plugins, here is what you need to know before your users upgrade. What the Secrets API Actually Does The core idea is simple: instead of a string sitting in wp options , you get a WP Secret object. The object prints as secret:my-plugin/api-key in logs, in var dump output, in JSON responses, and in REST API calls. The actual value only surfaces when you call - reveal on it explicitly. Serializing or cloning the object throws an exception. Under the hood, WordPress uses XChaCha20-Poly1305 libsodium with a four-tier key hierarchy. The root key is stored encrypted, derived from a WP SECRETS KEY constant you add to wp-config.php . The encrypted credential sits in the options table — same location as before — but now it is ciphertext, not a string your database admin can read. Two version slots exist per secret: CURRENT and PREVIOUS . Writing a new value moves the old one to PREVIOUS , giving you credential rotation without a service interruption window. The Four Functions You Need The API surface is narrow by design. The full official documentation is on Make WordPress Core https://make.wordpress.org/core/2026/11/11/the-secrets-api-in-wordpress-7-2/ . - wp set secret 'my-plugin/api-key', $value — stores encrypted; returns true or WP Error - wp get secret 'my-plugin/api-key' — retrieves a WP Secret object, null , or WP Error - wp delete secret 'my-plugin/api-key' — removes the stored credential - wp import option as secret 'old option name', 'my-plugin/api-key' — migrates existing plaintext options Secret names follow the convention plugin-slug/key-name — lowercase letters, digits, hyphens, and underscores, single slash, no leading or trailing hyphens. Names without a slash trigger doing it wrong . The slash is namespacing, not access control: any plugin on the site can read any secret. Write Your Plugin’s Getter Correctly The right pattern uses feature detection so your plugin runs on 7.1 and earlier while taking advantage of the new API on 7.2+: function my plugin get api key : string { // Allow ops to override via wp-config.php if defined 'MY PLUGIN API KEY' && MY PLUGIN API KEY { return string MY PLUGIN API KEY; } // Use Secrets API on WordPress 7.2+ if function exists 'wp get secret' { $secret = wp get secret 'my-plugin/api-key' ; if $secret instanceof WP Secret { return string $secret- reveal ; } // WP Error means the secret exists but cannot be decrypted. // Do NOT fall back to get option here. if is wp error $secret { return ''; } } // Legacy fallback for pre-7.2 installs return string get option 'my plugin api key', '' ; } The return value of wp get secret has three states, not two, and you must handle all three. WP Secret means success. null means the secret was never stored — show your setup screen. WP Error means the secret exists but cannot be decrypted, usually because the encryption key changed. Falling back to get option on a WP Error is the worst possible mistake: you silently bypass encryption for a credential that was deliberately migrated away from plain-text storage. Migrating Existing Plugins wp import option as secret handles the common case: you have an option with a plaintext key, and you want to move it to encrypted storage without requiring users to re-enter it: function my plugin migrate to secrets api : void { if function exists 'wp import option as secret' { return; } $legacy = get option 'my plugin api key' ; if is string $legacy || '' === $legacy { return; } $result = wp import option as secret 'my plugin api key', 'my-plugin/api-key' ; if is wp error $result { return; } // Verify the migration before deleting the original $secret = wp get secret 'my-plugin/api-key' ; if $secret instanceof WP Secret && hash equals $legacy, string $secret- reveal { delete option 'my plugin api key' ; } } add action 'admin init', 'my plugin migrate to secrets api' ; Run it on admin init . Existing installs migrate silently on the first admin page load after upgrading to 7.2. The hash equals check is not paranoia — it is the guard against deleting the legacy option before confirming the encrypted copy matches. What Changed in Beta 5 Breaking If you tested against an earlier beta, re-test now. Beta 5 November 10 introduced two breaking changes https://developer.wordpress.org/news/2026/11/whats-new-for-developers-november-2026/ that affect anyone who already built against the API. First, WP Secrets Keyring , WP Secrets Store , and WP Secrets Provider are now abstract classes, not interfaces. Any secrets.php drop-in that uses implements WP Secrets Keyring will throw a fatal error on 7.2. Update to extends . Second, WP SECRETS KEY in wp-config.php is now mandatory. The earlier fallback to LOGGED IN KEY and LOGGED IN SALT is gone. If you have secrets stored under the old fallback key on a beta site, you need to perform a three-step re-encryption sequence before RC1. The WordPress hosting team has the procedure https://make.wordpress.org/hosting/2026/11/12/call-to-test-wordpress-7-2-secrets-api/ . New installs get WP SECRETS KEY generated automatically. Existing sites will see a “Recommended” notice in Site Health until it is added. Generate the key with: php -r 'echo base64 encode random bytes 32 , "\n";' Start Now RC1 lands in two days. The final release ships December 8. The adoption path is clear: add function exists guards, handle all three return states, run the import on admin init . The options table was never a secrets manager. Everyone knew it. Nobody fixed it. WordPress 7.2 fixes it — use it.