A powerful, middleware-based HTTP request/response interceptor for web browsers. This userscript provides robust interception capabilities for both fetch and XMLHttpRequest APIs, allowing you to monitor, modify, and analyze all HTTP traffic in web applications.
- Universal HTTP Interception: Intercepts both
fetchandXMLHttpRequestAPIs - Middleware Architecture: Plugin-based system for processing requests and responses
- Multiple Interception Methods: Choose between Proxy, Direct Replacement, or Getter/Setter approaches
- Request/Response Tracking: Unique ID tracking for each request throughout its lifecycle
- Content-Type Aware Parsing: Intelligent parsing of JSON, text, and binary responses
- MSW-Inspired Architecture: Modern, clean design patterns based on Mock Service Worker
- Zero Dependencies: Pure JavaScript with no external dependencies
- Override-Friendly: Allows other scripts to override fetch while maintaining interception
- Cleanup System: Proper restoration of original browser APIs
- Error Resilience: Robust error handling for edge cases
- Prototype Chain Support: Works across different JavaScript contexts
- Body Stream Protection: Handles already-consumed response bodies gracefully
- Install Tampermonkey or Greasemonkey
- Copy the
script.jscontent - Create a new userscript and paste the code
- Save and enable the script
// Include the script in your project
<script src="path/to/script.js"></script>;
// Access the interceptor globally
window.reclaimInterceptor.addRequestMiddleware(/* your middleware */);const interceptor = new RequestInterceptor({
disableFetch: false, // Enable/disable fetch interception
disableXHR: false, // Enable/disable XHR interception
useProxyForFetch: true, // Use Proxy-based fetch interception (default)
useGetterForFetch: false, // Use getter/setter approach (most robust)
});const interceptor = new RequestInterceptor({
useProxyForFetch: true,
useGetterForFetch: false,
});- Pros: Clean, preserves original function behavior
- Cons: May not work in all environments
- Best for: Modern browsers, standard use cases
const interceptor = new RequestInterceptor({
useProxyForFetch: false,
useGetterForFetch: true,
});- Pros: Maximum compatibility, prevents override conflicts
- Cons: Slightly more complex
- Best for: Production environments, maximum reliability
const interceptor = new RequestInterceptor({
useProxyForFetch: false,
useGetterForFetch: false,
});- Pros: Simple, fast
- Cons: Can be overridden by other scripts
- Best for: Development, testing
// Log all requests
interceptor.addRequestMiddleware(async (requestData) => {
console.log(`π€ ${requestData.method} ${requestData.url}`, {
headers: requestData.headers,
body: requestData.body,
});
}, "request_logger");
// Log all responses
interceptor.addResponseMiddleware(async (response, requestData) => {
console.log(`π₯ ${response.status} ${requestData.url}`, {
headers: response.headers,
body: response.body,
});
}, "response_logger");// Add authentication headers
interceptor.addRequestMiddleware(async (requestData) => {
if (requestData.url.includes("/api/")) {
requestData.headers["Authorization"] = "Bearer " + getAuthToken();
}
}, "auth_injector");
// Add CORS headers
interceptor.addRequestMiddleware(async (requestData) => {
requestData.headers["X-Requested-With"] = "XMLHttpRequest";
requestData.headers["X-Custom-Header"] = "ReclamInterceptor";
}, "cors_headers");// Monitor API performance
interceptor.addResponseMiddleware(async (response, requestData) => {
const duration = Date.now() - requestData.timestamp;
if (duration > 5000) {
console.warn(
`π Slow request detected: ${requestData.url} (${duration}ms)`
);
}
// Track API errors
if (response.status >= 400) {
console.error(`β API Error: ${response.status} ${requestData.url}`, {
response: response.body,
request: requestData,
});
}
}, "performance_monitor");// Collect analytics data
interceptor.addResponseMiddleware(async (response, requestData) => {
const analyticsData = {
url: requestData.url,
method: requestData.method,
status: response.status,
duration: Date.now() - requestData.timestamp,
size: response.headers["content-length"] || 0,
timestamp: requestData.timestamp,
};
// Send to analytics service
sendToAnalytics(analyticsData);
}, "analytics_collector");// Block specific domains
interceptor.addRequestMiddleware(async (requestData) => {
const blockedDomains = ["malicious-site.com", "tracker.ads"];
const url = new URL(requestData.url);
if (blockedDomains.includes(url.hostname)) {
console.log(`π« Blocked request to: ${requestData.url}`);
// Note: Actual blocking would require additional implementation
}
}, "domain_blocker");{
id: "req_1640123456789_abc123", // Unique request identifier
url: "https://api.example.com/data", // Request URL
method: "POST", // HTTP method
headers: { // Request headers object
"Content-Type": "application/json",
"Authorization": "Bearer token123"
},
body: "request body data", // Request body (if any)
request: Request, // Original Request object
timestamp: 1640123456789 // Request timestamp
}{
id: "req_1640123456789_abc123", // Matching request ID
url: "https://api.example.com/data", // Response URL
status: 200, // HTTP status code
statusText: "OK", // HTTP status text
headers: { // Response headers object
"content-type": "application/json",
"cache-control": "no-cache"
},
body: { data: "parsed response" }, // Parsed response body
isMockedResponse: false, // Always false for real responses
originalResponse: Response, // Original Response object
timestamp: 1640123456790 // Response timestamp
}βββββββββββββββββββ ββββββββββββββββββββ βββββββββββββββββββ
β HTTP Request βββββΆβ Request βββββΆβ Actual β
β (fetch/XHR) β β Middlewares β β Network β
βββββββββββββββββββ ββββββββββββββββββββ β Request β
βββββββββββββββββββ
β
βββββββββββββββββββ ββββββββββββββββββββ βΌ
β Return to ββββββ Response β βββββββββββββββββββ
β Caller β β Middlewares ββββββ HTTP Response β
βββββββββββββββββββ ββββββββββββββββββββ βββββββββββββββββββ
new RequestInterceptor(options);Options:
disableFetch(boolean): Disable fetch interceptiondisableXHR(boolean): Disable XHR interceptionuseProxyForFetch(boolean): Use Proxy-based fetch interceptionuseGetterForFetch(boolean): Use getter/setter approach
Add a middleware function to process requests.
interceptor.addRequestMiddleware(async (requestData) => {
// Process request
}, "optional_id");Add a middleware function to process responses.
interceptor.addResponseMiddleware(async (response, requestData) => {
// Process response
}, "optional_id");Remove a request middleware by ID.
interceptor.removeRequestMiddleware("middleware_id");Remove a response middleware by ID.
interceptor.removeResponseMiddleware("middleware_id");Get all registered middleware IDs.
const ids = interceptor.getMiddlewareIds();
// Returns: { request: [...], response: [...] }Update configuration at runtime.
interceptor.updateOptions({
useGetterForFetch: true,
});Clean up and restore original browser APIs.
interceptor.cleanup();// Add conditional middleware
const authMiddleware = async (requestData) => {
if (needsAuth(requestData.url)) {
requestData.headers["Authorization"] = await getToken();
}
};
// Add middleware
const middlewareId = interceptor.addRequestMiddleware(authMiddleware);
// Later, remove it
interceptor.removeRequestMiddleware(middlewareId);interceptor.addResponseMiddleware(async (response, requestData) => {
try {
if (response.status === 401) {
// Handle unauthorized
await refreshToken();
// Optionally retry request
}
} catch (error) {
console.error("Middleware error:", error);
}
}, "error_handler");Cause: Response body consumed multiple times
Solution: The interceptor now handles this automatically with proper cloning
Cause: JavaScript errors in middleware functions
Solution: Check browser console for errors, add try/catch blocks
Cause: Script loading after page requests
Solution: Ensure script loads before any HTTP requests
Cause: Other scripts overriding fetch/XHR
Solution: Use getter/setter method (useGetterForFetch: true)
// Enable detailed logging
const debug = {
log: (...args) => console.log("π [Debug]:", ...args),
error: (...args) => console.error("β [Error]:", ...args),
info: (...args) => console.info("βΉοΈ [Info]:", ...args),
};- Cross-Origin Requests: Be aware of CORS implications when modifying headers
- Sensitive Data: Avoid logging sensitive information like passwords or tokens
- Performance Impact: Heavy middleware can affect page performance
- Content Security Policy: May be restricted by strict CSP headers
- Keep Middleware Lightweight: Avoid heavy computations in middleware
- Use Async/Await Properly: Don't block request flow unnecessarily
- Filter Early: Add URL/method checks at the start of middleware
- Batch Operations: Group related middleware operations
- Clean Up: Remove unused middleware to reduce overhead
git clone https://github.com/your-username/reclaim-interceptor.git
cd reclaim-interceptor- Use ES6+ features
- Follow async/await patterns
- Add JSDoc comments for new functions
- Maintain backward compatibility
- Test across different browsers
- Verify with various websites
- Check performance impact
- Test middleware combinations
This project is licensed under the MIT License - see the LICENSE file for details.
- Inspired by Mock Service Worker (MSW)
- Built for the Reclaim Protocol ecosystem
- Community feedback and contributions
- Issues: GitHub Issues
- Documentation: This README and inline code comments
Version: 2025-07-11
Author: Abdul Rashid Reshamwala