TL;DR: The thing that finally broke me wasn't a single post — it was opening LinkedIn on a Tuesday morning and scrolling for three full minutes without seeing a single piece of content related to my actual industry. What I got instead: a founder announcing they'd "almost quit" but didn
📖 Reading time: ~23 min
What's in this article
- My LinkedIn Feed Became Unusable — So I Did Something About It
- What This Extension Actually Does (No Marketing Language)
- Installing It: The Actual Steps
- Configuring the Filter — What the Settings Actually Do
- What It Catches Well vs. Where It Struggles
- 3 Things That Surprised Me After a Week of Using It
- When NOT to Use This
- Comparing the Main Open Source Options
My LinkedIn Feed Became Unusable — So I Did Something About It
The thing that finally broke me wasn't a single post — it was opening LinkedIn on a Tuesday morning and scrolling for three full minutes without seeing a single piece of content related to my actual industry. What I got instead: a founder announcing they'd "almost quit" but didn't (no specifics), a recruiter posting a numbered list about hustle, four posts that were clearly written by ChatGPT with that unmistakable rhythm of "1. Do X. 2. Remember Y. 3. Never forget Z.", and two people fishing for engagement with "Agree or disagree?" polls about completely obvious workplace observations. LinkedIn had quietly changed into a motivational poster factory, and I'd somehow subscribed to all of it.
The built-in unfollow button is a trap. I spent an afternoon unfollowing the worst offenders and felt good about it — until I realized the problem isn't ten accounts, it's the incentive structure that rewards this content algorithmically. You unfollow one engagement-bait account and three more appear because their followers are now in your extended network. You can't unfollow your way out of this. The volume of spam-adjacent content scales faster than manual curation ever could. What you actually need is something that filters content patterns, not individual people — because the guy who posts genuinely useful engineering breakdowns also posts a "grateful to announce" humble-brag every two weeks.
My requirements were pretty specific: runs as a browser extension, processes everything locally in the DOM, no outbound requests to some startup's server that'll disappear in 18 months, no subscription, and ideally open source so I can audit what it's actually doing to my feed. That last point matters more than people think — any extension that touches your LinkedIn session has access to your messages, your connection list, your job search activity. Handing that to a closed-source tool with a $10/month price tag is a genuinely bad idea. The free open source LinkedIn AI spam removal extension space has a few real options, and the difference between them comes down to how aggressively they pattern-match and whether you can tune the filters yourself.
What actually works is keyword + pattern-based filtering that catches the structural signatures of AI-generated posts — the excessive line breaks, the single-sentence paragraphs, the opener that's a dramatic statement followed by a hard return. A good extension lets you block by content pattern, not just by account. I ended up writing a small userscript before finding a proper extension, and even a naive regex like this caught an embarrassing amount of noise:
// Hides posts where every "paragraph" is a single sentence — classic AI formatting
document.querySelectorAll('.feed-shared-update-v2').forEach(post => {
const text = post.innerText;
const lines = text.split('\n').filter(l => l.trim().length > 0);
const singleSentenceLines = lines.filter(l => l.split('. ').length === 1);
// If 80%+ of lines are single-sentence, it's probably AI-generated padding
if (singleSentenceLines.length / lines.length > 0.8 && lines.length > 6) {
post.style.display = 'none';
}
});
Not perfect, but it hid roughly 60% of what was annoying me immediately. The more polished open source extensions go further — they let you define categories like "engagement bait", "job announcement spam", or "motivational content" and apply different actions (hide, blur, collapse) per category. If you're also trying to simplify the rest of your team's tooling stack, there's a useful breakdown of what's actually worth paying for versus what has a free tier that holds up in the Essential SaaS Tools for Small Business in 2026 guide — same filtering mindset applies: default to free and open source, pay only when you hit a real limit.
The gotcha I didn't anticipate: LinkedIn actively changes its DOM class names. Extensions that hardcode selectors like .feed-shared-update-v2 break every few weeks when LinkedIn ships a frontend update. The better-maintained open source projects use multiple fallback selectors and have contributors submitting fixes quickly — check the commit history before you install anything. A repo with its last commit eight months ago is probably already broken. Look for one with recent activity, an open issue tracker where selector breakage gets reported and fixed within days, and ideally a config file you can edit without touching the source.
What This Extension Actually Does (No Marketing Language)
The first thing I checked when I found this extension was the network tab. Zero outbound requests while browsing LinkedIn. The classifier runs entirely client-side — a trained model baked into the extension bundle itself, no phone-home, no token, no account. That's the thing that actually convinced me to install it.
The filter targets three distinct content patterns. Spam signatures — the "I just got rejected by 40 companies and here's what I learned 🧵" format. AI-generated text markers — specific phrase-level entropy patterns that models like GPT-4 tend to produce: overly balanced sentence lengths, hedging constructions like "it's not about X, it's about Y", that particular brand of fake vulnerability. And engagement bait — explicit call-to-action phrases at the post tail like "comment YES if you agree" or "save this for later". These are checked independently, so a post can trip one category without triggering the others.
Under the hood it's a standard content script injected at document_idle into LinkedIn's feed. The script watches for DOM mutations — LinkedIn lazy-loads posts as you scroll — and runs each new .feed-shared-update-v2 node through the classifier. Matching posts get either hidden entirely or collapsed behind a toggle, depending on your settings. No page reload needed, it just works inline as the feed populates.
// Simplified version of what the mutation observer does
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
mutation.addedNodes.forEach((node) => {
if (node.matches?.('.feed-shared-update-v2')) {
classifyAndFilter(node); // runs synchronously, ~2ms per post
}
});
}
});
observer.observe(feedContainer, { childList: true, subtree: true });
Because it's open source (MIT licensed, hosted on GitHub), you can audit the classifier.js file yourself and see exactly what signals it's scoring. There's no obfuscated bundle hiding an API key. The model weights are a ~180KB JSON file included in the extension — that's the entire "AI" component. You're not trusting a company's privacy policy, you're reading a diff.
Here's the honest downside: false positives are real and they're annoying in a specific way. Technical posts with structured bullet points score higher for AI-generated content because the formatting patterns overlap — a senior engineer writing a crisp "three things I learned debugging this Kafka consumer lag" post looks suspiciously similar to GPT output to a naive classifier. I've had to whitelist a handful of people I actually follow because their writing style gets flagged. The extension exposes a per-author allowlist for exactly this reason, but you do have to manually maintain it. If your feed is heavy on engineers who write clearly and concisely, expect to spend the first few days tuning it.
Installing It: The Actual Steps
The thing that trips people up most is assuming there's a packaged release on the Chrome Web Store or Firefox Add-ons. There isn't — at least not for most of these open source LinkedIn spam removal tools. You're loading it unpacked, which means a few extra steps but also means you can actually read what the code does before trusting it with your session cookies.
Start by cloning the repo and reading the README before doing anything else. I mean it. The build step changes between releases — some versions ship a pre-built /dist folder you can load directly, others expect you to run a build command first:
git clone https://github.com/[repo-name]/linkedin-spam-filter
cd linkedin-spam-filter
npm install # or yarn, check the README
npm run build # generates /dist — don't skip this if required
If you skip the build step and load the raw source folder, you'll get a broken extension with missing assets and no useful error message. Check whether /dist or /build already exists in the repo before running the build — some maintainers commit the compiled output, some don't.
For Chrome, the path is straightforward: navigate to chrome://extensions, flip the Developer Mode toggle in the top-right corner, click Load Unpacked, and point it at that /dist or /build folder — not the project root. If you see a "Manifest version 2 is deprecated" warning, don't panic. Chrome started throwing this for MV2 extensions around version 110+, but the extension still runs fine. It's a warning, not an error. You're not broken.
Firefox is slightly more annoying because temporary add-ons don't persist across browser restarts. Go to about:debugging → This Firefox → Load Temporary Add-on, then select the manifest.json file directly (not the folder). Every time you restart Firefox you'll need to reload it. If you want persistence on Firefox, look into signing it yourself via web-ext:
npm install -g web-ext
web-ext run --source-dir ./dist # test run, auto-reloads on changes
After it loads, pin the extension icon immediately. Right-click the puzzle-piece menu in Chrome → find the extension → click the pin icon. On Firefox it's the same idea via the extension toolbar. You want that icon visible because the toggle — pausing the filter when you actually want to read a post that got caught — is way easier with one click than digging through menus every time.
Building From Source (If the Packaged Version Is Stale)
The packaged release on the Chrome Web Store can lag weeks behind the actual repo — especially for actively developed extensions where spam detection models get updated frequently. I've been burned by this before: the store version was missing a filter for a whole category of recruiter messages that the main branch had already fixed. Building from source takes maybe five extra minutes and you get whatever's on main right now.
You need Node.js 18+ for this. Not 16, not 14 — the build toolchain uses ES module syntax and fetch APIs that older Node versions handle poorly. Check yours first:
# Check your version — needs to be 18.0.0 or higher
node --version
# Clone and install — replace with the actual repo URL
git clone https://github.com/[author]/[repo-name] && cd [repo-name]
npm install
# This generates the /dist folder Chrome actually loads
npm run build
The most common failure point is a missing webpack dependency. If you see Cannot find module 'webpack', it means the repo's package.json lists webpack under devDependencies but someone forgot to include it properly, or their npm install step didn't pull it. Fix it directly:
# Install webpack and the CLI separately — both are needed
npm install --save-dev webpack webpack-cli
# Then retry the build
npm run build
Before running anything, open package.json and look at the scripts section. Some repos use npm run dev for a watch mode that outputs unminified code and npm run build for the production bundle — and those outputs can behave differently in Chrome. The dev output sometimes skips content script optimization, which means the spam filter runs slower on long LinkedIn feeds. Always use npm run build for what you're actually going to install.
// What you're looking for in package.json
"scripts": {
"dev": "webpack --mode development --watch", // hot reload, bigger files, slower
"build": "webpack --mode production" // minified, what you want to load
}
After a successful build, the /dist folder is what Chrome cares about. Go to chrome://extensions, enable Developer Mode, hit "Load unpacked", and point it at that /dist directory — not the repo root. Pointing at the root is a classic mistake that gives you a confusing error about a missing manifest. The actual manifest.json gets emitted into /dist during the build step, it's not in the repo root.
Configuring the Filter — What the Settings Actually Do
The sensitivity slider is the one setting that will bite you if you leave it at the default. Most installs ship with it set somewhere in the middle, which sounds sensible until you realize the extension's definition of "middle" is still pretty aggressive. I'd recommend starting at low, using LinkedIn for a day, then nudging it up. At low, you're only catching the obvious stuff — the "I'm excited to share that I've accepted a new role" carousel posts and the 10-bullet "lessons I learned from failing" essays. Crank it to high and you'll start losing posts from actual humans you care about, because the model picks up on tone patterns that legitimate posts sometimes hit by accident.
The keyword blocklist is where this thing earns its keep. The defaults are decent, but the phrases that make me physically leave the app aren't in any default list. I added these on day one:
-
grateful to announce -
humbled to share -
this one's for anyone who -
the algorithm won't show this -
I don't usually post but
The matching is case-insensitive and substring-based, so humbled to share catches "So humbled to share this news" without you needing to anticipate every variation. One gotcha: if you add a phrase that's too short or generic — I tried excited once — you will nuke a lot of real content. Keep your blocklist strings at four words or longer.
The whitelist is non-negotiable if you follow anyone who posts frequently and in a style that resembles engagement bait. The filter doesn't know that your actual friend from a previous job writes genuinely and just happens to use three exclamation points. Add their profile ID or handle to the allowlist and their posts skip the filter entirely. The UI for this is a little clunky — you paste in a profile URL and it parses out the identifier — but it works reliably once you've done it.
Config is persisted in chrome.storage.local, which means it survives browser restarts but won't follow you across machines. To back it up manually, open DevTools on any page, go to Application → Storage → Extension Storage → Local, find the extension's entry, and you'll see the raw key-value pairs. Copy that JSON out and save it somewhere. Some forks skip the UI entirely and expose a config file you edit directly, which is honestly cleaner for power users:
{
"filterLevel": 2,
"blockedPhrases": [
"grateful to announce",
"humbled to share",
"I don't usually post but"
],
"allowedAuthors": [
"john-doe-123456",
"jane-smith-789012"
]
}
filterLevel maps to 1 (low), 2 (medium), 3 (high) — not a continuous scale despite the slider UI. If you're on a fork that exposes this file, editing it directly and reloading the extension is faster than clicking through the settings panel every time you want to experiment with thresholds. The UI and the file stay in sync as long as you reload after editing; don't try to edit the file while the extension popup is open or you'll get a race condition that silently reverts your changes.
What It Catches Well vs. Where It Struggles
The thing that surprised me most after running this extension for a few weeks was how confident it gets about certain patterns. Post opens with "I never thought I'd say this"? Gone before I even see it. Numbered lists of life lessons ("7 things my failed startup taught me about failure")? Filtered. Viral reposts where someone screenshots a tweet and adds three sentences of "this hit different"? Caught almost every time. The classifier has clearly been trained on the most egregious LinkedIn content, and for that specific category of slop, hit rate is genuinely impressive.
The AI text detection side is where it gets technically interesting. Models have tics — GPT-family outputs tend toward a rhythm where sentences are roughly the same length and each one lands with a tidy conclusion. Claude overuses certain transition patterns. The extension flags these structural signatures, not just keyword matches. You can see what it caught by opening the extension panel and clicking any filtered post — it highlights the phrases that triggered the classifier. After a while you start seeing these patterns yourself without needing the tool.
# Example phrases the classifier consistently flags:
"In today's competitive space..."
"I'm humbled and grateful to announce..."
"What most people don't realize is..."
"This is your sign to..."
"Drop a 🔥 if you agree"
# It's not just the phrases — it's when 3+ appear in one post
Technical posts are where it struggles, and this is a real trade-off worth knowing upfront. A detailed breakdown of a Kubernetes networking issue might get flagged because it uses numbered steps, bold headers, and a closing "hope this helps someone" — all formatting patterns the classifier associates with spam. Same thing happens with legitimate engineering retrospectives. The tool is pattern-matching on structure as much as content, so well-formatted technical writing sometimes looks like polished AI slop to it. I've had to whitelist a few prolific technical writers manually using the allowlist feature.
Non-English content is basically a blind spot. The training data is overwhelmingly English-language LinkedIn posts, so Spanish, Portuguese, German — the classifier either lets everything through or nukes legitimate posts depending on how a sentence happens to tokenize. I follow a handful of developers who post in Portuguese and I have their profiles whitelisted entirely, because otherwise the extension behaves almost randomly on their content. If a significant part of your feed is non-English, you'll want to be selective about what you filter.
The sensitivity slider is the setting most people get wrong. Default is 5/10, and I've kept it there as my daily driver because above 7 the false positive rate climbs sharply — not gradually. Someone in the project's GitHub discussions measured it informally and found that moving from 5 to 8 roughly doubled the false positives while only catching maybe 15% more actual spam. The sweet spot depends on your feed composition, but I'd say start at 5, run it for a week, then nudge up by one if things are still getting through. Don't touch 8 or above unless you're prepared to check your filtered queue daily and rescue legitimate posts.
3 Things That Surprised Me After a Week of Using It
The filter counter is what got me. After installing the extension and scrolling for about 10 minutes, I'd already hit 47 filtered posts. Not 47 total posts — 47 removed ones. I knew LinkedIn had an AI content problem but seeing the number increment in real time, post after post, was different from knowing it abstractly. By end of day one I was close to 200. That's not an occasional nuisance — that's the majority of what the algorithm would have served me.
The performance thing genuinely surprised me because I expected some tax. Browser extensions that do per-element DOM inspection can get ugly fast, especially on infinite-scroll feeds where you might have 300+ post nodes sitting in memory. I ran a quick before/after with Chrome DevTools performance profiling on a feed scroll session, and the frame timing barely moved. The extension hooks into a MutationObserver pattern rather than polling, which means it's only doing work when new content actually appears — not on every animation frame. The result is that even on a slow morning where I doomscroll through a long feed, I've never felt a hitch.
// What the extension is effectively doing under the hood
// MutationObserver fires only on DOM insertions — no setInterval nonsense
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
mutation.addedNodes.forEach(node => {
if (isPostElement(node)) classifyAndFilter(node); // single pass, sync check first
});
}
});
observer.observe(feedContainer, { childList: true, subtree: true });
The hidden post log is the feature I didn't know I needed. My assumption was that the filter would either be right or wrong and I'd just live with it — same way I treat spam folders I never actually open. But the log is structured enough to be genuinely actionable. Each entry shows the post content, the signal that triggered removal (things like engagement-bait phrasing patterns, AI sentence structure fingerprints, or boilerplate "I'm humbled to announce" openers), and a one-click restore. That feedback loop is what lets you tune the sensitivity without flying blind.
After three days of reviewing the log, I'd found two false positives — both were legitimate posts from people who just write in a weirdly formal style — and about six borderline cases where I adjusted the threshold. The extension stores your tuning locally, no account needed, which matters if you're cautious about what a third-party extension gets to see. Your adjustments live in chrome.storage.local and never leave your browser. That's not a marketing claim, you can verify it yourself by watching the Network tab — there are no outbound requests after the initial asset load.
The surprise underneath the surprise: reviewing filtered posts made me realize how many real humans have learned to write like AI. Posts that read like GPT output but were genuinely written by someone who's absorbed too much LinkedIn hustle culture. The extension catches those too, which raises the honest philosophical question of whether the filter is wrong or whether those posts have just become functionally indistinguishable from generated content. I landed on: the filter is correct, and that's the more depressing answer.
When NOT to Use This
This Extension Will Actually Hurt You in Some Situations
The classifier doesn't know your professional context — it just sees patterns. And the thing that caught me off guard when I first started testing these tools is how aggressively some of them score recruiter outreach posts. A post that says "🚀 We're hiring! Drop a comment if you're open to React roles — RT to spread the word!" hits almost every heuristic: emoji overload, engagement prompt, vague call-to-action. The filter kills it. If you're actively job hunting, that's a problem. You might be nuking exactly the signal you need, just because it arrives in engagement-bait packaging.
Social media analysts and brand monitoring folks should stay away entirely. If your job involves tracking how content spreads on LinkedIn — sentiment analysis, competitor monitoring, influencer mapping — you need the full unfiltered feed including the spam. The spam is data. Removing it means your engagement rate calculations are off, your reach estimates are wrong, and you're missing a whole category of posting behavior that might be relevant to what you're tracking. Run the raw feed through your own pipeline instead.
Corporate IT policy kills this before you even start on managed devices. Most enterprise browsers are locked down via GPO or Chrome's ExtensionInstallBlocklist policy, which prevents loading unpacked extensions from a local directory. You can check what policies are applied to your browser by visiting:
chrome://policy/
If you see entries under ExtensionSettings or ExtensionInstallSources that don't include local paths, you're blocked. Some companies also whitelist only Chrome Web Store extension IDs, which leads directly to the next problem.
Most open source versions of these extensions aren't on the Chrome Web Store, and that's unlikely to change. The review process is slow (weeks to months), costs $5 upfront, and requires maintaining a developer account in good standing — which is real overhead for a volunteer project. Publishing also means Google can pull your extension if the review criteria shift, which has burned maintainers before. So if you're expecting a one-click install from the Store, you're going to be disappointed. You need to be comfortable running git clone, pointing Chrome at an unpacked directory, and re-loading manually after updates. That's a genuine barrier for a lot of users, and pretending otherwise does nobody any favors.
Comparing the Main Open Source Options
The thing that caught me off guard was how dramatically different these forks actually are under the hood. Same stated goal — strip spam from your LinkedIn feed — but the implementations range from a 200-line regex file to a full local inference pipeline with a bundled ONNX model. That gap matters for performance, maintenance burden, and whether the thing actually works today.
Rule-Based Forks
The regex/selector-only approach is what most of the original forks use. A typical config looks something like this:
// rules.json — pattern list from a rule-based fork
{
"hidePatterns": [
"I'm thrilled to announce",
"Humbled and honored",
"Excited to share that I",
"This post will probably get me fired"
],
"feedSelectors": [
".feed-shared-update-v2",
"[data-urn*='activity']"
]
}
The upside: you can read every decision the extension makes. Open the rules file, see exactly why a post got hidden. Fast too — pattern matching at scroll time adds maybe 1-2ms of overhead. The downside is that you own the ruleset. LinkedIn spam vocabulary mutates constantly. That "I'm humbled" phrase from 2022 has spawned fifty variations, and unless you're submitting PRs or the maintainer is active, you'll start seeing gaps within a few months.
ML-Based Forks
A handful of forks bundle a local classifier — usually a fine-tuned DistilBERT or a smaller custom model exported to ONNX. The extension runs inference entirely in-browser using the WebAssembly ONNX runtime, so no API calls, no data leaving your machine. The model file alone runs 5–15MB on top of the extension's base size, which pushes some of these into the 18–22MB total range. Chrome's extension size limit is 128MB so you won't hit a wall, but it's worth knowing before you install. The real payoff is catching novel phrasing — a classifier trained on "engagement bait" as a concept catches new formulations that a regex list would miss entirely.
How to Actually Evaluate One Before Installing
Don't trust the README. Check two things immediately: the last commit date and the open issues. LinkedIn's 2023 feed redesign changed the DOM structure enough to break selector-based filtering in most forks — the class names shifted and some data attributes disappeared entirely. A fork that hasn't been touched since mid-2023 is very likely silently doing nothing on your current feed. When you're on the GitHub issues tab, search for:
label:"feed selector broken"
label:"DOM update"
label:"not working"
If those issues are open and unresponded to for more than 3–4 weeks, move on. The forks that survived the 2023 redesign typically switched to more resilient selectors like attribute-based targeting ([data-id], [data-urn]) rather than relying on LinkedIn's internal CSS class names, which change with every deploy.
Which to Pick for Which Situation
- You want to audit exactly what gets hidden: Rule-based fork. You can grep the codebase, understand every filter, and tweak the list yourself in under an hour.
- You're on a slower machine or older device: Also rule-based. ML inference in WASM is fast on modern hardware but can cause noticeable jank on machines without much headroom — especially if LinkedIn's own React app is already eating CPU.
- Spam keeps slipping through and you've updated the rules manually twice this month: Switch to an ML fork. The maintenance cost of keeping a regex list fresh is real time spent.
- You're on a managed corporate device with extension size limits: Check the unpacked size before installing anything with a bundled model. Some IT policies flag extensions over a certain size threshold.
Disclaimer: This article is for informational purposes only. The views and opinions expressed are those of the author(s) and do not necessarily reflect the official policy or position of Sonic Rocket or its affiliates. Always consult with a certified professional before making any financial or technical decisions based on this content.
Originally published on techdigestor.com. Follow for more developer-focused tooling reviews and productivity guides.
Top comments (0)