There's no guarantee such message will be sent because Chrome won't wait for it when closing the tab. It worked for you in the past because you probably used a persistent background script in ManifestV2 i.e. there was no "persistent": false, and you lucked out that the message was sent just in time before Chrome terminated the process.
One reliable solution is to use chrome.tabs.onRemoved in the background script. To detect navigation to a different page/site, you can use chrome.tabs.onUpdated or chrome.webNavigation.onCommitted. Of course, depending on what specifically you want to do in the background script, it may be nontrivial or inefficient to use this approach.
Another reliable solution is to keep an open chrome.runtime port: when the tab is closed or navigated away the port's onDisconnect will be fired in the background script. In ManifestV3 you'll have to reopen it every five minutes, otherwise the service worker will be terminated. Although it will consume some memory, but the overall effect would be positive because restarting the service worker for every unload event hundreds of times a day is much more wasteful and, in case of a portable device, is bad for the battery life.
content script:
reconnect();
function reconnect(port) {
port?.onDisconnect.removeListener(reconnect);
chrome.runtime.connect({name: 'unload'})
.onDisconnect.addListener(reconnect);
}
background script:
chrome.runtime.onConnect.addListener(port => {
port.timer = setTimeout(onTimer, 270e3, port);
port.onDisconnect.addListener(onUnload);
}
});
function onTimer(port) {
port.onDisconnect.removeListener(onUnload);
port.disconnect();
}
function onUnload(port) {
clearTimeout(port.timer);
console.log('diconnected', port.sender);
}