DEV Community

Cover image for Chrome Rejected Us Twice. Firefox Approved Instantly. Edge Is Still Thinking
Eugeniya Ivanova
Eugeniya Ivanova

Posted on

Chrome Rejected Us Twice. Firefox Approved Instantly. Edge Is Still Thinking

You have a few extensions installed right now, and you probably can't name them all. An ad blocker, a password manager, something you installed three years ago for one specific task that has been quietly living there ever since. That's normal: the average Chrome user has eight to twelve installed and actively uses two or three.

The whole point of an extension is to remove a context switch. Instead of opening a new tab, finding the service, logging in, and pasting the link, you get one icon right where you already are. The value is in skipping those five little steps, which is why extensions can look trivial until you start using one.

That was my situation. I write articles and then share them across social accounts: finish the piece, open our product in a new tab, copy the link, pick the accounts, send. Five context switches. Enough that I'd often leave it for later.

I wanted one button, so we built an extension. Click the icon and it pulls in the URL of the page you're on. You write your post and send it to the accounts you've picked without leaving the tab. The whole thing weighs seventeen kilobytes.

For scale: the Chrome Web Store held 283,585 extensions as of July 18, 2026, against 30,509 in Edge in mid-June and roughly 83,000 in Firefox earlier this year. Chrome is nearly ten times the size of Edge, which matters later. Meanwhile, extension trackers put the median at about eighteen users, with seventy percent sitting at a hundred or fewer. Everyone sees the giants, but a typical extension is used by a few dozen people.

Launching wasn't the interesting part. Over about two weeks, we went through three stores and collected two rejections and one HTTP 400 that locked out half the team. That's what this article is about, along with the actual wording we got.

What's inside

Manifest V3, a popup, and a small amount of logic. The extension reads a token from the logged-in tab of our product, exchanges it for an API key, and stores it in chrome.storage, so nobody has to copy anything by hand.

Here's the manifest we ended up with:

{
"manifest_version": 3,
"name": "...",
"version": "0.2.3",
"permissions": ["storage"],
"action": { "default_popup": "popup.html" }
}

More interesting is what's missing.

Chrome turned us down twice

The first rejection came back as Spam and Placement / excessive keywords. In the description, we had listed all ten platforms the extension works with — LinkedIn, X, Instagram, and the rest — so readers would know where they could post. The automated filter read it differently.

Chrome's policy defines keyword spam as irrelevant or excessive keywords in a description used to manipulate ranking. On paper, we fit the definition: ten recognizable brand names in a row inside a short description look like an attempt to influence search, regardless of intent.

When your product genuinely supports ten networks, an honest list of them can look exactly like keyword spam. We fixed it by generalizing: "all your connected social accounts" instead of the full list, with no brand names at all.

The second rejection arrived as Use of Permissions. We had requested tabs but only ever called chrome.tabs.create, which doesn't require the tabs permission and works fine without it. It was a textbook case: permission requested, permission unused.

The policy requires the narrowest permissions necessary for your features, and the automated checks are good at spotting a gap between the manifest and the code. We dropped tabs and kept storage.

An iteration later, we added an "insert this page" feature, and that one needed activeTab — the narrow version of the permission we'd been rejected for. It's granted when the user clicks, applies only to the active tab, and the store has no problem with it. The lesson was simple: your manifest should contain exactly what your code actually calls.

Firefox: declare your data, get approved immediately

There was no rejection here, just a validator error for a missing property. Since November 3, 2025, every new extension has had to declare its data collection in the manifest through browser_specific_settings.gecko.data_collection_permissions. If you collect nothing, you still have to say so explicitly with none. Without that declaration, the build doesn't get signed.

"browser_specific_settings": {
"gecko": {
"data_collection_permissions": {
"required": ["authenticationInfo"]
}
}
}

After that came automated screening and almost immediate approval. Firefox was easily the fastest.

Edge: still waiting

Technically, it's the same Chromium and the same extension, but the differences start before you get anywhere near the code. The developer program won't accept a work or school account, so we had to register a personal Microsoft account in an incognito window to stop the browser from helpfully supplying the corporate one.

Then the validator said "manifest missing." The problem turned out to be the archive: manifest.json has to sit at the root of the ZIP rather than inside a nested folder. We repacked it flat with zip -j, and it went through.

Then we waited.

We got published, uploaded an update, and now we're waiting again. At least the current version stays available while the update is under review. Users keep running what's already live.

The HTTP 400 that only worked for me

The team went to test the extension, and every one of them hit HTTP 400 on auto-connect. It worked fine for me.

First, we ruled out authentication. A broken or empty token returns 401, and we were getting 400. The token was being accepted; something else in the request was being rejected.

Then came a false lead. While digging through the web app bundle, we found that it sends both an access token and a refresh token, while the extension was only sending the first. It looked like the answer. We fixed it. It didn't help.

Then I realized why it only worked for me. The code had a guard along the lines of if (apiKey) return. My key was already sitting in the cache, so the mint request never fired. I hadn't touched the branch that was failing for everyone else in months.

{ "error": "Maximum of 10 active API keys allowed" }

The extension minted a fresh key on every connect. Over a week of testing, people had accumulated ten keys and hit the ceiling.

That cost us most of a day and taught us two things. Never diagnose a 400 without reading the response body — the status code tells you the request was rejected, and only the server knows why, but it will usually tell you if you ask. And "works on my machine" can simply mean you have a cached result while the broken path only runs on a clean install.

A few other things came up along the way. Store review doesn't test your integration: the reviewer has no account in your product, so functional bugs sail through, because what's being checked is policy, permissions, and the listing — a green status only means you didn't break the store's rules. Screenshots have to show your real interface, since invented marketing graphics get spotted. Trader verification is its own saga, with Organization type, a D-U-N-S number, and a name and address that have to match the registry character for character or you get "no match." And Edge has its own asset dimensions and its own promotional tile, which can't include Chrome branding.

So was it worth it?

The extension is tiny, but it solves a problem I have every day, which is probably the best reason to build anything. If your product lives in a browser, an extension like this takes about a week to put together. Most of that time goes into working out what each store wants, and all three want different things.

Our extension is two days old. Now we'll see whether anyone else finds it as useful as we do.

We're digging further now, toward an add-on that would let you post straight from the document or deck you're working in. That's a different marketplace with different rules, so I suspect there'll be a part two.

Here's the link if you want to try it: Publora — Schedule Social Posts.

Have you shipped anything to the extension stores? What did they reject it for?

Top comments (0)