Net Tinker Help

Leer en español

On this page

Rule types

Every rule has exactly one type, which decides what happens once it matches a request:

Type What it does When to use it
Mock response The request never reaches the network. You fabricate the status, headers and body yourself. The backend endpoint doesn't exist yet, or you need a specific error/edge case (500, empty list, malformed data) that's hard to trigger for real.
Modify request body The request still goes out for real, but with a different body than the page sent. Testing how the backend reacts to a payload the UI wouldn't normally send.
Modify headers Add, override or remove request and/or response headers. The request still goes out for real. Forcing a feature flag header, testing a missing/extra CORS header, faking an auth header locally.
Redirect Sends the request to a different URL entirely. Pointing a call at a local server or a different environment without touching the app's config.
Cancel The request fails outright — fetch rejects, XHR fires an error event. Testing what your UI does when a call fails or a script fails to load.
Modify query params Add or remove query string parameters. The request still goes out for real. Forcing a query param the UI doesn't expose (a debug flag, a page size).
Replace in URL Regex-replaces part of the URL, keeping the rest intact. The request still goes out for real. Swapping staging for production in a URL without a full redirect rule.
Delay Holds the real request back by a fixed number of milliseconds before letting it through. Checking your loading states and spinners actually show up.
Response body The request goes out for real; the real response body gets find/replaced or has one JSON field overwritten before your code sees it. Flipping one field in a real API response (e.g. a status) without hand-writing a full mock.

Mock vs. modify & passthrough

This is the single most common point of confusion, so it's worth spelling out on its own:

Mock — the request never leaves the browser. Net Tinker builds a fake Response (or fakes the XHR properties) and hands it straight back. The real server never even sees the request.
Modify & passthrough (request body, headers, query params, replace-in-URL) — the request really goes out over the network, just not quite the way the page originally built it. The real server does see it, and its real response comes back (possibly with response headers further modified on the way back, for the "headers" rule type).
Modify the real response body — the request goes out completely unchanged, and only the body coming back gets transformed (find/replace, or overwriting one JSON field) before your code ever reads it. Non-JSON bodies can't be inspected for the "overwrite a field" mode, so they're left untouched rather than silently doing nothing you'd notice.

A practical way to tell them apart while debugging: open DevTools' Network tab. A mocked request never shows up there at all (it never reached the network); a modified-and-passed-through one does, with the rewritten URL/body/headers.

Using variables

Define a name/value pair once (the "{{ }} Variables" button in the options page) and reuse it as {{name}} anywhere you'd otherwise retype the same string: a rule's body, a header's value, a redirect URL, a query param's value, or the replacement text of a "Replace in URL" rule.

Define apiHost = api.staging.example.com, then use https://{{apiHost}}/v2/users as a redirect target. Change the environment once, in one place, instead of hunting down every rule that hardcoded the old host.

A name that doesn't match any defined variable is left exactly as written — {{typo}} shows up literally in the request or response, rather than silently disappearing, so a mistake is obvious instead of a mystery. Variables are not substituted into a rule's matching fields (the URL pattern, or a header/body/initiator condition) — those compare against the real, live request, so a variable there wouldn't mean anything.

Environments

An environment (the "🌐 Environments" button, next to Variables) is a named set of overrides on top of your variables — Local, Staging, Prod, whatever you need. It only has to override what actually changes between environments; anything it doesn't mention keeps the variable's own base value.

With apiHost = local.test as the base value, a "Prod" environment can override just apiHost = api.example.com and leave every other variable alone.

Pick the active environment from the star button next to it in the options page, or from the dropdown in the popup (only shown once at least one environment exists). With no active environment, every variable simply uses its own base value — exactly like before environments existed.

Why a rule might not be firing

Check these in order — they cover every reason a rule can silently fail to apply:

  1. The master toggle (top of the popup or options page) is off. Nothing applies at all while it is.
  2. The rule's own enable/disable toggle is off.
  3. The rule belongs to a group that's disabled. A disabled group silently disables every rule inside it — the rule list shows a ⚠ next to rules affected by this.
  4. The rule is pinned to a specific tab (📌) and you're testing from a different one.
  5. The request's HTTP method doesn't match the rule's configured method (if it's not set to "Any").
  6. The URL/host pattern doesn't actually match. If the match type is Regex, a typo that makes the pattern invalid means the rule matches nothing rather than throwing an error — double-check it doesn't have unbalanced brackets or other syntax errors.
  7. There's a header condition, body condition or initiator domain condition attached to the rule, and the current request doesn't satisfy it. For header and body conditions on XHR requests specifically: a redirect/query-param/ replace-in-URL rule can never fire based on either, because the URL is decided before the page has set its headers or called send(body) — this only works for the "mock", "cancel", "delay", "modify body" and "modify headers" rule types on XHR (it works for every rule type on fetch). A body condition also only ever matches plain text (JSON, form-encoded, plain strings) — FormData/Blob/binary bodies are never read for it.
  8. The request isn't fetch or XMLHttpRequest. Net Tinker can only intercept those two — it cannot affect <img>, <script src>, CSS, fonts, or any other resource type when it comes to applying a rule (the DevTools capture panel can still see all of those, it just can't act on them).
  9. Another rule earlier in the list matched first. The first matching rule wins — reorder rules if two of them could both apply to the same request.

Things that look like bugs but aren't

Net Tinker works by patching fetch/XMLHttpRequest inside the page's own JavaScript. That approach has a few unavoidable side effects:

Building rules from real traffic

Open DevTools on any page and look for the "Net Tinker" panel (next to Elements, Console, Network, etc.). It lists every request the page makes while the panel is open — not just fetch/XHR, every resource type — so you can find the exact call you want to work with.

  1. Each row has a dropdown of rule types plus a dedicated Mock button. Picking a type opens the rule editor pre-filled with that request's real URL and method.
  2. Mock goes further: it also copies the request's real status code, response headers and response body into the new rule, so you start from what the server actually returned instead of typing a fixture by hand.
  3. A ⚡ mark next to a row means one of your currently-enabled rules would intercept that exact request — evaluated live against your current rules, not against whatever was active when the request actually happened.
  4. Each row also has a "Copy as…" option to copy the request as a curl command or a fetch() snippet, for pasting into a terminal or the console.
  5. For a GraphQL request (detected automatically and shown as its operation name next to the URL), creating a rule from it also fills in a Body condition requiring that operation's name — because a GraphQL endpoint serves every operation at the same URL, a rule matched on URL alone would otherwise apply to all of them. You can see and edit this in the opened rule (it's just the same body condition described above, pre-filled for you).

Captures shown in this panel live only in memory while DevTools is open — closing DevTools clears them, and nothing is written to storage until you explicitly turn a captured request into a rule.

What "sanitize on export" does (and doesn't) cover

When exporting rules to share a setup, the "sanitize" option scans header names, query parameter names, and JSON body field names (recursively) for anything that looks like a credential — names containing words like auth, token, secret, password, apikey, credential, signature, cookie or session — and replaces their value with __REDACTED__.

It deliberately does not touch:

Treat sanitize-on-export as a safety net against the most common accidental leak, not as a guarantee that an exported file contains no secrets — review what you're sharing before you send it.