I’m building a Chrome extension (Manifest V3) that intercepts file download links (e.g., .zip, .exe, .mp4, etc.) on pages like thank-you or success pages, and redirects them to my desktop app (Fetchify) instead of letting Chrome download them directly.
I’m successfully sending the download URL to my app, but Chrome still starts downloading the file, even though I’m calling e.preventDefault() and e.stopPropagation() in my content script.
My goal:
Intercept download links and:
1. Prevent Chrome’s native download
2. Send the file URL to my local app via HTTP POST
What’s working:
-> The content script detects download links.
-> The download URL is sent to my local app (http://localhost:12345/api/download).
What’s not working:
-> Chrome still downloads the file natively, even after interception.
My manifest.json :
{
"manifest_version": 3,
"name": "Fetchify Downloader",
"version": "1.0",
"description": "Send downloads directly to the Fetchify app from your browser.",
"background": {
"service_worker": "background.js"
},
"permissions": [
"downloads",
"contextMenus",
"activeTab",
"scripting"
],
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["autoDownloadScript.js"],
"run_at": "document_idle"
}
],
"host_permissions": ["<all_urls>"],
"icons": {
"16": "icons/icon16.png",
"32": "icons/icon32.png",
"48": "icons/icon48.png",
"128": "icons/icon128.png"
}
}
My autoDownloadScript.js :
(function () {
const downloadExtensions = /.(zip|exe|mp4|mp3|pdf|iso|rar|7z|msi|deb|apk|tar.gz|docx?|xlsx?|pptx?)$/i;
function sendToFetchify(url) {
console.log("[Fetchify] Auto-detected download:", url);
fetch("http://localhost:12345/api/download", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ url })
})
.then(res => {
if (res.ok) console.log("✅ Sent to Fetchify.");
else console.warn("⚠️ Rejected by Fetchify.");
})
.catch(err => console.error("❌ Fetchify error:", err));
}
function interceptLinkClick(e) {
const anchor = e.target.closest("a");
if (!anchor || !anchor.href) return;
const url = anchor.href;
if (downloadExtensions.test(url)) {
e.preventDefault(); // Stop Chrome default download
e.stopPropagation();
console.log("[Fetchify] Intercepted link click:", url);
sendToFetchify(url);
}
}
function preventDefaultOnMatchingLinks() {
const links = document.querySelectorAll("a[href]");
for (const link of links) {
if (downloadExtensions.test(link.href)) {
link.addEventListener("click", e => {
e.preventDefault();
e.stopPropagation();
sendToFetchify(link.href);
});
}
}
}
function tryAutoDetectDownloadLinks() {
const links = Array.from(document.querySelectorAll("a[href]"));
for (const a of links) {
if (downloadExtensions.test(a.href)) {
console.log("[Fetchify] Auto-detected static link:", a.href);
sendToFetchify(a.href);
break; // Only handle one
}
}
}
// Intercept user clicks early (for dynamic DOM)
document.addEventListener("click", interceptLinkClick, true);
// Prevent static links from triggering download
window.addEventListener("DOMContentLoaded", preventDefaultOnMatchingLinks);
// Auto-detect on thank-you/success pages
window.addEventListener("load", () => {
const url = window.location.href.toLowerCase();
if (url.includes("thank-you") || url.includes("success")) {
setTimeout(tryAutoDetectDownloadLinks, 1500);
}
});
})();
What I’ve tried:
-> Using e.preventDefault() and e.stopPropagation() in both capturing and bubbling phases.
-> Adding both DOMContentLoaded and click interception.
-> Matching <all_urls> and running on document_idle.
Questions:
1. Is there a reliable way to prevent the browser from downloading the file when the link is clicked?
2. Should I be handling this at a different level (e.g., background script or with chrome.webRequest)?
3. Is there a way around this in Manifest V3 or any known workaround?