Drop-in wget replacement
Same flags, same output, same scripts. Add an alias and your existing automation keeps running. Nothing to learn, nothing to rewrite.
v1.0.1 · Linux + macOS
A drop-in wget replacement that automatically bypasses bot protection. When sites return 403, hand you a CAPTCHA, or just refuse to talk to a plain HTTP client, rewget retries with browser-grade fingerprints and, if it has to, a real headless browser.
$ brew install neul-labs/tap/rewget $ rewget https://example.com/file.tar.gz --2026-06-05 10:14:02-- https://example.com/file.tar.gz file.tar.gz 100%[============>] 42.1M 18.6MB/s [stage 1: wget] ok
3
stages of fallback: wget, fingerprint, browser
7 d
per-domain cache so the slow path runs once
6
signed browser profiles: Chrome, Firefox, Safari, Edge
What is rewget?
rewget is a drop-in wget replacement written in Rust. It keeps the wget command-line surface exactly as it is and adds a three-stage fallback: plain wget first, browser-impersonating HTTP second, a real headless browser third. When a site blocks a plain HTTP client with a 403, a CAPTCHA, or a rate limit, rewget quietly retries with something the site will accept.
On sites that never block, you pay nothing — you get wget, byte for byte. rewget also caches which stage works per domain for seven days, so repeated jobs against the same host skip straight to the answer.
$ wget https://protected.example/dataset.zip HTTP request sent, awaiting response... 403 Forbidden ERROR 403: Forbidden.
$ rewget https://protected.example/dataset.zip [stage 1: wget] 403 → escalating [stage 2: rquest/chrome131] 200 OK dataset.zip 100%[============>] 128M 22.4MB/s
How it works
Common case: wget. Blocked case: a browser fingerprint. Hard case: a real browser. The first stage that works wins, and rewget remembers which one it was.
stage 1 · wget
Runs wget (or wget2) with your exact flags. If the site does not block, you get the same output you would have got from wget directly. No daemon spin-up, no browser cost.
stage 2 · impersonate
rewgetd retries through rquest with a Chrome, Firefox, Safari, or Edge fingerprint. Most fingerprint-based blocks fold here without ever touching a real browser.
stage 3 · js preflight
When the page genuinely needs JavaScript, chromiumoxide drives Chromium, solves the challenge, exports cookies and headers, and stage 2 retries with the session.
Want the whole pipeline — CLI shim, rewgetd daemon, the per-domain cache, and signed profile updates?
Read the architecture walkthrough →
What you get
Same flags, same output, same scripts. Add an alias and your existing automation keeps running. Nothing to learn, nothing to rewrite.
When stage 1 returns 403, rewgetd retries the same URL with a real Chrome, Firefox, Safari, or Edge TLS and HTTP/2 fingerprint via rquest.
For pages that genuinely need JavaScript, stage 3 spins up Chromium via chromiumoxide, solves the challenge, exports the session, and resumes the download.
Once rewget learns which stage works for a host, subsequent downloads skip straight there. The common case stays fast; the slow path runs once.
Browser fingerprint profiles are signed and verified on update. New Chrome and Firefox releases roll out through --rewget-update-profiles, not silent network fetches.
rewgetd owns the browser pool and TLS sessions. The CLI stays a thin shim — fast to start, cheap to call from scripts and Makefiles.
Usage
$ rewget https://example.com/file.tar.gz $ rewget --rewget-no-fallback https://example.com/file.tar.gz
$ rewget --rewget-js https://example.com/
$ rewget --rewget-profile=firefox136 https://example.com/
Where it earns its keep
Vendor a tarball in CI that a plain runner IP gets 403’d on. rewget escalates once, caches the result, and your pipeline stops flaking.
A dataset or artifact behind Cloudflare or a JS challenge. Stage 3 solves the challenge and downloads the file — same wget command.
Bulk-fetch across many hosts where some fingerprint TLS. Per-domain caching means each host is probed once, then fast forever.
Honest comparisons
We are not trying to win an argument about wget vs curl. Both are good. rewget keeps the wget shape and fixes the cases where it falls down.
Same CLI, same scripts. rewget is what wget would be if it noticed when a site was actively blocking it.
Different shape. curl is one-shot; rewget (like wget) is download-oriented with resume and recursion. Pick rewget when you want the wget muscle memory to keep working.
FAQ
Yes. The CLI shim parses --rewget-* flags and forwards everything else unchanged to wget (or wget2 if you ask). If you alias wget=rewget, your existing scripts keep working. When a site does not block, you pay zero overhead — you get wget.
Stage 1 runs plain wget. If it succeeds, that is the answer. If it fails on a fallback HTTP status (configurable via --rewget-fallback-codes), stage 2 retries through rquest using a browser TLS and HTTP/2 fingerprint. If stage 2 still fails, stage 3 launches headless Chromium to handle JavaScript challenges. The first stage that works wins.
Chrome 131 and 130, Firefox 136 and 133, Safari 18, and Edge 131. List them with --rewget-list-profiles. Update to the latest set with --rewget-update-profiles (Ed25519 verified).
The README lists Linux and macOS as the platform matrix today. We are not claiming Windows-native support until the release artifacts say so.
Homebrew (brew install neul-labs/tap/rewget), npm (npm install -g rewget), PyPI (uv tool install rewget or pip install rewget), or cargo install rewget from source. There is also a one-line install script in the repo.
Use --rewget-no-fallback. The command fails fast on a block instead of escalating to stage 2 or 3. That is the right behaviour when you want CI to flag a regression instead of hiding it.
No. rewget handles fingerprint-based blocks and JavaScript challenges — the cases where a real browser would succeed and a plain HTTP client would not. If a site requires you to log in, rewget does not change that.
The CLI is. The daemon is a second binary that starts on demand and handles stage 2 and 3. Both ship from the same release; Homebrew, npm, PyPI, and the install script wire them together.
Explore the docs
Every section of the site, one click deep. Start with how it works, or jump to the vocabulary and the questions people ask before adopting it.
The CLI shim, the rewgetd daemon, the fallback pipeline diagram, per-domain caching, and signed profile updates.
The full feature set grouped by category — drop-in compatibility, the three stages, caching, signed profiles, architecture.
Install via Homebrew, npm, PyPI, or cargo, alias wget=rewget, run your first blocked download, and learn the --rewget-* flags.
CI & release mirroring, protected downloads behind Cloudflare, and bulk dataset & mirror fetching — with a detail page each.
Honest, side-by-side comparisons of rewget against bare wget and bare curl on CLI shape, fallback, and platform matrix.
What to do when wget returns 403 even with a user-agent, downloading from JS-challenge sites, and how the fallback affects normal downloads.
TLS fingerprints, JA3/JA4, bot protection, JS challenges, and the rest of the vocabulary rewget works with.
Engineering writing on resumable downloads, shipping a single static binary, and the wget-vs-curl debate.
The thesis — why we built a wget that gets through instead of picking a side in the wget/curl argument.
wget: ERROR 403?Install rewget, alias wget=rewget, and forget about it. When a site lets you in, it is plain wget. When it does not, rewget is already retrying.