h3fuzz is a content discovery tool with automatic HTTP/3 acceleration and a persistent web console. It is a fork of ffuf, so existing ffuf targets, wordlists, matchers, filters, request files, and most command-line workflows remain available.
The normal HTTPS protocol order is HTTP/3, then HTTP/2, then HTTP/1.1. Before a
scan, h3fuzz can also qualify HEAD or Range: bytes=-1 as a smaller response
strategy. It uses either strategy only when repeated baseline requests show
that the result metadata remains reliable.
Run h3fuzz only against systems that you own or have explicit permission to test. Content discovery, especially at high request rates, can disrupt a service, trigger defenses, consume bandwidth, and create infrastructure cost. Start with conservative settings on an unfamiliar target and coordinate load testing with its operator.
Tom Stacey's PortSwigger research, HTTP/3 in Burp Suite: it's time to find a bigger wordlist, showed how response minimization, multiplexed QUIC streams, and multiple HTTP/3 connections can raise the ceiling for request-heavy security testing. PortSwigger reported more than 100,000 requests per second from a laptop to a remote host over Wi-Fi, and about 180,000 requests per second from a cloud host in the same region as the target.
h3fuzz applies those ideas to ffuf's content-discovery model. It is an independent Go implementation informed by the research and by the open source Turbo Intruder and HTTP3-Adapter projects. It does not copy Turbo Intruder's request engine and does not claim that every target can sustain the rates reported in the research.
For an eligible HTTPS target, an automatic scan:
- Negotiates QUIC with ALPN
h3, receives HTTP/3 settings, checks connection reuse, and runs a bounded concurrent-stream probe. - Uses an HTTP/3 connection pool when qualification succeeds. Otherwise it negotiates HTTP/2 over TLS and falls back to HTTP/1.1 when needed.
- Compares repeated full
GETbaselines withHEAD, then with a final-byte Range request ifHEADis unreliable. - Runs the content-discovery workload with the selected protocol and response strategy.
- Falls back to a full
GETfor an individual optimized request when its live response no longer matches the qualified behavior.
Protocol choice and response minimization are independent. A scan can use
HTTP/3 with full GET responses, or a qualified smaller response over HTTP/2
or HTTP/1.1.
Prebuilt archives and checksums are available from
GitHub Releases. Release
archives include the h3fuzz binary, final.txt, documentation, license files,
and the local performance harness.
Common archive names are:
| Platform | Archive |
|---|---|
| Linux x86-64 | h3fuzz_0.1.0_linux_amd64.tar.gz |
| Linux ARM64 | h3fuzz_0.1.0_linux_arm64.tar.gz |
| macOS Apple silicon | h3fuzz_0.1.0_macOS_arm64.tar.gz |
| macOS Intel | h3fuzz_0.1.0_macOS_amd64.tar.gz |
| Windows x86-64 | h3fuzz_0.1.0_windows_amd64.zip |
The GitHub CLI can download a specific archive and its checksum file. This
example downloads the Linux x86-64 release. Authenticate first with
gh auth login when the repository requires GitHub access:
version=0.1.0
asset="h3fuzz_${version}_linux_amd64.tar.gz"
gh release download "v${version}" \
--repo assetnote/h3fuzz \
--pattern "$asset" \
--pattern "h3fuzz_${version}_checksums.txt"
grep " ${asset}$" "h3fuzz_${version}_checksums.txt" | sha256sum -c -
mkdir "h3fuzz-${version}"
tar -xzf "$asset" -C "h3fuzz-${version}"
cd "h3fuzz-${version}"
./h3fuzz -V
./h3fuzz -webOn macOS, use the appropriate macOS archive and replace sha256sum with
shasum -a 256. On Windows, download the matching .zip, verify its SHA-256
value against the checksum file, extract it, and run h3fuzz.exe -web from the
extracted directory. Keeping the extracted files together makes the bundled
final.txt available as the default wordlist.
The module currently requires Go 1.26. The compiled web assets are checked in and embedded in the binary, so a normal build does not require Node.js.
git clone https://github.com/assetnote/h3fuzz.git
cd h3fuzz
go build -o ./h3fuzz .Verify the binary:
./h3fuzz -V
./h3fuzz -hTo change the web application, install pnpm, rebuild the frontend, then rebuild the Go binary:
pnpm --dir frontend install --frozen-lockfile
pnpm --dir frontend build
go build -o ./h3fuzz .The web console is the recommended way to use h3fuzz for interactive content discovery:
./h3fuzz -webThe process prints the console URL, username, and password. By default:
- the console listens on
https://127.0.0.1:8787 - the username is
h3fuzz - TLS uses a locally generated self-signed certificate
- the password is generated once for the selected database and reused on later runs
- sessions, results, settings, terminal history, and saved views persist in SQLite
Open the printed URL, accept the local certificate on first use, and enter the printed credentials. Basic authentication also protects the REST API and live WebSocket connection.
Use an explicit database path when you want the state in a known location:
./h3fuzz -web -web-db ./h3fuzz-web.dbThe generated certificate and key are stored beside that database unless
-web-cert and -web-key select an existing TLS identity. Protect the
database and its backups because the database contains the Basic Auth secret
and may contain request headers or cookies.
Select the plus button and complete the single-page scan form:
- Enter an absolute target containing a fuzz keyword, such as
https://target.example/FUZZ. h3fuzz does not addFUZZautomatically. - Keep
final.txtor select another wordlist readable by the h3fuzz process. - Select the ASP Minimal, ASP / ASP.NET, PHP, JSP / Java, or Directories profile and review the resulting extensions.
- Review matchers and filters. The bundled profiles match all statuses and
filter
404responses. Status, size, word, line, time, and regular expression controls are available. - Enable the Akamai target preset only when browser-like client hints and fetch metadata are needed. Its headers remain visible and editable.
- Leave protocol mode and response minimization on Automatic unless the target requires an override.
- Search Advanced options when an inherited ffuf flag is not among the common controls.
- Select Create and run, or create an Idle session for later use.
final.txt contains DirSearch-style %EXT% entries. The web defaults enable
DirSearch compatibility so those entries expand using the selected profile.
The web console's regular HTTP worker baseline is 1,000. When HTTP/3 is
selected and -t was not explicitly set, the adaptive worker supervisor sizes
request workers from the negotiated connection and stream capacity.
The form also provides a copyable CLI preview. A compatible h3fuzz or ffuf command can be pasted into the same form to populate its fields.
The console supports multiple concurrent, persistent sessions. Each session provides:
- live status, progress, rolling rate, 30-second average, overall average, peak rate, ETA, request, response, and error counters
- a protocol preflight report with negotiation, response-strategy, capacity, timing, and downgrade evidence
- pause, resume, stop, clone, rerun, archive, pin, compare, and bulk session actions
- server-side result search, sorting, filters, facets, saved views, and virtualized rendering
- list and collapsible directory-tree result views
- result details with copy URL, copy path, copy cURL, and open target actions
- categorized transport errors and representative diagnostics
- searchable, virtualized terminal history with severity and timestamp controls
- JSON, path-only text, and terminal exports with preview, copy, and download
- inherited defaults and named presets for wordlists, profiles, headers, matching, filtering, and protocol settings
Filters can be added after a scan starts. They are saved with the session and applied by the server across the complete result set, not only the rows loaded in the browser.
| Flag | Purpose | Default |
|---|---|---|
-web |
Start the web console | off |
-web-listen HOST:PORT |
Set the listen address | 127.0.0.1:8787 |
-web-db PATH |
Set the SQLite database path | platform XDG data path |
-web-cert PATH |
Use an existing PEM certificate | generated beside the database |
-web-key PATH |
Use an existing PEM private key | generated beside the database |
-web-allow-remote |
Permit a non-loopback listener | off |
-web-plain-http |
Disable TLS for a trusted local reverse proxy | off |
-web-allow-input-cmd |
Permit web sessions to execute -input-cmd |
off |
The server refuses a non-loopback listener unless -web-allow-remote is also
set. Prefer an SSH tunnel instead of exposing the console directly:
ssh -L 8787:127.0.0.1:8787 user@scanner-hostIf direct binding is required, restrict network access and provide a trusted certificate whose subject alternative names cover the listen host:
./h3fuzz -web \
-web-listen scanner.example.com:8787 \
-web-allow-remote \
-web-cert /secure/scanner.example.com.crt \
-web-key /secure/scanner.example.com.keyPlain HTTP sends Basic Auth credentials without transport encryption. Use
-web-plain-http only on loopback behind a trusted local TLS reverse proxy.
The database stores result metadata, not raw HTTP response bodies. Large scans can still consume substantial space when most responses are retained. Filter the target's normal not-found response, delete sessions that are no longer needed, and use Compact database after deletion when disk space must be returned to the operating system.
The CLI remains useful for automation, existing ffuf workflows, and controlled throughput measurement.
./h3fuzz \
-u https://target.example/FUZZ \
-w final.txt \
-D \
-e html,js \
-mc all \
-fc 404-D enables DirSearch compatibility so %EXT% markers in final.txt are
expanded. -e alone keeps ffuf's append-extension behavior and does not
replace those markers.
Common ffuf-compatible patterns include:
# Virtual-host discovery
./h3fuzz -u https://target.example/ -w hosts.txt -H 'Host: FUZZ.target.example'
# Query-value discovery
./h3fuzz -u 'https://target.example/search?q=FUZZ' -w values.txt
# JSON POST body
./h3fuzz -u https://target.example/api -w values.txt \
-X POST -H 'Content-Type: application/json' -d '{"name":"FUZZ"}'
# Match every status, then remove a known wildcard response by word count
./h3fuzz -u https://target.example/FUZZ -w paths.txt -mc all -fw 42Run ./h3fuzz -h for the complete inherited and h3fuzz-specific flag list.
- A scan must contain an active fuzz keyword in its URL, header, body, or other
configured input. The web form intentionally does not append
FUZZ. final.txtrequires DirSearch compatibility and at least one extension when its%EXT%entries are used. In the CLI, use-D -e ....- The Errors counter reports inputs that did not produce a usable response,
such as malformed expansions, DNS or transport failures, and timeouts. HTTP
403,404, and500responses are normal responses handled by matchers and filters. - Large numbers of retained matches can make rendering or SQLite persistence the bottleneck. Verify the target's wildcard response and filter that known noise before drawing transport-performance conclusions.
- Check the Preflight tab or CLI banner for the negotiated protocol and downgrade reason. A server advertising HTTP/3 is not enough by itself to pass direct negotiation and the bounded capability checks.
Automatic mode uses the fastest protocol that it proves usable:
HTTP/3 -> HTTP/2 -> HTTP/1.1
| Intent | Flag |
|---|---|
| Use automatic selection | no protocol flag |
| Print the HTTP/3 capability report and exit | -http3-fingerprint |
| Require HTTP/3 and fail on downgrade | -http3 |
| Skip HTTP/3, prefer HTTP/2, allow HTTP/1.1 fallback | -http2 |
| Disable only the automatic HTTP/3 probe | -no-http3 |
| Require HTTP/1.1 and disable HTTP/2 negotiation | -http1 |
Example fingerprint:
./h3fuzz -u https://target.example/FUZZ -http3-fingerprintPlain http:// targets use HTTP/1.1. h3fuzz does not automatically negotiate
cleartext HTTP/2 (h2c). Proxies also prevent the direct QUIC probe because it
would bypass the configured route. See
Protocol selection and fallback for exact mode,
fallback, conflict, and regression-test behavior.
For an eligible bodyless GET, h3fuzz compares three full baselines with its
HEAD and Range probes. It keeps full GET when status, logical size, word or
line counts, matchers, filters, scrapers, or changing behavior make the smaller
strategy unreliable.
Disable this qualification when exact response bodies are required:
./h3fuzz -u https://target.example/FUZZ -w paths.txt \
-no-response-optimizationPortSwigger's result is a research measurement, not a universal h3fuzz guarantee. Request rate depends on latency, response strategy, input duration, server capacity, client and server CPU, UDP buffers, packet loss, matchers, filters, output, and result persistence.
h3fuzz has exceeded 100,000 completed responses per second in privately retained local Darwin diagnostic cohorts. The later five-run current-source cohort recorded a median of 127,008.228 completed-response RPS, with every run above 100,000 for more than 40 seconds and exact client and origin accounting. The raw benchmark bundles are not distributed in this repository. The recorded metrics and hashes demonstrate the controlled local path, not arbitrary remote performance. This is not the pending native-Linux separated-host release certification. Co-located native-Linux profiles were around 60,000 RPS and showed that the shared client and origin CPU budget was the limiting environment.
For an authorized, controlled ceiling test, use the CLI-only high-rate policy with a long flat wordlist and a known baseline filter:
./h3fuzz \
-u https://target.example/FUZZ \
-w /path/to/long-flat-wordlist.txt \
-http3-high-rate \
-http3-high-rate-acknowledge \
-http3-target-rps 100000 \
-http3-max-connections 64 \
-http3-streams 256 \
-http3-max-workers 16384 \
-mc all -fc 404 \
-run-telemetry run.jsonThe policy requires direct HTTP/3, automatic response qualification, a finite
single wordlist, structured telemetry, no explicit -t, and explicit operator
acknowledgement. It rejects incompatible options and fails before load when the
declared connection, stream, worker, and measured-latency bounds cannot supply
the requested target. A target RPS value is a sizing objective, not a promise.
Read the performance and certification guide before interpreting a rate. It links the implementation analysis, retained measurements, harness contract, action plan, and remaining certification work.
The repository includes a Docker-based origin with HTTP/1.1, HTTP/2, HTTP/3, TCP-only, UDP-only, reliable and misleading response-optimization routes, and instrumented request accounting.
With Docker Compose v2, Bash, Python 3, and a built binary:
docker compose -f harness/compose.yaml up --build --wait
harness/scripts/smoke.sh
harness/scripts/correctness.sh
harness/scripts/benchmark.sh --repeat 1 --mode auto --connections 0
docker compose -f harness/compose.yaml downThese checks validate protocol selection, multiplexing, response strategy, fallback, logical and transport accounting, and clean drain behavior. A local harness run does not prove that a remote target will sustain the same rate. See the complete harness guide.
- Documentation index
- Protocol selection and fallback
- Performance and certification
- HTTP/3 implementation and 100k RPS analysis
- 100k RPS implementation action plan
- Harness reference
h3fuzz is built on ffuf, created by Joona Hoikkala and contributors, and retains ffuf's MIT license and copyright notice.
The HTTP/3 approach credits Tom Stacey and PortSwigger:
- HTTP/3 in Burp Suite: it's time to find a bigger wordlist
- PortSwigger Turbo Intruder
- Tom Stacey's HTTP3-Adapter
HTTP/3 transport support uses a pinned fork of quic-go. See Third-party notices for dependency provenance and license details.
See LICENSE.