Converting a Chrome extension to Safari and iOS: the complete guide
Apple ships a command-line tool that converts a Chrome extension into a Safari one. Run it, and you get an Xcode project that compiles on the first try. This is where most teams get the wrong idea about how the rest of the port will go.
I’ve done this work twice at production scale — at Honey, where I built the company’s first iOS browser extension and ported the legacy Safari extension to Apple’s modern API, and at Pie, where I owned Safari and iOS for an ad blocker with two million users. This guide is the map I wish I’d had: what the converter actually does, what breaks, and what the real work looks like.
What the converter actually does
Safari has supported the WebExtension API — the same standard Chrome extensions are built on — since Safari 14. The converter wraps your existing extension in the packaging Safari requires:
xcrun safari-web-extension-converter /path/to/your-extensionThat produces an Xcode project containing a small native app with your extension embedded inside it. This is the first structural difference from Chrome: a Safari extension is not a standalone artifact you upload to a store. It ships inside a Mac or iOS app, and users get it from the App Store, then enable it in Safari’s settings.
The converter copies your JavaScript, your manifest, and your assets. It does not check whether the APIs you call actually exist in Safari. Everything compiles; whether it works is a different question, answered API by API.
What breaks, by category
1. Request blocking and modification
This is the big one. If your extension blocks or rewrites network requests with the blocking webRequestAPI, that capability does not exist in Safari. Safari’s model is declarativeNetRequest: you hand the browser a list of rules and it applies them, without your JavaScript ever seeing the request. That’s a redesign, not a translation — your dynamic blocking logic has to be re-expressed as declarative rules, and anything that genuinely required inspecting requests at runtime needs a different approach or has to be cut. Ad blockers, privacy tools, and anything with custom filtering live in this category. (I wrote more about this migration in the webRequest article.)
2. Background script lifecycle
Safari does not run persistent background pages. If your extension assumes its background script lives forever — in-memory state, long-lived timers, an open WebSocket — it will break in ways that look random: the script gets suspended, state evaporates, events arrive with nobody listening. The fix is the same discipline Manifest V3 pushed on Chrome: event-driven code, state in storage, nothing important living only in memory. If you already survived Chrome’s MV3 migration, you’ve done most of this work.
3. API coverage gaps
Safari implements a large subset of the WebExtension API, not all of it. The pattern that costs teams weeks: an API exists as a property (so feature detection passes) but does nothing, or behaves subtly differently. Storage quotas differ. Some tabs and windows behaviors differ, especially on iOS where the whole windowing model is different. The only trustworthy answer is an audit of every API your extension touches against the Safari version range you intend to support — which is precisely the first deliverable of a port assessment.
4. The native layer nobody on a JS team signed up for
Past the JavaScript, you now own an Xcode project: bundle identifiers, entitlements, provisioning profiles, code signing, and a containing app that Apple expects to do something more than exist. None of this is hard for someone who does it weekly. All of it is alien to a JavaScript team, and it’s where ports stall — the extension “works” but nobody can produce a signed build that installs on someone else’s machine. Set up CI (Xcode Cloud or GitHub Actions with proper signing) early, not as a finishing touch.
iOS: the same codebase, a different product
Since iOS 15, iPhones and iPads run real Safari Web Extensions — your same WebExtension code, in the same wrapper project. This is the single biggest reason to port: your competitors’ Chrome extensions have zero presence on the phone. But treat iOS as its own target. The popup you designed for a 1440-pixel desktop becomes a popover on a 390-pixel phone. Touch replaces hover. Some APIs available on macOS are missing or behave differently. And your containing app now faces iOS App Review, which is stricter than the Mac’s. Plan a real iOS QA pass on device — the simulator won’t catch everything.
App Store review, for extension people
Your extension ships inside an app, so it goes through App Review. The rejections I’ve seen most, at Honey and Pie and since:
- The containing app does too little. Apple wants the app to have some purpose — at minimum, clear instructions and state for enabling the extension. A blank window gets flagged.
- Permissions without explanation. If your extension wants access to all websites, the review notes need to say why, in plain language, and the app should explain it to users too.
- Privacy labels that don’t match reality. Your App Store privacy questionnaire has to agree with what the extension actually collects. Analytics SDKs people forgot about are a classic trap.
- Metadata mentioning other browsers. Keep the store listing about the Safari product.
Review times are usually a day or two now, but a rejection loop can eat weeks if you’re learning the guidelines by trial and error. Writing honest, thorough review notes up front is the cheapest insurance there is.
Maintaining both after launch
The port isn’t the end state; two storefronts are. The setup that works: keep one WebExtension codebase with a thin, well-isolated Safari layer, feature-detect rather than fork, and automate the Apple release path so shipping to Safari doesn’t depend on the one person with a working Xcode setup. Budget for Safari’s annual changes — new macOS and iOS versions move extension behavior more than Chrome updates do.
When not to port
An honest section, because it’s a real outcome: if your extension’s core value depends on capabilities Safari doesn’t offer — deep request inspection, APIs Apple doesn’t implement, or Chrome-specific surfaces — a port produces a worse product under your brand, and you shouldn’t ship it. Roughly one in five extensions I assess gets a “don’t port, and here’s why” answer. That answer costs a week and saves a quarter.
The short version
- The converter gives you packaging, not compatibility.
- Blocking
webRequestis the most common redesign; persistent background state is the most common source of weird bugs. - iOS is the prize, and it’s a real second target.
- The Apple toolchain — signing, CI, App Review — is where JS teams lose the most time, and the easiest part to hand to someone who lives there.
Questions clients ask
Can every Chrome extension be ported to Safari?
Most can, but not all as a straight translation. Extensions built on blocking webRequest, complex background lifecycles, or Chrome-only APIs need parts redesigned around Safari’s model. A small number — those whose core feature depends on something Safari deliberately doesn’t allow — shouldn’t be ported at all, and it’s better to know that in week one.
Do I need a Mac and an Apple Developer account?
Yes to both. The converter and Xcode only run on macOS, and distribution goes through the App Store, which requires an Apple Developer Program membership ($99/year). Budget a few days for Apple to approve a new developer account.
How long does a real port take?
A simple extension with no request blocking: often 4–6 weeks to shipped-and-approved. A content blocker or anything built on webRequest: 6–10 weeks, because the blocking layer gets redesigned. The wide range is exactly why I audit before quoting.
Does the same code run on macOS and iOS Safari?
Largely yes — Safari Web Extensions share the WebExtension codebase across both, wrapped in one app project. But iOS has real differences: popovers behave differently than desktop popups, some APIs are unavailable, and testing on device matters. Treat iOS as its own QA target, not a free checkbox.
Want the answer for your extension specifically?
The Safari Port Assessment does everything in this guide against your actual codebase: a full compatibility catalog, a DNR migration plan, and a fixed quote — in one week, for $2,500.