Summary
For an apex host under a multi-label eTLD — example.co.uk, example.com.au, example.co.jp — ParamBuilder resolves the eTLD+1 to the public suffix itself (co.uk) and emits _fbp / _fbc with domain=co.uk. Browsers reject cookies scoped to a public suffix, so the cookies are never stored: every request mints a new browser id and CAPI receives no stable _fbp.
Single-label eTLDs (example.com) are unaffected, and the same host with a www. prefix is resolved correctly, which is probably why this has gone unnoticed.
Reproduction
<?php
require 'vendor/autoload.php';
foreach (['example.co.uk', 'www.example.co.uk', 'example.com'] as $host) {
$pb = new FacebookAds\ParamBuilder(array('https://' . $host));
$pb->processRequest($host, array(), array(), null);
foreach ($pb->getCookiesToSet() as $c) {
printf("host=%-22s %s domain=%s value=%s\n", $host, $c->name, $c->domain, $c->value);
}
}
Output with facebook/capi-param-builder-php 1.3.1 (and current main):
host=example.co.uk _fbp domain=co.uk value=fb.1.…
host=www.example.co.uk _fbp domain=example.co.uk value=fb.2.…
host=example.com _fbp domain=example.com value=fb.1.…
Expected for the first row: domain=example.co.uk.
Cause
In getEtldPlusOne(), the domain-list branch confirms the candidate is a suffix of the host and then requires the preceding character to be a dot:
if ($host[$lastOccurrence - 1] === '.') {
return $domain_candidate;
}
When the candidate is the host, $lastOccurrence is 0, so this reads $host[-1] — which since PHP 7.1 is the last character of the string, not a boundary check. It is 'k' for example.co.uk, the candidate is discarded, and control falls through to:
$slice = explode(".", $host);
if (count($slice) > 2) {
return substr($host, strpos($host, '.') + 1);
}
which strips the first label and yields co.uk. For a two-label host the fallback returns the host, which is why .com sites look fine.
Suggested fix
Accept the exact match in the domain-list branch:
if ($lastOccurrence === 0 || $host[$lastOccurrence - 1] === '.') {
return $domain_candidate;
}
That makes the configured domain list authoritative, as intended. The label-stripping fallback is still wrong for multi-label suffixes when no list is supplied, so it may be worth refusing to return a candidate that consists solely of a known public suffix.
Impact
facebook-for-woocommerce (3.7.6, latest) constructs the builder as new \FacebookAds\ParamBuilder( array( get_site_url() ) ) and passes $cookie->domain straight to setcookie(), so every WooCommerce store on a .co.uk-style apex domain is currently unable to persist _fbp or _fbc. It also hands the same domain to the browser in the consent-release path. Verified on a live store: set-cookie: _fbp=…; path=/; domain=co.uk.
Summary
For an apex host under a multi-label eTLD —
example.co.uk,example.com.au,example.co.jp—ParamBuilderresolves the eTLD+1 to the public suffix itself (co.uk) and emits_fbp/_fbcwithdomain=co.uk. Browsers reject cookies scoped to a public suffix, so the cookies are never stored: every request mints a new browser id and CAPI receives no stable_fbp.Single-label eTLDs (
example.com) are unaffected, and the same host with awww.prefix is resolved correctly, which is probably why this has gone unnoticed.Reproduction
Output with
facebook/capi-param-builder-php1.3.1 (and currentmain):Expected for the first row:
domain=example.co.uk.Cause
In
getEtldPlusOne(), the domain-list branch confirms the candidate is a suffix of the host and then requires the preceding character to be a dot:When the candidate is the host,
$lastOccurrenceis0, so this reads$host[-1]— which since PHP 7.1 is the last character of the string, not a boundary check. It is'k'forexample.co.uk, the candidate is discarded, and control falls through to:which strips the first label and yields
co.uk. For a two-label host the fallback returns the host, which is why.comsites look fine.Suggested fix
Accept the exact match in the domain-list branch:
That makes the configured domain list authoritative, as intended. The label-stripping fallback is still wrong for multi-label suffixes when no list is supplied, so it may be worth refusing to return a candidate that consists solely of a known public suffix.
Impact
facebook-for-woocommerce(3.7.6, latest) constructs the builder asnew \FacebookAds\ParamBuilder( array( get_site_url() ) )and passes$cookie->domainstraight tosetcookie(), so every WooCommerce store on a.co.uk-style apex domain is currently unable to persist_fbpor_fbc. It also hands the same domain to the browser in the consent-release path. Verified on a live store:set-cookie: _fbp=…; path=/; domain=co.uk.