DEV Community

hex
hex

Posted on

Building a Chrome Extension Request Interceptor: A Complete Development Guide

Executive Summary

Image description

Understanding how websites communicate with backend services is crucial for developers, security researchers, and system integrators. While browser DevTools provide basic network monitoring, they miss dynamically generated requests and lack advanced filtering capabilities. This guide details the complete development process of a Chrome extension that intercepts all HTTP requests (XHR and Fetch) at the JavaScript level, providing deep visibility into website behavior through a custom DevTools panel.

Key Benefits:

  • Captures requests missed by standard DevTools
  • Provides stack traces for request origins
  • Offers detailed timing and performance data
  • Enables comprehensive website behavior analysis

Project Requirements and Architecture Planning

Initial Problem Statement

Standard browser DevTools have limitations when analyzing complex web applications:

  • Limited Request Context: No stack traces showing where requests originate
  • Timing Gaps: Missing JavaScript-level performance data
  • Dynamic Request Blindness: Some programmatically generated requests aren't captured
  • Integration Challenges: Difficult to export or process captured data

Technical Requirements

Core Functionality:

  • Intercept all XMLHttpRequest and Fetch API calls
  • Capture complete request/response data including headers and bodies
  • Provide stack traces for debugging request origins
  • Display data in an intuitive DevTools panel interface
  • Support real-time monitoring across all website tabs

Technical Constraints:

  • Must work with Manifest V3 (Chrome's latest extension format)
  • Cannot break existing website functionality
  • Must handle high-frequency request scenarios
  • Needs to work across all domains and protocols

Architecture Design

The extension uses a multi-layer architecture to overcome Chrome's security restrictions:

┌─────────────────┐    ┌──────────────────┐    ┌─────────────────┐    ┌──────────────────┐
│   Web Page      │    │  Content Script  │    │ Background      │    │  DevTools Panel │
│                 │    │                  │    │ Service Worker  │    │                  │
│ ┌─────────────┐ │    │ ┌──────────────┐ │    │ ┌─────────────┐ │    │ ┌──────────────┐ │
│ │ Injected    │◄┼────┼►│ Message      │◄┼────┼►│ Message     │◄┼────┼►│ UI Display   │ │
│ │ Script      │ │    │ │ Forwarder    │ │    │ │ Router      │ │    │ │ & Controls   │ │
│ └─────────────┘ │    │ └──────────────┘ │    │ └─────────────┘ │    │ └──────────────┘ │
└─────────────────┘    └──────────────────┘    └─────────────────┘    └──────────────────┘
        │                        │                        │                        │
        ▼                        ▼                        ▼                        ▼
   Intercepts                Bridges                 Routes &                 Displays
   HTTP calls               contexts                Queues                  captured data
Enter fullscreen mode Exit fullscreen mode

Phase 1: Project Setup and Manifest Configuration

Creating the Extension Structure

First, establish the project directory structure:

request-interceptor/
├── manifest.json
├── background.js
├── content_script.js
├── injected.js
├── devtools.html
├── devtools.js
├── panel.html
├── panel.css
├── panel.js
└── icons/
    └── icon16.png
Enter fullscreen mode Exit fullscreen mode

Manifest V3 Configuration

The manifest.json file defines the extension's capabilities and architecture:

{
  "manifest_version": 3,
  "name": "Request Interceptor",
  "version": "1.0",
  "description": "Intercepts requests in-page and displays them in a DevTools panel.",
  "permissions": [
    "storage"
  ],
  "host_permissions": [
    "<all_urls>"
  ],
  "icons": {
    "128": "icon.png"
  },
  "background": {
    "service_worker": "background.js"
  },
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["content_script.js"],
      "run_at": "document_start",
      "all_frames": true
    }
  ],
  "devtools_page": "devtools.html",
  "web_accessible_resources": [
    {
      "resources": ["injected.js"],
      "matches": ["<all_urls>"]
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Key Configuration Decisions:

  • "run_at": "document_start": Ensures injection before any website scripts execute
  • "all_frames": true: Captures requests from iframes and embedded content
  • "<all_urls>": Universal access for comprehensive website analysis
  • web_accessible_resources: Allows content script to inject our monitoring code

Phase 2: Core Interception Logic Development

The Injected Script Foundation

The injected.js file runs in the website's context and performs the actual HTTP interception:

(function () {
    // Prevent multiple injection
    if (window.__HybridInterceptorActive) return;
    window.__HybridInterceptorActive = true;

    const sendDataToDevtools = (data) => {
      window.postMessage({
        type: "FROM_PAGE",
        payload: data
      }, "*");
    };

    const getStackTrace = () => {
      try {
        throw new Error();
      } catch (e) {
        return e.stack ? e.stack.split("\n").slice(2).join("\n") : 'Stack trace not available';
      }
    };
Enter fullscreen mode Exit fullscreen mode

Design Rationale:

  • Global Flag: Prevents multiple injections that could cause conflicts
  • PostMessage API: Safely communicates with content script across security boundaries
  • Stack Trace Capture: Provides debugging context for request origins

XMLHttpRequest Interception

XMLHttpRequest interception requires monkey-patching three prototype methods:

const originalXhrOpen = XMLHttpRequest.prototype.open;
const originalXhrSend = XMLHttpRequest.prototype.send;
const originalSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;

XMLHttpRequest.prototype.open = function (method, url) {
  this._interceptorData = {
    type: 'XHR',
    method,
    url,
    headers: {},
    start: performance.now(),
    trace: getStackTrace()
  };
  return originalXhrOpen.apply(this, arguments);
};

XMLHttpRequest.prototype.setRequestHeader = function (key, value) {
    if (this._interceptorData && this._interceptorData.headers) {
        this._interceptorData.headers[key] = value;
    }
    return originalSetRequestHeader.apply(this, arguments);
};

XMLHttpRequest.prototype.send = function (body) {
  if (this._interceptorData) {
    this._interceptorData.body = body || null;
  }

  const onStateChange = () => {
    if (this.readyState === 4 && this._interceptorData) {
      this._interceptorData.end = performance.now();
      this._interceptorData.duration = this._interceptorData.end - this._interceptorData.start;
      this._interceptorData.status = this.status;
      this._interceptorData.response = this.responseText;
      this._interceptorData.timestamp = new Date().toISOString();

      sendDataToDevtools(this._interceptorData);
    }
  };

  this.addEventListener("readystatechange", onStateChange);
  return originalXhrSend.apply(this, arguments);
};
Enter fullscreen mode Exit fullscreen mode

Technical Challenges Solved:

  • State Management: Using instance properties to track request data across method calls
  • Header Capture: Intercepting setRequestHeader calls to build complete header map
  • Timing Precision: Using performance.now() for microsecond-accurate duration measurement
  • Non-intrusive Design: Preserving original functionality while adding monitoring

Fetch API Interception

Fetch interception is more complex due to its Promise-based nature and response streaming:

const originalFetch = window.fetch;
window.fetch = function(...args) {
  const [url, config] = args;
  const fetchData = {
    type: 'Fetch',
    method: config?.method || 'GET',
    url: url.toString(),
    headers: config?.headers ? new Headers(config.headers).toJSON() : {},
    body: config?.body || null,
    start: performance.now(),
    trace: getStackTrace(),
    timestamp: new Date().toISOString()
  };

  return originalFetch(...args)
    .then(response => {
      const responseClone = response.clone();
      fetchData.status = response.status;
      fetchData.end = performance.now();
      fetchData.duration = fetchData.end - fetchData.start;

      responseClone.text().then(text => {
        fetchData.response = text;
        sendDataToDevtools(fetchData);
      });

      return response;
    })
    .catch(error => {
      fetchData.status = 'Error';
      fetchData.response = error.message;
      fetchData.end = performance.now();
      fetchData.duration = fetchData.end - fetchData.start;
      sendDataToDevtools(fetchData);
      throw error;
    });
};
Enter fullscreen mode Exit fullscreen mode

Key Implementation Details:

  • Response Cloning: Essential for reading response body without consuming the original stream
  • Headers Processing: Custom toJSON() method for Headers object serialization
  • Error Handling: Capturing both network errors and successful responses
  • Promise Chain Preservation: Maintaining original Fetch behavior for website compatibility

Phase 3: Inter-Context Communication System

Content Script Bridge

The content script (content_script.js) bridges the isolated website context with the extension's privileged context:

// 1. Inject the monitoring script into the page
const script = document.createElement('script');
script.src = chrome.runtime.getURL('injected.js');
(document.head || document.documentElement).appendChild(script);
script.onload = () => script.remove();

// 2. Listen for messages from the injected script
window.addEventListener("message", (event) => {
  // Security validation
  if (event.source !== window || !event.data.type || event.data.type !== "FROM_PAGE") {
    return;
  }

  // 3. Forward the data to the background script
  chrome.runtime.sendMessage({
    type: 'INTERCEPTED_REQUEST',
    data: event.data.payload
  });
}, false);
Enter fullscreen mode Exit fullscreen mode

Security Considerations:

  • Source Validation: Ensures messages originate from the same window
  • Type Filtering: Only processes messages with expected structure
  • Script Cleanup: Removes injection script element after execution

Background Service Worker

The background script (background.js) manages connections and message routing:

const connections = {};
const messageQueue = {};

chrome.runtime.onConnect.addListener(port => {
  const extensionListener = (message) => {
    if (message.name === "init") {
      const tabId = message.tabId;
      connections[tabId] = port;

      // Process any queued messages for this tab
      if (messageQueue[tabId]) {
        messageQueue[tabId].forEach(msg => port.postMessage(msg));
        delete messageQueue[tabId];
      }

      port.onDisconnect.addListener(() => {
        delete connections[tabId];
      });
    }
  };
  port.onMessage.addListener(extensionListener);
});

chrome.runtime.onMessage.addListener((request, sender) => {
  if (sender.tab) {
    const tabId = sender.tab.id;
    if (tabId in connections) {
      connections[tabId].postMessage(request);
    } else {
      // Queue messages when DevTools panel isn't ready
      if (!messageQueue[tabId]) {
        messageQueue[tabId] = [];
      }
      messageQueue[tabId].push(request);
    }
  }
  return true;
});
Enter fullscreen mode Exit fullscreen mode

Advanced Features:

  • Connection Management: Maintains persistent connections to DevTools panels
  • Message Queuing: Buffers messages when DevTools isn't open
  • Tab Isolation: Routes messages to correct panel instance
  • Cleanup Handling: Removes stale connections on disconnect

Phase 4: DevTools Panel Integration

Panel Registration

DevTools integration starts with panel registration (devtools.js):

chrome.devtools.panels.create(
    "Request Spy",
    "icons/icon16.png",
    "panel.html"
);
Enter fullscreen mode Exit fullscreen mode

Panel HTML Structure

The panel UI (panel.html) uses semantic HTML for accessibility and maintainability:

<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="panel.css">
  </head>
  <body>
    <div class="toolbar">
      <button id="clear-button">Clear</button>
    </div>
    <div id="requests-container"></div>

    <template id="request-template">
      <div class="request-entry">
        <details>
          <summary class="request-summary">
            <span class="status-badge"></span>
            <span class="method-badge"></span>
            <span class="url"></span>
            <span class="duration"></span>
          </summary>
          <div class="details-grid">
            <div class="grid-header">URL:</div>
            <div class="grid-value full-url"></div>
            <div class="grid-header">Timestamp:</div>
            <div class="grid-value timestamp"></div>
            <div class="grid-header">Request Headers:</div>
            <div class="grid-value"><pre class="headers"></pre></div>
            <div class="grid-header">Request Body:</div>
            <div class="grid-value"><pre class="request-body"></pre></div>
            <div class="grid-header">Response Body:</div>
            <div class="grid-value"><pre class="response-body"></pre></div>
            <div class="grid-header">Trace:</div>
            <div class="grid-value"><pre class="trace"></pre></div>
          </div>
        </details>
      </div>
    </template>

    <script src="panel.js"></script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

UI Design Decisions:

  • Details/Summary Elements: Native browser collapsible functionality
  • Template Element: Efficient DOM cloning for dynamic content
  • Grid Layout: Organized data presentation
  • Pre Elements: Preserve formatting for code and JSON

Panel Styling

CSS styling (panel.css) follows DevTools design patterns:

body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  font-size: 14px;
  margin: 0;
  padding: 0;
  background-color: #f3f3f3;
}

.status-badge {
  padding: 2px 8px;
  border-radius: 10px;
  font-size: 12px;
  color: white;
  font-weight: bold;
  white-space: nowrap;
  flex-shrink: 0;
}

.status-badge.status-ok { background-color: #28a745; }
.status-badge.status-error { background-color: #dc3545; }
.status-badge.status-other { background-color: #6c757d; }

.details-grid {
  display: grid;
  grid-template-columns: 150px 1fr;
  gap: 8px;
  padding: 12px;
  border-top: 1px solid #eee;
  background-color: #fafafa;
}
Enter fullscreen mode Exit fullscreen mode

Visual Design Principles:

  • Status Color Coding: Immediate visual feedback for response status
  • Responsive Grid: Adapts to different panel sizes
  • Developer-Friendly Typography: Monospace fonts for code content
  • Consistent Spacing: Professional appearance matching DevTools aesthetic

Panel JavaScript Logic

The panel logic (panel.js) handles data reception and UI updates:

document.addEventListener('DOMContentLoaded', () => {
  const requestsContainer = document.getElementById('requests-container');
  const requestTemplate = document.getElementById('request-template');
  const clearButton = document.getElementById('clear-button');

  // Establish connection to background script
  const backgroundPageConnection = chrome.runtime.connect({ name: "devtools-panel" });
  backgroundPageConnection.postMessage({ 
    name: 'init', 
    tabId: chrome.devtools.inspectedWindow.tabId 
  });

  // Handle incoming request data
  backgroundPageConnection.onMessage.addListener((message) => {
    if (message.type === 'INTERCEPTED_REQUEST' && message.data) {
      addRequestToView(message.data);
    }
  });

  // Clear functionality
  clearButton.addEventListener('click', () => {
    requestsContainer.innerHTML = '';
  });

  function addRequestToView(data) {
    const template = requestTemplate.content.cloneNode(true);

    // Status badge configuration
    const statusBadge = template.querySelector('.status-badge');
    const statusStr = data.status ? data.status.toString() : '???';
    statusBadge.textContent = `${data.type} ${statusStr}`;

    if (statusStr.startsWith('2')) {
      statusBadge.classList.add('status-ok');
    } else if (statusStr.startsWith('4') || statusStr.startsWith('5') || statusStr === 'Error') {
       statusBadge.classList.add('status-error');
    } else {
       statusBadge.classList.add('status-other');
    }

    // Populate template with request data
    template.querySelector('.method-badge').textContent = data.method;
    template.querySelector('.url').textContent = data.url;
    template.querySelector('.full-url').textContent = data.url;
    template.querySelector('.duration').textContent = data.duration ? `${data.duration.toFixed(0)}ms` : '';
    template.querySelector('.timestamp').textContent = data.timestamp;

    template.querySelector('.headers').textContent = JSON.stringify(data.headers || {}, null, 2);
    template.querySelector('.request-body').textContent = formatBody(data.body);
    template.querySelector('.response-body').textContent = formatBody(data.response);
    template.querySelector('.trace').textContent = data.trace || 'N/A';

    requestsContainer.appendChild(template);
  }

  function formatBody(body) {
    if (body === null || body === undefined) return 'N/A';

    if (typeof body === 'string') {
        try {
            // Pretty-print JSON if possible
            return JSON.stringify(JSON.parse(body), null, 2);
        } catch (e) {
            return body;
        }
    }

    if (typeof body === 'object') {
        return JSON.stringify(body, null, 2);
    }

    return body.toString();
  }
});
Enter fullscreen mode Exit fullscreen mode

Advanced UI Features:

  • Template Cloning: Efficient DOM manipulation for high-frequency updates
  • Smart JSON Formatting: Automatic pretty-printing of JSON content
  • Real-time Updates: Immediate display of intercepted requests
  • Connection Management: Robust handling of DevTools panel lifecycle

Phase 5: Testing and Deployment

Local Testing Process

1. Extension Loading:

# Navigate to chrome://extensions/
# Enable "Developer mode"
# Click "Load unpacked" and select project directory
Enter fullscreen mode Exit fullscreen mode

2. Functional Testing:

  • Open any website with AJAX functionality
  • Open DevTools and navigate to "Request Spy" tab
  • Trigger various HTTP requests (form submissions, API calls, etc.)
  • Verify all request data is captured and displayed correctly

3. Performance Testing:

  • Test on high-traffic websites (social media, news sites)
  • Monitor for performance degradation
  • Verify memory usage remains stable during extended sessions

Common Issues and Solutions

Issue: Requests Not Appearing

  • Diagnosis: Check if injected script loaded successfully
  • Solution: Verify web_accessible_resources configuration in manifest

Issue: DevTools Panel Not Loading

  • Diagnosis: Check background script connection handling
  • Solution: Ensure proper tab ID routing and connection management

Issue: Missing Request Data

  • Diagnosis: Timing issues with response body reading
  • Solution: Implement proper Promise handling in Fetch interception

Production Deployment

Chrome Web Store Preparation:

  1. Icon Assets: Create required icon sizes (16x16, 48x48, 128x128)
  2. Store Listing: Prepare description, screenshots, and promotional images
  3. Privacy Policy: Document data handling practices
  4. Permissions Justification: Explain necessity of broad permissions

Advanced Use Cases and Applications

Website Behavior Analysis

API Discovery:

Example: Analyzing a Social Media Site
┌─────────────────────────────────────────────────────────────┐
│ Captured Requests:                                          │
│ POST /api/v1/posts          → Content publishing           │
│ GET  /api/v1/feed           → Timeline loading             │
│ POST /api/v1/analytics      → User behavior tracking       │
│ GET  /api/v1/notifications  → Real-time updates            │
└─────────────────────────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

Authentication Flow Mapping:

  • Capture OAuth token exchanges
  • Monitor session refresh mechanisms
  • Identify security headers and CSRF protection

Performance Analysis:

  • Measure API response times across different endpoints
  • Identify slow or failing requests
  • Analyze request frequency patterns

Security Research Applications

AJAX Endpoint Discovery:

  • Identify hidden API endpoints not visible in source code
  • Map complete API surface area
  • Discover internal vs. public endpoints

Data Flow Analysis:

  • Track sensitive data transmission
  • Identify unencrypted communications
  • Monitor third-party integrations

Rate Limiting Detection:

  • Observe request throttling behavior
  • Identify API usage limits
  • Test error handling mechanisms

Integration Development Support

Third-Party API Integration:

  • Reverse-engineer API call patterns
  • Understand required headers and authentication
  • Capture complete request/response examples

Microservices Architecture Analysis:

  • Map service-to-service communication
  • Identify service dependencies
  • Document API contracts

Performance Optimization and Best Practices

Memory Management

Request Data Lifecycle:

// Implement request history limits
const MAX_REQUESTS = 1000;
if (requestHistory.length > MAX_REQUESTS) {
  requestHistory.splice(0, requestHistory.length - MAX_REQUESTS);
}
Enter fullscreen mode Exit fullscreen mode

Cleanup Strategies:

  • Clear stored request data on tab close
  • Implement periodic cleanup of old entries
  • Optimize large response body handling

Scalability Considerations

High-Frequency Request Handling:

  • Implement request batching for performance
  • Add filtering options to reduce noise
  • Consider sampling for extremely high-traffic sites

Resource Usage Optimization:

  • Minimize memory footprint of stored request data
  • Implement lazy loading for response bodies
  • Optimize DOM updates for large request lists

Security Best Practices

Data Sanitization:

function sanitizeRequestData(data) {
  // Remove sensitive headers
  const sensitiveHeaders = ['authorization', 'cookie', 'x-api-key'];
  const cleanHeaders = { ...data.headers };

  sensitiveHeaders.forEach(header => {
    if (cleanHeaders[header]) {
      cleanHeaders[header] = '[REDACTED]';
    }
  });

  return { ...data, headers: cleanHeaders };
}
Enter fullscreen mode Exit fullscreen mode

Permission Minimization:

  • Request only necessary permissions
  • Implement user consent for sensitive data capture
  • Provide opt-out mechanisms for privacy-conscious users

Future Enhancement Roadmap

Phase 1 Enhancements

  • Request Filtering: Add URL pattern and method filtering
  • Export Functionality: Generate HAR files for external analysis
  • Search Capabilities: Full-text search across request/response data

Phase 2 Advanced Features

  • Request Replay: Resend captured requests with modifications
  • Performance Metrics: Detailed timing breakdowns and statistics
  • Custom Scripting: JavaScript execution against captured data

Phase 3 Enterprise Features

  • Team Collaboration: Share captured sessions across team members
  • Integration APIs: Connect with external monitoring systems
  • Automated Testing: Generate test cases from captured requests

Conclusion

This Chrome extension demonstrates sophisticated browser extension development techniques while solving real-world web development challenges. The multi-context architecture, advanced JavaScript interception patterns, and custom DevTools integration showcase modern web platform capabilities.

Key Technical Achievements:

  • Successful implementation of non-intrusive HTTP monitoring
  • Robust cross-context communication system
  • Professional-grade developer tool interface
  • Comprehensive website behavior analysis capabilities

Applications for Technical Documentation:

  • Demonstrates complex system architecture documentation
  • Shows integration between multiple technical components
  • Provides real-world example of developer tool creation
  • Illustrates security considerations in extension development

The extension serves as both a practical development tool and an excellent example of technical writing that combines deep technical knowledge with clear, actionable documentation. This approach directly aligns with Google's technical writing requirements for roles involving developer tools, system integration, and infrastructure documentation.

Repository Link: https://github.com/hexcreator/hook-http-requests

Top comments (0)