I recently upgraded my device from IOS 18.4 to IOS 26. My web extension has disapeared from safari. I can see it in Settings > Apps > Safari > Extensions and when I turn it on and re-open safari. I just get a mesasge that says "{extension name} is no longer avaiable". I have tried Manifest V2 and Manifest V3 both yield the same results. The current production extension bundled with the IOS app has the same problem. I can no longer use or test my own extension !? Help please !
Safari Extensions
RSS for tagEnhance and customize the web browsing experience on Mac, iPhone, and iPad with Safari Extensions
Posts under Safari Extensions tag
111 Posts
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
I’m a developer working on a Safari Web Extension that’s distributed via the App Store and also tested locally through Xcode. I’m running into an issue that’s affecting my ability to debug errors reported to my Sentry error logging instance from production.
The Problem
When an error is thrown in one of my extension scripts (e.g., background.js, popup.js, or content.js), the error is sent to Sentry but the captured JavaScript error stack trace replaces the file paths with the webkit-masked-url://hidden placeholder like this:
ReferenceError: Cannot access uninitialized variable.
at ? (webkit-masked-url://hidden/:14677:28)
at ? (webkit-masked-url://hidden/:16307:3)
This happens consistently across both App Store builds and local Xcode runs. It prevents me from seeing which script the error came from or resolving the actual source code lines using uploaded source maps in Sentry.
My Setup
Safari Version: 18.5 (Stable on macOS)
Distribution: App Store and local Xcode development
Extension Type: Safari Web Extension
Error Reporting: Sentry (@sentry/browser SDK)
Bundler: Webpack with inline-source-map
What I’ve Confirmed
I can see the actual source files in Safari’s Web Inspector under the Sources tab when the extension is running.
My source maps are uploaded to Sentry correctly and are associated with the matching release.
Errors from Safari are being captured by Sentry, but the file URLs are masked, so stack traces cannot be resolved against my original source.
My Question
Is this behavior (masking file URLs in stack traces with webkit-masked-url://hidden/) intentional for Safari Web Extensions?
If so, is there any supported method or workaround to allow exception stack traces to reveal the original script path (e.g., popup.js, background.js) so tools like Sentry or even console logs can point to real locations? I fully understand the privacy/security rationale behind the masking, but as the extension developer, this is making it extremely difficult to debug runtime issues in production.
I’d really appreciate any insight into:
Whether this masking is expected and permanent behavior
If there are any entitlements, debug settings, or Info.plist keys that can alter this behavior for development or for trusted/own extensions
If Apple recommends a different way to log extension errors that includes script name or source references
Thanks in advance for your help! I’m happy to share more technical details or try out suggestions.
In a Safari Web Extension using Manifest V3, how can a content script access an HTML file that is bundled with the extension (e.g., to inject it as an iframe)?
Safari's CSP seem to prevent the use of browser.runtime.getURL() in the MAIN world — is there a recommended way to load such resources securely?
Is there any supported mechanism in Safari Web Extensions (MV3) for capturing or logging network request data (like fetch, XHR, or webRequest) triggered by the web page?
Calling SFContentBlockerManager.reloadContentBlocker from related App extension intermittently fails
I have an app which has at least two extensions:
A Content Blocker extension with a request handler that returns an appropriate NSExtensionItem as part of beginRequest. A different file URL is returned depending upon if the content blocking is on or off by a user setting
A Safari Web Extension that includes a toolbar button and popover that enables users to enable or disable the ad blocking of the content blocker extension
All three targets (App, Content Blocker appex and Web Extension appex) use an App Group default to read and set the on or off status of the content blocking.
When the user changes the content blocking status, the app group default is updated and SFContentBlockerManager.reloadContentBlocker(...) is called.
The Content Blocker extension reads the default and then returns the appropriate file URL.
The issue is, I have noticed that whenever SFContentBlockerManager.reloadContentBlocker(...) is called from the app, Safari always applies the correct rules from the returned file URL.
However sometimes when SFContentBlockerManager.reloadContentBlocker(...) is called from the Safari Web Extension using native messaging, Safari does NOT apply the correct rules from the returned file URL.
Using logging I have confirmed that the Content Blocker extension always returns the appropriate file URL irrespective if called as a result of the app or the web extension.
Despite this, Safari does not seem to always apply the returned file URL rules when it is called from the Safari Web Extension appex. In these cases, quitting Safari and relaunching it seems to make it apply the rules correctly (obviously this is applying it due to its launch state, not due to the Web extension appex asking it to do so at that point).
All targets have access to the App Group location where the active content blocking file URL belongs and the inactive content blocking file URL is within the Safari content blocker target as a resource.
I don't think this is a memory status issue as I cannot see the Content Blocker extension being killed when it returns complex rules --- the fact it always works when called via the app also seems to rule this possibility out.
This brings up a number of questions:
Is calling SFContentBlockerManager.reloadContentBlocker(...) from a different appex, of the same app target and app group supported? (it seems to work sometimes and did work in previous versions of the app).
Is there an issue that the Content Blocker extension sometimes returns a file URL that perhaps the calling Web Extension appex may not have access to (even though Safari should via the Content Blocker extension)?
Any other ideas of why this may not be working correctly?
Has anyone else experienced this?
It seems to happen on both iOS and macOS Safari using the same codebase.
Even default Safari Web Extension project is not displayed on iOS 18.4 simulator ("No extensions installed"), so it's not possible to test extensions in simulator, only on real device.
Hi,
I’m working with the SFExtensionProfileKey in my Safari Web Extension. As I understand it, this key is to get the UUID of the profile currently in use. However, it seems to be missing (no key in userInfo) when the default profile is active. Also, I haven’t found any API to get a profile’s human-readable name or list all available profiles.
Could someone clarify:
If the value of SFExtensionProfileKey is absent, can I safely assume the default profile is in use?
Is there a supported way to get a profile’s display name?
Does Safari expose an API for getting all profiles?
Thanks in advance for your insights!
As with the adoption of MV3 standards among all major browser vendors that allow browser extensions at the client-side, I understand that this is the same with Safari as well, as mentioned here (https://www.wwdcnotes.com/notes/wwdc22/10099/). However, as with Firefox, browsers may choose to adopt them incompletely and with few changes. I had a few questions regarding how Safari views this transition and what would be the next steps from here.
Thus, it would be really great if the browser team could provide your insights on any or all of the following points:
Would Safari adopt the exact standards proposed by the Chromium ecosystem such as with functionalities like header-based modifications in the coming days.
What would be the general timeline be for this in general?
Does this also translate to the fact that existing standards with MV2 standards would not be allowed to operate any further, as with the timeline with Chromium?
Regards
The extension popups don't seem to support the dark mode media query.
The only way the query gets detected is when a color-scheme is added:
<meta name="color-scheme"content="light dark">
Hi!
I'm working on a web extension for Safari and I need to send messages from the containing application to JavaScript. For this I use the method
class func dispatchMessage(
withName messageName: String,
toExtensionWithIdentifier identifier: String,
userInfo: [String : Any]? = nil
) async throws
of the SFSafariApplication class. If the site is opened in Safari in normal mode, everything works as expected. However, if the site is "docked", the messages are not transmitted to this "Web App".
I've been unable to successfully get a webpage to send a message to a Safari web extension, no matter what I try doing.
I've added the following to my manifest.json file, and it's running manifest v3
{
"externally_connectable": {
"matches": [ "*://mywebsite.com/*", "*://localhost:3000/*" ]
}
}
My web page executes the following code snippet. I've tried this both while running my site locally (on localhost) and pushed to production.
let safariExtensionId = "co.companyname.productname.Extension (ABCD1234)"
browser.runtime.sendMessage(safariExtensionId, { greeting: "hello"},
function(response) {
console.log("Received response from background page");
console.log(response.farewell);
}
);
In the Safari web extension's background.js file, I've added the following onMessageExternal listener:
browser.runtime.onMessageExternal.addListener((message, sender, sendResponse) => {
console.log("Received message from the sender.");
console.log(message.greeting);
sendResponse({ farewell: "Goodbye!" });
});
This is directly copied from the instructions in this WWDC video:
https://developer.apple.com/documentation/safariservices/messaging-between-a-webpage-and-your-safari-web-extension
It's also extremely difficult to debug what's happening since the extensions service working frequently does not appear in the Web Extension Background Content menu
Is there something I'm doing wrong, or a bug I'm not aware of?
Hello - we have a Mac application that uses a browser extension and the web extension JS APIs to communicate with Safari. As of macOS 15.4 / Safari 18.4 the tab OnAttached and tab onDetached events are no longer received.
After some testing we verified that the events were working properly as of macOS 15.3 / Safari 18.3 but appear to have been broken in macOS 15.4. Note a similar issue was reported previously for Safari 17.6 and was fixed in macOS 15.0 (FB14324177).
We have made a TestFlight version of our app (Tabby) available to simplify debugging via https://testflight.apple.com/join/Va8Zdv9d.
To reproduce the issue:
Install the Tabby TestFlight build on macOS 15.4 or 15.4.1
Open Safari, go to Safari settings and select the Extensions tab
Enable the Tabby extension and grant permissions to all windows all the time
Open a Safari window with at least 3 tabs
Note the open window and tabs displayed in Tabby
In Safari, perform a tab detach by dragging a tab out of the window
Expected behavior
Within Safari the detached tab should now be in it’s own window, and via the onDetached event Tabby should update to show the tab in it’s own window AND removed from the original window.
Observed
Safari fails to send the onDetached event and Tabby will continue to display the detached tab in its original window in addition to the new window.
You can also use the repro steps above to observe the onDetached event being received or not by Tabby in the Safari developer console. The same steps but re-attaching the tab to the original window can be used to observe the onAttached event being received or not.
We’ve attached two screen recordings to the Feedback ID below, one showing the events working on macOS 15.3, and one showing the events failing to be received on macOS 15.4.1. Note it also fails on macOS 15.4.
FEEDBACK ID: FB17367977
Hi!
I'm working on a web extension for Safari and I need to send messages from the containing application to JavaScript. For this I use the method
class func dispatchMessage(
withName messageName: String,
toExtensionWithIdentifier identifier: String,
userInfo: [String : Any]? = nil
) async throws
of the SFSafariApplication class. If the site is opened in Safari in normal mode, everything works as expected. However, if the site is "docked", the messages are not transmitted to this "Web App".
Is it possible to somehow link the container application to the docked website so that messages from the application are received by this "Web App"?
That you.
We have an existing Safari App Extension distributed outside the App Store (self-distributed). Recently, we converted another browser extension to a Safari Web Extension and used the same bundle ID as the original application to avoid any change on the CX side.
After distributing this updated app, we noticed that the Safari extension was disabled on users' machines, and users are now required to manually re-enable it in Safari's preferences.
Is this the expected behavior and is there way to avoid this for future updates ?
I want to migrate from a Safari App Extension to a Safari Web Extension, but don't know how to get rid of the message, telling users that my extension can access their passwords. Here is a message which I see:
I was thinking that this might be because all Safari Web Extension get this type of access, but I have a Safari Web Extension which does not require such level of access:
Here is the manifest:
{
"manifest_version": 2,
"default_locale": "en",
"name": "__MSG_extension_name__",
"description": "__MSG_extension_description__",
"version": "1.1",
"icons": {
"48": "images/icon-48.png"
},
"background": {
"scripts": [
"background.js"
],
"persistent": true
},
"browser_action": {
"default_popup": "popup.html",
"default_icon": {
"16": "images/toolbar-icon-16.png"
}
},
"permissions": [
"nativeMessaging", "tabs"
]
}
and here is the Info.plist file:
Here is the entire code of the extension:
https://github.com/kopyl/web-extension-simplified
When our Safari Web Extension makes a api request from its background script (registered via "scripts" in manifest.json, e.g., "background": { "scripts": ["js/background.bundle.js"] }) to our authenticated API endpoint (https://api-domain/user), the Cookie header is not included in the request. This occurs only when the extension is running within a non-default Safari User Profile. This causes our API to treat the user as unauthenticated. The exact same extension code, manifest, and API call work correctly (Cookie header is present and user is authenticated) when the extension is running in the Default Safari User Profile.
There does not appear to be any way to use or create iCloud passkeys with a Safari Web Extension, either using the navigator.credentials API in an extension origin webpage such as the popover, or using the AuthenticationServices framework in the SafariWebExtensionHandler.
I've setup an associated domain for my plugin, and I know it works for the host application. But I get errors trying to do so in the web extension target.
createCredentialRegistrationRequests results in the following error:
Domain=com.apple.AuthenticationServices.AuthorizationError Code=1004 "Application with identifier <ID> is not associated with domain <RPID>
The other problem, assuming the entitlement works correctly for the web extension, is that there is no NSWindow to use as the presentation target from the SafariWebExtensionHandler.
Trying to use the navigator.credentials.create JS API (which is the preferred method, frankly, in a web extension) results in the following error:
NotAllowedError: The request is not allowed by the user agent or the platform in the current context, possibly because the user denied permission.
Chrome has a great solution for this that I believe should be adopted by Safari. If an extension has host permissions for a relying party it wants to claim, or if it has an associated domain entitlement for it, webauthn operations should be allowed.
updateDynamic rules is blocking ads on my device with iOS 17.4, but on my iOS 18 device the same code is not blocking ads.
Is this a known issue?
Hello,
According to the documentation:
If you provide your extension in macOS and don’t want to use the Mac App Store for distribution, you can sign and notarize your extension’s app with a Developer ID to distribute it outside the Mac App Store.
However, I found this to be untrue in practice. Even after signing and notarising the Safari extension correctly, it is not possible to enable it in Safari without turning on "allow unsigned extension".
This makes it impossible to distribute your Developer ID–signed and notarized extension outside the Mac App Store.
I would like to distribute my web extension directly to employees in my organization using MDM without having each user manually enable "allow unsigned extension" for it to work. Any way to make it work?
The documentation is quite confusing in this aspect, it says "Safari only supports signed extensions" but my extension is rejected even if notarised and signed.
使用direct distribution进行分发时,safari web extension 在safari setting 中显示没有权限读取、修改或传输任何网页的内容。
但是我在看公证日志显示插件是正常的公证的
这导致safari extension 无法使用。
公证日志
https://www.coupert.com/img/2025-04-10/notarization-log.json