For years wp_set_cookie() in WordPress meant one thing: the WP Consent API’s wrapper that set a cookie only if the visitor had consented to that category. On 15 September 2026 the plugin, on 200,000 sites, removed that function to make room for a core function of the same name — and core’s version has a different signature and no consent check at all. The core patch is still an open pull request, so right now the gate is gone and the replacement has not arrived. If your code calls wp_set_cookie($name, $value, 'marketing'), read this before your next update.
We wrote last week about the WP Consent API failing open: with no CMP registered, wp_has_consent() returns true for everything. This is the same plugin, a different problem, and it arrived on 15 September.
What changed
The WP Consent API shipped 2.1.0 on 15 September 2026, 8:35 GMT. Its changelog entry is one line:
Change: internally unused
wp_set_cookie()function dropped in favor of upcoming WordPress corewp_set_cookie()function.
“Internally unused” is accurate about the plugin itself — nothing inside it called the function. It says nothing about the themes and plugins that did.
Two functions, one name, different contracts
This is the whole issue, so here they are side by side. The old one, read from the plugin’s 2.0.1 source:
// WP Consent API =< 2.0.1
function wp_set_cookie( $name, $value = '', $consent_category = '', $expires = 0,
$path = '', $domain = '', $secure = false, $httponly = false ) {
// ...
if ( wp_has_consent( $consent_category ) ) {
setcookie( $name, $value, $expires, $path, $domain, $secure, $httponly );
}
}
The third argument is a category string, and the setcookie() call is inside a consent check. No consent, no cookie. Pass an empty category and it fires _doing_it_wrong() telling you to name one.
Now the one proposed for core, read from pull request 12444:
// WordPress core, proposed
function wp_set_cookie( string $name, string $value, array $options = array() ): bool {
// defaults include 'samesite' => 'Lax'
// ...
if ( ! apply_filters( 'send_cookie', true, $name, $value, $options ) ) {
return false;
}
// ...
}
The third argument is an options array. There is no consent check anywhere in it. It always sets the cookie unless a filter says otherwise. It is a good function — it forces SameSite=Lax by default, returns a bool, and adds the send_cookie and wp_set_cookie_options filters — but it is not the same function, and it does not do the thing the old one existed to do.
Where that leaves each version
We did not run any of this: there is no PHP or WordPress on the machine this was written on. What follows is read from the source of both functions, which is why the exact declarations are quoted above rather than paraphrased.
- Plugin 2.1.0 (current), core unchanged.
wp_set_cookie()is no longer defined by anything. Code that calls it hits an undefined function. - Plugin ≤ 2.0.1, core unchanged. The old behaviour still works. This is what most sites still running an older version have today.
- Plugin ≤ 2.0.1 the day core lands this. Both declarations are unguarded — neither is wrapped in
function_exists(); we checked both files. Core loads first, the plugin then redeclares, and PHP stops with “Cannot redeclare wp_set_cookie()”. Pinning the old plugin version is therefore not a safe long-term answer. - Plugin 2.1.0 with core landed. The call resolves to core’s function. Passing
'marketing'as the third argument passes a string where an array is declared, which is aTypeError. If you had instead updated the call to core’s shape, the cookie is set — consent or not.
Note the stage: the core side is an open pull request against trunk, tied to Trac #37000, carrying @since x.y.z with no release assigned. We checked trunk’s wp-includes/functions.php today: it defines neither wp_set_cookie() nor wp_remove_cookie(). Nothing has shipped in core. The plugin change, by contrast, is live on 200,000 installs.
What to do now
1. Find the calls.
grep -rn "wp_set_cookie(" wp-content/themes wp-content/plugins
2. Stop relying on the name for consent. Make the gate explicit where you set the cookie, so it does not matter whose function wins:
if ( function_exists( 'wp_has_consent' ) && wp_has_consent( 'marketing' ) ) {
setcookie( 'my_cookie', $value, array(
'expires' => time() + MONTH_IN_SECONDS,
'path' => COOKIEPATH,
'secure' => is_ssl(),
'httponly' => true,
'samesite' => 'Lax',
) );
}
Keep in mind what that check returns when no CMP is registered, which is the failure mode we measured last week: true, for every category.
3. When core lands, move the gate into the filter. The send_cookie filter is the right seam for a site-wide rule, because it sees every cookie core sets rather than only the ones you remembered to wrap:
add_filter( 'send_cookie', function ( $send, $name, $value, $options ) {
if ( str_starts_with( $name, 'mkt_' ) ) {
return function_exists( 'wp_has_consent' ) && wp_has_consent( 'marketing' );
}
return $send;
}, 10, 4 );
4. Do not assume the plugin gates anything by itself. Installing the WP Consent API never blocked a cookie on its own; it published a category and a question. Something has to answer the question, and something has to obey the answer.
The wider point
A consent mechanism that lives in a function name is one rename away from disappearing, and the disappearance is silent: no error, no banner change, just a cookie that now gets written. The only way to know which of your cookies still depends on it is to look at what the site actually writes, from the outside — tracing each cookie back to the code that set it is the exercise.
A plugin update can remove a consent check without changing a single line of your own code. Scan your site to see which cookies are written before consent with CookieInspector.