Mock API Responses in Chrome Without a Proxy: 4 Methods Compared
You want to see how the page behaves when the API returns a 500, an empty list, or a field that doesn't exist yet. You don't want to install a root certificate, configure a proxy, or ask the backend team for a special build. Here are four ways to do it from inside Chrome, what each is good at, and where each falls short.
By Sharaj Rewoo · Updated 25 September 2026
Why mock in the browser at all?
Traditional mocking means a proxy such as Charles, Fiddler or mitmproxy sitting between the browser and the network. Proxies are powerful, but for HTTPS they need a trusted root certificate on your machine, which corporate laptops often block, and they intercept everything rather than the one tab you care about.
Mocking inside the browser avoids both problems. It's the fastest way to:
- Reproduce failure states — 500s, timeouts, malformed JSON, expired tokens — that are hard to trigger on a real backend.
- Build the frontend ahead of the API by standing in a response for an endpoint that isn't deployed yet.
- Test edge-case data — an empty array, a 10,000-item list, a name with emoji, a
nullwhere the UI expects a string. - Demo or debug production without changing anything on the server.
Method 1: Chrome DevTools Local Overrides
Built into Chrome, no install needed. Local Overrides lets you replace a network response (or its headers) with a file saved on your machine, and Chrome serves that file every time the page requests the URL.
How to set it up
- Open DevTools and go to the Network panel. Reload so the request you want appears.
- Right-click the request and choose Override content. The first time, Chrome asks you to pick a folder to store overrides in and to allow access to it.
- Chrome opens the response in the Sources → Overrides tab. Edit the JSON, save with Ctrl/Cmd+S.
- Reload the page. The request now shows a purple dot in the Network panel, meaning it was served from your override.
To change response headers instead — for example to test a missing Cache-Control or a CORS header — right-click the request and choose Override headers, or open the request's Headers tab and edit a response header there. Chrome stores these in a .headers file in the same overrides folder.
Strengths and limits
- Good: zero setup, works on any site, overrides persist across reloads and browser restarts as long as "Enable Local Overrides" stays checked.
- Limit: you can change the body and headers, but not the status code — a 200 stays a 200. You can't simulate latency per request either (network throttling is global).
- Limit: matching is by exact URL. Requests whose query string changes each time (cache-busters, timestamps) are awkward to override.
- Limit: overrides only apply while DevTools is open, and the request must have happened at least once so you can pick it.
Method 2: A mocking extension (Mockingbird)
Extensions sit between DevTools and a full proxy: they intercept traffic for a tab, but give you rules, status codes and delays that DevTools can't. Mockingbird is the one I built for this; the same approach applies to other mocking extensions.
How it works
- Arm a tab. Mockingbird captures that tab's live traffic — method, URL, status, size and timing — with headers, query params and pretty-printed JSON bodies.
- Add a mock rule. Match an endpoint by glob, regex or substring and return any status, headers, body and latency you choose. Cross-origin mocks work, with CORS handled for you.
- Or pass through and rewrite. Let the real request hit the network, then override the status, merge headers, or rewrite specific JSON fields with JSONPath. This is ideal for "same response, but this one field is null".
- Set a breakpoint. Hold a request at the request or response stage, edit it live, then continue or abort — or add a delay to simulate a slow server.
- Save and share. Export rule sets as JSON or the captured session as HAR.
Strengths and limits
- Good: no code access needed, works on any site including production, supports status codes, latency and pattern matching.
- Good: nothing leaves your machine — Mockingbird has no server and stores rules locally.
- Limit: it uses the Chrome DevTools Protocol, so Chrome shows a "debugging this browser" banner while a tab is armed, and it works on one tab at a time.
- Limit: mocks live in your browser, not in the codebase, so they don't run in CI.
Method 3: Mock Service Worker (MSW) in the app
Mock Service Worker is a JavaScript library that registers a service worker in your app and answers matching requests with handlers you write in code. Because it intercepts at the network level, your app's fetch or axios calls don't know they're being mocked.
// src/mocks/handlers.js
import { http, HttpResponse } from 'msw'
export const handlers = [
http.get('/api/orders', () =>
HttpResponse.json([], { status: 200 })), // empty state
http.post('/api/checkout', () =>
new HttpResponse(null, { status: 503 })), // outage
]
// src/mocks/browser.js
import { setupWorker } from 'msw/browser'
import { handlers } from './handlers'
export const worker = setupWorker(...handlers)
// in your app entry, dev only:
// if (import.meta.env.DEV) await worker.start()
You also run npx msw init public/ once to copy the worker script into your public folder.
Strengths and limits
- Good: mocks are code, so they're versioned, reviewed and shared with the whole team. The same handlers run in Node for unit and integration tests, which makes MSW the best choice for mocks you want to keep.
- Good: full control over status, headers, delays and dynamic responses.
- Limit: you need access to the app's source and a build. It's no help when testing a site you don't own, or a deployed staging build you can't change.
- Limit: service workers require HTTPS or localhost, and only one can control a page scope, so apps that already use a service worker need care.
Method 4: Patching fetch or XHR from the console
The quick-and-dirty option. Paste a snippet into the DevTools console (or save it under Sources → Snippets) that wraps window.fetch and returns a fake response for matching URLs:
const realFetch = window.fetch;
window.fetch = async (input, init) => {
const url = typeof input === 'string' ? input : input.url;
if (url.includes('/api/profile')) {
return new Response(JSON.stringify({ name: null }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
return realFetch(input, init);
};
Strengths and limits
- Good: no install, no files, full control over status and body, can be conditional or random.
- Limit: the patch disappears on reload, so it only catches requests made after you paste it — not the ones fired on page load.
- Limit: it only covers
fetch. Apps usingXMLHttpRequest(including older axios setups) need a separate XHR patch, and requests made inside workers or iframes aren't affected. - Limit: some libraries capture a reference to
fetchat startup, so your patch never sees their calls.
Comparison
| DevTools Overrides | Mocking extension | MSW | Console patch | |
|---|---|---|---|---|
| Setup effort | Low | Low (install once) | Medium (code + build) | Very low |
| Works on any site | Yes | Yes | No — your app only | Yes |
| Survives reload | Yes | Yes | Yes | No |
| Needs code access | No | No | Yes | No |
| Change status code | No | Yes | Yes | Yes |
| Change headers | Yes | Yes | Yes | Yes |
| Simulate latency | Global throttling only | Per request | Per request | Yes, manually |
| URL pattern matching | Exact URL | Glob / regex / substring | Paths and params | Whatever you code |
| Share with the team | Copy the folder | Export rules as JSON | In the repo | Share the snippet |
| Runs in CI | No | No | Yes | No |
Which one should you pick?
- You need to tweak one response body, once: DevTools Local Overrides. It's already installed.
- You're testing failure states on a site you don't own, or on staging/production: a mocking extension. Status codes, delays and pattern matching are what DevTools is missing. This is the typical QA case.
- You're a frontend developer building against an API that isn't ready, or you want mocks in tests: MSW. The mocks become part of the codebase.
- You need an answer in the next 30 seconds: patch fetch in the console, and accept it'll vanish on reload.
Many teams end up using two: an extension or DevTools for exploratory testing, and MSW for the mocks that should run on every build.
Where the real responses come in
Mocks are only useful if they look like the real API. Two habits help. First, start mocks from captured traffic rather than typing JSON from memory — export a HAR, or use XHRScribe, which records a tab's real XHR and fetch calls and turns them into runnable API tests. Second, watch for the real API drifting away from your mocks. Telltale learns each endpoint's schema from live traffic and alerts you when a response gains, loses or changes a field — exactly the moment your mocks go stale.
If you're deciding which failure responses to mock, the network section of 50 negative and edge-case test cases is a good starting list.