[BUG] Empty ruleset still injects ~2000ms delay on every fetch/XHR request (v26.7.27)

Open
#4,788 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
3/5
Estimated time
1-2 days
Newbie friendliness
72/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Quiet
Tech stack
javascript
Domain
tooling

Research direction

Start by reproducing the delay with an empty ruleset, then inspect the interceptor in client.cs.js, especially the patched fetch and XMLHttpRequest paths and the W() and B() entry points. Done means requests with empty delayRules, requestRules, and responseRules complete without the ~2000ms delay while configured rules continue to work.

Written by the indexing model from the issue text.

Description

Issue 草稿:Requestly Chrome 扩展在规则为空时对所有 fetch/XHR 请求施加 ~2000ms 延迟

提交地址https://github.com/requestly/requestly/issues/new
标题建议[BUG] Empty ruleset still injects ~2000ms delay on every fetch/XHR request (v26.7.27)


Title

[BUG] Empty ruleset still injects ~2000ms delay on every fetch/XHR request (v26.7.27)

Summary

With zero rules configured (no Delay / Request / Response rules — window.__REQUESTLY__ shows all rule arrays empty), the Requestly Chrome extension's content-script interceptor still forces a constant ~2000ms delay on every fetch and XMLHttpRequest on every page it injects into. Disabling the extension instantly restores normal (<30ms) latency.

The delay is applied in JS (not at the network/DNR layer): Resource Timing shows TTFB of 10–21ms, but await fetch() resolves after ~2010–2050ms. An iframe whose contentWindow.fetch was not patched by Requestly completes the same request in ~24ms.

Client

  • Desktop — macOS (Darwin 25.5.0)
  • Desktop — Windows
  • Desktop — Linux
  • CLI

Protocol

  • HTTP / REST (any protocol — delay is protocol-agnostic)
  • GraphQL
  • WebSocket
  • Socket.IO
  • gRPC
  • MQTT
  • Not protocol-specific

Feature Area

  • Request editor
  • Collections
  • Environments & Variables
  • Mock Server
  • API Design (OpenAPI)
  • Collection Runner
  • Import / Export
  • Authentication
  • Scripts
  • Other — HTTP Interceptor (fetch/XHR monkey-patch in content script)

App Version

  • Extension: Requestly 26.7.27 (MV3, extension ID mdnleldcmiljblolnjhpnblkcekpdkpa)
  • Previous version present on disk: 26.6.10 (downgrade path available for bisect)
  • Browser: Google Chrome 150.0.0.0 (macOS)
  • OS: macOS (Darwin 25.5.0)

Steps to Reproduce

  1. Install Requestly v26.7.27 in Chrome.
  2. Ensure no rules are configured — no Delay, no Request, no Response rules. (A freshly reset Requestly account / local state with zero rules reproduces this.)
  3. Open any website where Requestly's content script injects (any http://* or https://* page, excluding app.requestly.com).
  4. From DevTools Console on that page, run:
    const t = performance.now();
    await fetch('/any-path').then(r => r.text());
    console.log(performance.now() - t);
    
  5. Observe the logged duration is consistently ~2000ms, regardless of the path or whether the resource even exists (a 404 path is also delayed ~2000ms).
Minimal reproduction snippet (run in page console)
// 1. Confirm no rules are loaded
console.log(window.__REQUESTLY__);
// → { sharedState: {}, delayRules: [], requestRules: [], responseRules: [] }  ← ALL EMPTY

// 2. Confirm fetch is monkey-patched by Requestly
console.log(window.fetch.toString());
// → async(...r)=>{...const u=W({url:i,method:a,type:"fetch",...}); u&&await B(u.delay);...}
//                          ^ W() returns truthy even with empty rulesets → awaits ~2000ms

// 3. Measure delay — constant ~2000ms on any request
for (let i = 0; i < 5; i++) {
  const t0 = performance.now();
  await fetch('/api/logs/stats').then(r => r.text()).catch(() => {});
  console.log('run', i, Math.round(performance.now() - t0) + 'ms');
}
// → ~2018, ~2014, ~2014, ~2012, ~2015  (all ~2000ms)

// 4. Control: an iframe whose fetch was NOT patched by Requestly
const iframe = document.createElement('iframe');
iframe.srcdoc = '<html><body></body></html>';
document.body.appendChild(iframe);
await new Promise(r => iframe.onload = r);
const t = performance.now();
await iframe.contentWindow.fetch('/api/logs/stats').then(r => r.text());
console.log('iframe (unpatched):', Math.round(performance.now() - t) + 'ms');
// → ~24ms  ← normal!
iframe.remove();

Expected Behavior

With an empty ruleset (delayRules: [], requestRules: [], responseRules: []), the interceptor's match function W({url, method, type, ...}) should return undefined/null, so the u && await B(u.delay) guard short-circuits and no delay is applied. Every request should complete at native speed (~10–50ms for local).

Actual Behavior

W() returns a truthy value (an object with delay ≈ 2000) even when no rules exist, so await B(u.delay) executes and stalls every request by ~2000ms.

Measured evidence
Measurement Value Notes
curl https://<site>/api/logs (bypasses extension) 18–52 ms Backend & network are fast
await fetch() from page console 2010–2050 ms Constant, independent of payload
fetch('/404-nonexistent') 2006 ms Delay applies to non-existent paths too
fetch('/favicon.svg'), static image 2015–2057 ms Applies to all resources on the domain
Resource Timing responseStart - requestStart (TTFB) 10–21 ms Network layer is fast — delay is in JS
iframe contentWindow.fetch (unpatched by Requestly) 24 ms Bypassing the interceptor = normal
window.fetch.toString() monkey-patched async(...r)=>{...await B(u.delay)...}
XMLHttpRequest.prototype.open/send monkey-patched contains rqProxyXhr, await B(s.delay)
window.__REQUESTLY__ {delayRules:[], requestRules:[], responseRules:[]} No rules configured
Parallel vs serial behavior

Issuing 3 concurrent fetch calls resolves in ~2046ms total (not ~6000ms), indicating the delay is awaited in parallel per-request rather than serialized — consistent with a per-request await B(delay) in each intercepted Promise.

Additional Context

Why this was hard to diagnose (and a clue for the team)

Because the delay is injected in the JS layer and Resource Timing reports honest network timings, the symptom looks identical to "my backend is slow" — but the backend is fine (curl confirms <50ms). The constant ~2000ms floor regardless of path/status, plus the empty __REQUESTLY__ ruleset, are the fingerprints.

Historical note

Issue #127 ("Allow Delay Network Requests Rule greater than 2000ms") suggests 2000 may be a default/ceiling value in the delay code path. The fact that the observed delay sits right at ~2000ms — and is applied even with no delay rule defined — strongly suggests a code path where a default delay value is applied when the matcher has no rule but still returns a truthy object.

Likely code location

The interceptor lives in the content script client.cs.js (matches http://*/*, https://*/*, run_at: document_start). The patched fetch and XMLHttpRequest.prototype.open/send reference an internal W() (returns matched delay rule pair) and B() (executes the delay). When the ruleset is empty, W() should return falsy but appears to return a truthy {delay: ~2000}.

Workaround

Disabling the Requestly extension immediately restores normal request latency.

Regression bisect candidate

Version 26.6.10 is still present on disk alongside 26.7.27. If the team can't reproduce on 26.6.10, this is likely a regression introduced in 26.7.x. I can run a bisect if helpful.

Environment

  • Extension: Requestly 26.7.27 (ID: mdnleldcmiljblolnjhpnblkcekpdkpa)
  • Browser: Chrome 150.0.0.0
  • OS: macOS (Darwin 25.5.0)
  • Rules configured: none (empty ruleset confirmed via window.__REQUESTLY__)

提交后建议附带的诊断数据(可选,增强说服力)

如官方需要,可提供:

  1. client.cs.jsW()B() 的反混淆实现(可由官方自行定位)
  2. 完整的 Resource Timing 截图(TTFB 10ms vs fetch 2018ms 的对比)
  3. 禁用扩展前后 await fetch() 的耗时对比曲线
Dominant language
No language data
Stars
6.8k
Forks
694
PR merge metrics
No merged PRs in 30d

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from requestly/requestly

All issues in requestly/requestly

Similar issues

More DevTools issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.