Back to articles

Turn extension uninstalls into feedback with one line of code

chrome.runtime.setUninstallURL opens a page after someone uninstalls your extension. Here's how to turn that uninstall moment into structured churn feedback.

Maxim Kosterin
5 min read
A jigsaw puzzle-piece lifting off the paper, leaving a soft orange watercolor imprint where it sat — the uninstall moment turned into feedback.
A jigsaw puzzle-piece lifting off the paper, leaving a soft orange watercolor imprint where it sat — the uninstall moment turned into feedback.

Most extension developers learn about an uninstall the same way: the active-user count ticks down by one. No reason, no context, no chance to respond. The user is already gone.

But the browser gives you exactly one more moment with them — and almost nobody uses it.

The method nobody reaches for

The WebExtensions Management API includes a quiet little method called chrome.runtime.setUninstallURL. The official description is one sentence: it "specifies the URL to direct the user to after uninstall."

That's it. When a user removes your extension, the browser opens the URL you registered in a new tab. It's the uninstall equivalent of an exit interview — a single, reliable signal fired at the exact moment a user decides to leave.

You set it once, from your background service worker:

chrome.runtime.setUninstallURL("https://example.com/goodbye")

The hard part isn't the API. It's everything behind the URL: a page to host, a form to build, somewhere to store responses, and a way to read them back. That's enough friction that most developers never bother — which is why the uninstall moment is such underused signal.

Point it at a hosted page instead

You don't need to build any of that. Point the URL at an Extenshi-hosted feedback page and the whole back half is handled for you:

chrome.runtime.setUninstallURL(
	"https://uninstall-form.extenshi.io/<code>",
)

On uninstall, the browser opens a branded survey, the user picks a reason from a fixed taxonomy — too buggy, found an alternative, privacy, price, missing features — and the response lands in your developer cabinet. No server, no database, no form code. One line.

Tag the version they left on

Add one more piece and the data gets much sharper. Append the running version so every response is attributed to the exact build the user was on when they bailed:

chrome.runtime.setUninstallURL(
	"https://uninstall-form.extenshi.io/<code>?v=" +
		chrome.runtime.getManifest().version,
)

Now a bad release stops being invisible. Instead of a vague dip in your user count, you see a cluster of uninstalls all tagged with the same version — the clearest possible signal that the build you shipped on Tuesday is the one driving people away.

Why structured beats free-text

It's tempting to just open a blank "tell us why you left" box. Don't. Free text is unsortable, unaggregatable, and mostly empty.

A fixed set of reasons makes churn comparable: you can watch the "too buggy" share fall after a stability release, or the "too expensive" share jump after a pricing change. That's the difference between feedback you can act on and feedback you skim once and forget.

Closing the other end of the loop

The uninstall page is one half of the extension lifecycle. The other half is the install. The same Extenshi tooling generates a hosted welcome page driven by chrome.runtime.onInstalled — one snippet opens a branded onboarding page on first install (never on update), with getting-started steps and confirmed-install trends by version. Between the two, you see your users arrive and you hear why they leave.

What you can — and shouldn't — put in the URL

The uninstall URL is a plain GET, so you can only attach what fits in a query string. The version tag above is the high-value one. An extension ID and a slug to route the response are fine too. Resist the urge to staple on anything that identifies the person — emails, account IDs, anything you scraped from the page they were last on.

There are two reasons. First, the browser fires this URL outside any consent the user gave to your extension, so passing personal data through it is exactly the kind of quiet data flow I keep flagging in my post on extension privacy policies and data selling. Second, you don't need it — a churn reason tagged with a version is more useful than a name attached to a shrug.

Keep in mind this is a chrome.runtime method, so it lives in the same lifecycle surface I broke down in runtime tracking vs permissions. It runs without any extra permission in the manifest — there's no uninstall permission to request — which is part of why it stays so quietly under-used.

FAQ

Does uninstalling Chrome remove the extension's uninstall URL? The URL only fires when the extension is removed, not the browser. If someone uninstalls Chrome itself, no tab opens — there's no running browser left to open it.

Does setUninstallURL work in Firefox? Yes, with one caveat. Firefox supports browser.runtime.setUninstallURL through the same WebExtensions API, so the one-liner is cross-browser — but Firefox doesn't persist the URL the way Chromium does. Chrome and Edge remember it across updates and service-worker restarts; Firefox needs it re-set on every background-script run, so call setUninstallURL on each startup rather than exactly once. Firefox also won't open the URL if the extension was disabled before it was removed. Safari is the holdout — its extension model doesn't expose the method at all.

Can I read the data the user typed before they left? No. The uninstall URL is the only signal you get, and it's a one-way GET. You can't reach back into the removed extension's storage — it's already gone. Whatever you want to know has to be encoded in the URL or asked on the page it opens.

Get started

Build and brand your survey in the cabinet at dojo.extenshi.io/tools/uninstall-feedback — it generates the exact URL and snippet for your extension, ready to paste (full setup walkthrough here). Configuring it takes a verified claim on the extension, the same one you use for free security scans.

It's free, and it sits next to the rest of the developer toolkit — the @extenshi/cli scanner, the manifest and privacy-policy generators, and a one-time free scan-and-read allowance you can top up with prepaid packs when you outgrow it.

One line of background code, and the next user who leaves tells you why.

Related Articles