AI-Created Software

The following AI-generated aka vibe-coded software. The software is specific to my needs and my system and may not work for you or your system. Support is limited, but if you report a bug I'll certainly look into it. All code is provided in a zip file for you to review should you choose.

Header Injector Pro icon

Header Injector Pro

A Chrome extension (Manifest V3) that adds custom HTTP request headers to the domains you choose, and shows you which of your rules are actually firing.

Useful for hitting a staging API that needs an auth token, testing feature flags behind a header, or faking a X-Forwarded-For against your own dev server. Rules are applied by Chrome’s declarativeNetRequest engine, so headers are set before the request leaves the browser and nothing in your page code changes.

Features

  • Per-rule switches. Keep eight rules on file and run only the two you need.
  • Match counts. Each rule shows how many requests it has hit and when, so a typo’d header or a filter that matches nothing is visible instead of silent.
  • Precise domain matching. A domain rule matches that host and its subdomains, not any URL that happens to contain the string.
  • Import and export. Save a set of rules as JSON, commit it next to the project it belongs to, hand it to a teammate.
  • Rule manager. A full-page view with filtering, editing and duplication.
  • Duplicate a rule and edit the copy, rather than retyping a long token.
  • One master switch to pause everything without deleting anything. The toolbar icon greys out and the badge clears while paused.

Install

An unpacked, developer-mode extension. There is no store listing.

  1. Open chrome://extensions
  2. Turn on Developer mode (top right)
  3. Click Load unpacked and select this folder
  4. Pin the extension so the toolbar icon and badge stay visible

After editing any file, hit the reload arrow on the extension’s card.

Using it

The popup is for quick work: add a rule, flip one on or off, glance at what’s firing. Manage all rules opens the full manager, which adds filtering, import, export and the last URL each rule matched.

A rule is three things:

Field Example Notes
Domain or URL pattern api.staging.example.com See matching, below
Header name Authorization Case-insensitive
Header value Bearer eyJhbGci… May be empty

Matching

Domain is the default and covers most cases. It uses requestDomains, which matches the host and any subdomain of it, on any port and any path. So example.com matches https://api.example.com/v1 but not https://other.test/?ref=example.com.

URL pattern is the escape hatch, using Chrome’s urlFilter syntax:

Pattern Matches
||localhost:3000 localhost on port 3000 only
https://api.example.com/v2* one path prefix
*/graphql any host, that path

|| anchors the host, * matches anything, | anchors the start or end of the URL. Type a port, a path or a * into the domain field and the rule is converted to a URL pattern for you, with a note saying what it became.

Rules apply to main frames, sub frames, XHR/fetch and scripts.

Match counts

The count next to a rule is how many requests it has modified since the browser started. It comes from onRuleMatchedDebug, which Chrome only reports for unpacked extensions — pack this and the counts stay empty while the rules themselves keep working. Counts live in session storage and reset when Chrome restarts; Clear counts resets them by hand.

If a rule shows “Rejected”, Chrome refused it. The message says why, and the other rules keep working: rules are re-applied individually when a batch fails, so one bad rule can’t take the rest down with it.

Import and export

Export writes every rule, including the disabled ones, to a JSON file. Import offers to add them to what you have or replace everything.

{
  "format": "header-injector-pro/rules",
  "version": 1,
  "exportedAt": "2026-09-08T12:00:00.000Z",
  "rules": [
    { "enabled": true, "matchType": "domain", "match": "api.example.com",
      "key": "Authorization", "value": "Bearer …" }
  ]
}

Rule ids are deliberately left out so an imported file can never collide with rules you already have. A bare array of rules is also accepted, as is the old {"domain": …} field name.

Exports contain header values in plain text. They are usually credentials. Treat the file accordingly and keep it out of version control.

Files

manifest.json     Permissions, worker, icons, options page
background.js     Service worker: rule sync, match tracking, toolbar state
popup.html/.js    Quick add, per-rule switches, live match counts
options.html/.js  Full manager: filter, edit, duplicate, import, export
lib/store.js      Rule model, validation, storage, import/export  (pure + tested)
lib/dom.js        DOM builders, icons, relative times, toasts
lib/ui.css        Design tokens and shared components
icons/            Toolbar and store artwork, plus icon.svg source
tools/            make_icons.py, regenerates every PNG from one definition

Regenerating the icons

tools/make_icons.py renders all eight PNGs on a 2048px master and downsamples them, writing straight into icons/.
Needs Pillow and NumPy:

pip install pillow numpy && python3 tools/make_icons.py

Sizes 16 and 32 are drawn from a simplified two-line variant with a solid arrowhead, because the three-line mark and its thin chevron blur together that small. Keep both the active and -off sets: background.js names all eight paths and setIcon throws if one is missing.
icons/icon.svg is the vector source for the large variant.

lib/store.js holds everything that decides what a rule means, so the worker and both pages can’t drift apart. The top half is pure and has no dependency on the chrome namespace, which makes it testable outside the browser.

Upgrading from 1.2

Stored rules migrate automatically the first time 1.3 reads them.
Each gains an id and an on switch, and the old domain field is re-read: plain domains become domain matches, anything with a wildcard becomes a URL pattern.

Matching got stricter. Version 1.2 wrapped every domain as *domain* and substring-matched it against the whole URL. If you were relying on that — a rule whose “domain” was really a path fragment, say — it will stop matching. Switch that rule to URL pattern mode and write the pattern explicitly.

Permissions

Permission Why
declarativeNetRequest Registers the header rules with Chrome’s network engine
declarativeNetRequestFeedback Reports which rules matched, for the counts
storage Saves rules locally, and match counts for the session
<all_urls> modifyHeaders rules need host access to the sites they touch

Nothing leaves your machine. Rules are in chrome.storage.local, counts are in chrome.storage.session, and neither syncs to your Google account.

Limitations

  • Request headers only. Response headers would need a responseHeaders block in the rule action.
  • Set, not append or remove. Each rule overwrites a header of that name.
  • Some headers are off limits. Chrome won’t let extensions touch Host, Connection and friends; the form rejects those names at entry.
  • CORS preflight. Adding a non-simple header to a cross-origin request triggers a preflight the server has to allow.
  • No profiles yet. Rules are one flat list.
    Import/export covers the switch-between-environments case for now.
  • Match counts are best-effort. They are buffered and written in batches, so a burst of requests may take a moment to show up.

Debugging

  • Service worker logs: chrome://extensionsService worker on the card

  • What is actually registered: run this in the service worker console

    chrome.declarativeNetRequest.getDynamicRules().then(console.table)
  • Confirm the header on the wire in DevTools → Network → the request → HeadersRequest headers. DevTools sometimes shows provisional headers for cached requests, so do a hard reload.

Tests

The pure half of lib/store.js has a test suite covering normalisation, the v1 migration, id stability and the import parser.
With Node 18+:

cp lib/store.js lib/store.mjs && node test_store.mjs && rm lib/store.mjs
(1 votes, average: 5.00 out of 5)

Leave a Reply

Your email address will not be published. Required fields are marked *

Notify me of followup comments via e-mail.