A Firefox extension that intercepts and rewrites HTTP/HTTPS request URLs in real time using named regex rules — supporting before, replace, and after modes with live testing and JSON import/export.
URL Modifier sits between Firefox and the network. Every HTTP and HTTPS request — pages, scripts, images, API calls, WebSockets — passes through your rules before it reaches the server. Each rule matches a pattern in the URL and applies your modification text, in one of three modes:
| Mode | Effect |
|---|---|
| Before | Inserts your text immediately before the matched pattern |
| Replace | Replaces the matched pattern with your text (supports $1, $2 capture groups) |
| After | Appends your text immediately after the matched pattern |
Rules are applied top-to-bottom in order. Patterns are JavaScript regular expressions, with automatic fallback to literal string matching if the regex is invalid.
- Real-time URL rewriting — intercepts all request types:
main_frame,sub_frame,script,image,xmlhttprequest,websocket,media,beacon, and more - Three-layer interception —
webRequest.onBeforeRequest+webNavigation.onBeforeNavigate+tabs.onUpdated, so even cold-start Intent URLs from external apps (e.g. Signal opening a link in Firefox) are always caught - Case-insensitive matching — patterns match regardless of URL capitalisation
- Regex or literal — full JavaScript regex with capture group references, automatic literal fallback for invalid patterns
- Enable / disable per rule — toggle individual rules without deleting them
- Live URL tester — paste any URL and instantly see the rewritten result
- Import / Export — save and load your full rule set as a JSON file
- Toolbar icon — turns green when active, grey when inactive
- Persistent state — rules and active state survive browser restarts
- No data collection — all processing is local; nothing is sent anywhere
- Download the latest signed
.xpifrom the Releases page - Transfer it to your device (email, Google Drive, USB, etc.)
- Open Firefox on Android, tap the address bar, and navigate to the file — or open it from a file manager
- Tap Add when Firefox prompts
Note: The extension requires Firefox for Android 142 or later. Unsigned
.xpifiles require Firefox Nightly. The signed release works on standard Firefox.
From a release (recommended):
- Download the signed
.xpifrom the Releases page - In Firefox, go to
about:addons→ ⚙ gear icon → Install Add-on From File… - Select the
.xpi
Load unpacked (developer mode):
- Go to
about:debugging→ This Firefox → Load Temporary Add-on… - Select
manifest.jsonfrom the cloned repository - Active until Firefox restarts
- Go to your browser's extensions page (
chrome://extensions,edge://extensions, etc.) - Enable Developer mode
- Click Load unpacked and select the repository folder
Note: Chrome Web Store extensions cannot use blocking
webRequest. The extension falls back todeclarativeNetRequestfor CWS-distributed versions. For full functionality, load unpacked.
xcrun safari-web-extension-converter /path/to/url-modifier \
--project-location ~/Desktop \
--app-name "URL Modifier"Open the generated Xcode project, build, and run. The extension appears in Safari's extension preferences.
- Click the URL Modifier icon in the Firefox toolbar
- Click + Add Rule
- Fill in:
- Name — a label for the rule (e.g.
Strip UTM params) - Pattern — a regex or plain text to match in the URL (e.g.
[?&]utm_[^&]*) - Modification — the text to insert or use as a replacement (leave empty to delete the match)
- Name — a label for the rule (e.g.
- Select the Mode: Before, Replace (default), or After
- Click the Activate toggle in the header to start intercepting
| Name | Pattern | Modification | Mode | Effect |
|---|---|---|---|---|
| Strip UTM params | [?&]utm_[^&]* |
(empty) | Replace | Removes ?utm_source=google&utm_medium=… from every URL |
| Force HTTPS | ^http:// |
https:// |
Replace | Upgrades all HTTP links to HTTPS before they load |
| Add CDN prefix | (static\.example\.com) |
cdn. |
Before | static.example.com → cdn.static.example.com |
| Strip trailing slash | /$ |
(empty) | Replace | Removes trailing slashes |
| Redirect subdomain | ^https://old\. |
https://new. |
Replace | old.site.com → new.site.com |
| Remove referrer token | [?&]ref=[^&]* |
(empty) | Replace | Strips referral tracking tokens |
| Swap domain | a\.com |
abc.com |
Replace | Rewrites any a.com URL to abc.com |
- Use
^and$to anchor to the start or end of the URL - In Replace mode, reference capture groups with
$1,$2, etc.
Example: pattern(https?://)old\.(example\.com), modification$1new.$2→ rewrites the subdomain while keeping the protocol and domain - Special characters (
.,*,+,?) must be escaped with\to match literally - The global flag (
g) and case-insensitive flag (i) are always applied — all matches in a URL are replaced, and case doesn't matter - Invalid regex patterns automatically fall back to literal string matching
Rules can be saved and loaded as JSON. Click Export to download your rules, and Import to load a previously saved file. The format is:
[
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Strip UTM params",
"pattern": "[?&]utm_[^&]*",
"modification": "",
"mode": "replace",
"enabled": true
}
]mode must be one of "before", "replace", or "after".
The extension uses three independent interception mechanisms. Any one of them is sufficient to catch a request; together they make missed rewrites effectively impossible:
External app (e.g. Signal) opens a URL
│
▼
┌─────────────────────────────────────────────────────┐
│ Layer 1 — webRequest.onBeforeRequest │
│ Registered synchronously before any async code. │
│ Firefox holds the request open while storage │
│ loads. Catches ~99% of requests. │
└─────────────────────────────────────────────────────┘
│ (if missed)
▼
┌─────────────────────────────────────────────────────┐
│ Layer 2 — webNavigation.onBeforeNavigate │
│ Fires for main-frame navigations slightly later. │
│ Issues a tabs.update() redirect. │
└─────────────────────────────────────────────────────┘
│ (if missed)
▼
┌─────────────────────────────────────────────────────┐
│ Layer 3 — tabs.onUpdated │
│ Last resort. Catches any URL that actually begins │
│ loading. Redirects immediately. May cause a brief │
│ flash of the original URL. │
└─────────────────────────────────────────────────────┘
│
▼
Rewritten URL loads
When an external app opens a URL in Firefox, the OS delivers an Intent and Firefox cold-starts. The background script must be parsed and executed before the listener can fire. The webRequest listener is registered at the top level of the script (not inside an async boot function), so Firefox attaches it as early as possible. If the storage cache isn't warm yet when the first request arrives, the handler awaits the storage promise — Firefox holds the request open during this wait rather than dropping it.
Rules are stored in browser.storage.local. An in-memory cache is kept warm via browser.storage.onChanged, so the listener never needs to hit disk during a request. The cache is initialised null (not empty array) so the handler can distinguish "not loaded yet" from "loaded but no rules", and wait appropriately.
When a URL is rewritten, Firefox fires onBeforeRequest again on the new URL. A Set of recently-redirected URLs prevents the extension from intercepting its own redirects in an infinite loop. Each entry expires after 5 seconds.
url-modifier/
├── manifest.json MV3 manifest
└── src/
├── background/
│ └── background.js Service worker — all interception logic
├── popup/
│ ├── popup.html Toolbar popup UI
│ └── popup.js Popup logic
├── options/
│ ├── options.html Full-page options editor
│ └── options.js Options logic
└── icons/
├── icon-{16,32,48,128}.png Default (inactive) toolbar icons
├── icon-on-{16,32,48,128}.png Active state icons (green)
└── icon-off-{16,32,48,128}.png Inactive state icons (grey)
| Permission | Why it's needed |
|---|---|
storage |
Save rules and active state to browser.storage.local |
webRequest |
Intercept HTTP/HTTPS requests |
webRequestBlocking |
Return a redirect URL from the request handler |
webNavigation |
Layer 2 interception via onBeforeNavigate |
tabs |
Layer 2/3 redirect via tabs.update() |
<all_urls> |
Apply rules to requests on any domain |
This extension collects no user data. Rules are stored locally on your device and never transmitted anywhere.
| Browser | Platform | Min version |
|---|---|---|
| Firefox | Windows, macOS, Linux | 140+ |
| Firefox | Android | 142+ |
| Chrome | All | 88+ |
| Edge | All | 88+ |
| Brave | All | 1.30+ |
| Opera | All | 74+ |
| Vivaldi | All | 5.0+ |
| Safari | macOS, iOS | 16+ (requires Xcode conversion) |
# Clone the repository
git clone https://github.com/gerbilbyte/URL-Modifier.git
cd URL-Modifier
# Package as .xpi (the zip format Firefox expects)
zip -r url-modifier.xpi \
manifest.json \
src/background/background.js \
src/popup/popup.html \
src/popup/popup.js \
src/options/options.html \
src/options/options.js \
src/icons/icon-16.png \
src/icons/icon-32.png \
src/icons/icon-48.png \
src/icons/icon-128.png \
src/icons/icon-on-16.png \
src/icons/icon-on-32.png \
src/icons/icon-on-48.png \
src/icons/icon-on-128.png \
src/icons/icon-off-16.png \
src/icons/icon-off-32.png \
src/icons/icon-off-48.png \
src/icons/icon-off-128.png
# Verify the zip
unzip -t url-modifier.xpiTo distribute on Firefox, the .xpi must be signed by Mozilla. Signed packages can be found in the Releases page.
Pull requests are welcome. For significant changes, please open an issue first to discuss what you'd like to change.
- Fork the repository
- Create a feature branch (
git checkout -b feature/my-change) - Commit your changes (
git commit -m 'Add my change') - Push the branch (
git push origin feature/my-change) - Open a Pull Request

