The chrome.debugger API allows Chrome extensions to interact with the Chrome DevTools Protocol (CDP), enabling network traffic interception. This is useful for monitoring, logging, or modifying network requests and responses in real-time.
Ensure your manifest.json includes the necessary permissions:
{
"manifest_version": 3,
"name": "Network Traffic Interceptor",
"version": "1.0",
"permissions": ["debugger", "storage"],
"host_permissions": ["https://www.google.com/*"],
"background": {
"service_worker": "service-worker.js"
},
"action": {}
}
In your service worker (service-worker.js), attach the debugger to the active tab:
async function attachDebugger(tab) {
if (!tab || !tab.id) return;
if (!tab.url.startsWith("http")) {
console.error("Debugger can only be attached to HTTP/HTTPS pages.");
return;
}
const debuggee_id = { tabId: tab.id };
try {
await chrome.debugger.detach(debuggee_id);
} catch (e) {
// Ignore if not attached
}
try {
await chrome.debugger.attach(debuggee_id, "1.3"); // https://chromedevtools.github.io/devtools-protocol/
await chrome.debugger.sendCommand(debuggee_id, "Network.enable", {});
console.log("Network interception enabled.");
} catch (error) {
console.error("Failed to attach debugger:", error);
}
} // Google's boilerplates: https://github.com/GoogleChrome/chrome-extensions-samples/blob/main/api-samples/
// usage: Attach on action click
chrome.action.onClicked.addListener(async (tab) => {
await attachDebugger(tab);
});
Set up event listeners for network events:
const pending_requests = new Map();
chrome.debugger.onEvent.addListener(function (source, method, params) {
if (method === "Network.responseReceived") {
// Store request ID for later retrieval
pending_requests.set(params.requestId, params.response.url);
}
if (method === "Network.loadingFinished") {
const request_id = params.requestId;
const url = pending_requests.get(request_id);
if (!url) return;
pending_requests.delete(request_id);
chrome.debugger.sendCommand(
source,
"Network.getResponseBody",
{ requestId: request_id },
function (result) {
if (chrome.runtime.lastError) {
console.error(
`Failed to get response body: ${chrome.runtime.lastError.message}`
);
return;
}
if (result && result.body) {
const body = result.base64Encoded ? atob(result.body) : result.body;
console.log(`Response from ${url}:`, body);
// Process the response body here
}
}
);
}
});
manifest.json and service-worker.js files as shown above.chrome://extensions/.This error stems from Network.getResponseBody and is symptomatic of the following common causes:
Network.enable must be called before requests are made. If called after, existing request IDs are invalid.Network.enable resets the domain state, invalidating previous IDs.getResponseBody before loadingFinished.Mitigation:
chrome.action.onClicked) to prevent multiple or conflicting Network.enable commands. Each Network.enable resets the Network domain state, clearing buffers and invalidating existing request IDs. Calling it redundantly or out of sequence can cause state resets mid-session, leading to the "No resource" error.chrome.runtime.lastErrorNetwork.enable with increased limits, e.g., Network.enable({ maxTotalBufferSize: 200000000, maxResourceBufferSize: 50000000 }), to prevent FIFO eviction of response data before retrieval.Network.requestWillBeSent: Request initiated.Network.responseReceived: Response headers received.Network.dataReceived: Response data chunks (if chunked).Network.loadingFinished: Response fully loaded.Network.enable initializes/reinitializes the domain, clearing buffers and invalidating IDs.The Network domain manages an in-memory buffer for response bodies. Enabling resets this buffer, ensuring fresh state but invalidating old data.