ffuf Explained — A Fast Web Fuzzer Written in Go thumbnail

ffuf Explained — A Fast Web Fuzzer Written in Go

⏱ approx. 16 min views 16 likes 0 LOG_DATE:2026-06-01
TOC

ffuf (Fuzz Faster U Fool) is a fast web fuzzer written in Go, released by Joona Hoikkala (@joohoi) in 2018. The word "fuzzing" usually evokes throwing malformed input at protocols or binaries, but what ffuf does is brute-force part of an HTTP request against a wordlist to surface "what exists." It replaces a marker called FUZZ placed in the request with each line of a wordlist, then reads the responses to discover hidden directories, files, parameters, subdomains, and virtual hosts. Go's concurrency makes it very fast, so it has become the de-facto tool for the web-enumeration phase of pentests and CTFs.

01

What ffuf is — the FUZZ keyword at its core #

There is exactly one key to understanding ffuf: the FUZZ keyword. Wherever you place that string in a request, ffuf replaces it with each line of the wordlist (-w) and sends the request. Where you put it determines what you are looking for.

Where you place FUZZ What you end up searching for
End of the URL path (/FUZZ) Directory / file discovery
A query/body value (?id=FUZZ) Parameter value brute-force
A query key (?FUZZ=x) Hidden parameter-name discovery
The Host header (Host: FUZZ.target) vhost / subdomain enumeration

For example, brute-forcing https://target/FUZZ with admin / login / backup … makes the paths that actually exist on the server stand out by their status code or response size. ffuf itself doesn't understand "hit vs. miss" from context — it relies on differences in the response for a human to judge the hits. That is exactly why the "matchers and filters" below are its lifeblood.

▸ "Fuzzing" here doesn't mean breaking things

ffuf is mostly about content discovery and enumeration. It points in a different direction from classic fuzzers that crash a target with malformed values — it's closer to "a brute-forcer that fires a dictionary fast and looks for hits."

02

Legal and ethical considerations #

ffuf sends thousands to tens of thousands of requests in a short time. Technically that is a load close to a DoS, and against the wrong target it is clearly an attack. Firing ffuf at someone else's server without permission can be an illegal act under Japan's Unauthorised Computer Access Act or laws against obstruction of business, and the equivalents elsewhere.

▸ Targets you may use ffuf against
  • Servers / apps you own or operate — a local test environment, a VPS you pay for, an isolated learning lab
  • Targets with explicit written permission — a pentest or vulnerability-assessment contract where the scope (hosts, period, rate limits) is documented
  • Legitimate learning platforms — Hack The Box, TryHackMe, PortSwigger Web Security Academy, and similar environments that allow fuzzing

The faster the tool, the more lethal a casual shot at someone else's box becomes. Fire at a production service without a rate limit (-rate / -t) and you can knock it over — leading to damages and criminal liability in the worst case. Always confirm the target and your authorization first.

03

The basics — directory/file discovery and wordlists #

The most basic use is directory/file discovery, placing FUZZ in the URL path.

Minimal directory discovery
$ ffuf -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt -u https://target/FUZZ # -w the wordlist to use # -u target URL (where FUZZ is substituted)

Whether a fuzz run hits or misses is decided by the quality of the wordlist. The de-facto choice is SecLists. Some representative lists by purpose:

Wordlist Purpose
Discovery/Web-Content/raft-medium-directories.txt Directory list based on real-site observations (balanced)
Discovery/Web-Content/raft-medium-files.txt File-name list (raft-* comes in directories/files/words variants)
Discovery/Web-Content/directory-list-2.3-medium.txt The classic large list (from DirBuster)
Discovery/Web-Content/common.txt Light and fast opener (~4,700 entries)
Discovery/DNS/subdomains-top1million-*.txt For subdomain enumeration

To also try extensions, use -e. For each candidate of FUZZ, it additionally sends requests with the given extensions appended.

With extensions + recursion
$ ffuf -w wordlist.txt -u https://target/FUZZ -e .php,.html,.bak,.txt -recursion -recursion-depth 2 # -e also try each candidate with these extensions # -recursion dig further inside directories that hit
04

Matchers and filters — how to kill false positives #

Just "firing" ffuf drowns your results in a sea of false positives. Many servers return a custom 404 page (soft-404) with a 200 status for nonexistent paths, or answer every request identically via a wildcard. Controlling this is what matchers and filters are for. Whether you can wield ffuf at all comes down almost entirely to these two.

  • Matchers (-m*) — show only what matches the criteria (a whitelist)
  • Filters (-f*) — exclude what matches the criteria (a blacklist)
Criterion Matcher Filter
Status code -mc 200,301,403 -fc 404,400
Response size (bytes) -ms 1234 -fs 0,4242
Word count -mw 56 -fw 56
Line count -ml 10 -fl 10
Regex -mr "regex" -fr "regex"
Response time -mt >100 -ft >100

By default ffuf shows -mc 200,204,301,302,307,401,403,405,500. To see only HTTP 200, for instance, state it explicitly with -mc 200. Multiple codes are comma-separated (-mc 200,301,403), and -mc all captures every status.

Matcher — show only 200 responses
$ ffuf -w wordlist.txt -u https://target/FUZZ -mc 200 # -mc 200 show only responses with status 200 # -mc 200,301 show only 200 and 301 (comma-separated) # -mc all capture every status (pair with filters or -ac)
▸ The field rule — observe the shape of a "miss" first

Manually hit a nonexistent path once (e.g. /zzz-not-exist-123) and note its status, size, and word count. If misses are constant at "200 / 4242 bytes / 56 words," exclude them wholesale with -fs 4242 or -fw 56, and whatever remains is a candidate hit. Size and word-count filters are often more powerful than status-code ones.

When miss responses vary per candidate and can't be killed with a fixed value, hand it to auto-calibration.

Auto-calibration to strip soft-404s automatically
$ ffuf -w wordlist.txt -u https://target/FUZZ -ac # -ac send a few dummy requests at startup, learn the # "miss response," and turn it into a filter automatically # pair with -mc all to capture every status and let -ac drop misses

-ac first hits a few random, nonexistent paths and registers their responses (size, word count, etc.) as "this is a miss" into an automatic filter. On servers that return wildcard responses or soft-404s, having it or not makes a night-and-day difference in how readable the results are.

05

Fuzzing modes and attack modes #

Where you place FUZZ switches the mode. The four representative ones:

Virtual-host / subdomain enumeration #

When one IP hosts multiple domains (virtual hosting), swap the Host header to find hidden sites — including internal hosts that aren't in DNS.

vhost fuzzing
$ ffuf -w subdomains.txt -u https://target.com/ -H "Host: FUZZ.target.com" -fs 4242 # substitute FUZZ in the Host header; drop the miss size with -fs

Discovering GET parameters #

Placing FUZZ in the query string lets you find hidden parameter names the app accepts (e.g. a debug flag, a file selector).

Fuzzing parameter names / values
# find parameter names $ ffuf -w params.txt -u https://target/page?FUZZ=test -fs 0 # brute-force a parameter value (e.g. finding valid id values) $ ffuf -w ids.txt -u https://target/page?id=FUZZ

POST data / login brute-force #

With -X POST and -d you can put FUZZ in the body to brute-force a login form's password, for example. Drop the failure responses with -fc / -fs and keep only the successes.

Brute-forcing a login form
$ ffuf -w passwords.txt -u https://target/login -X POST \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "username=admin&password=FUZZ" -fc 200 # if a failed login returns 200, drop it and the success (e.g. 302) remains

Multiple FUZZ and attack modes #

Name your wordlists (-w list.txt:KEYWORD) to brute-force several positions at once. As in Burp Suite's Intruder, the attack mode (-mode) chooses how they combine.

Attack modeBehaviorTypical use
clusterbombAll combinations across wordlists (cartesian product)Every username × password pair
pitchforkConsumes each list in parallel by row indexValidating paired user/pass entries
sniperApplies a single list one position at a timeTesting several candidate positions with one dictionary
clusterbomb for every user × pass combination
$ ffuf -w users.txt:USER -w pass.txt:PASS \ -u https://target/login -X POST \ -d "username=USER&password=PASS" -mode clusterbomb -fc 200
06

Operational tips — rate, output, disguise #

The main options for tuning how you fire. Rate control especially matters — to avoid knocking the target over, to evade detection, and simply as courtesy.

Option Effect
-t 40 Concurrent threads (default 40). Lower it for slower, lighter load
-rate 50 Max requests per second. Near-mandatory for real engagements
-p 0.1 Delay between requests (seconds). -p "0.1-0.5" randomizes it
-H "..." / -b "..." Add headers / cookies (to explore authenticated pages)
-x http://127.0.0.1:8080 Through a proxy (pipe into Burp to observe and record)
-o out.json -of json Write results to a file (json / html / csv / md, etc.)
-ic Ignore # comment lines in the wordlist
-c -v Color + verbose (clearer hit URLs and redirect targets)
-timeout 10 Request timeout in seconds
▸ The footprint ffuf leaves for defenders

An ffuf scan is easy to detect: access logs fill with many 404/403 responses in a short window, and if the User-Agent is left default, a Fuzz Faster U Fool-style string remains. A WAF, rate limiting, or 404 monitoring will stop most of it. Attackers dodge this with -rate throttling or -H UA spoofing — but in a legitimate engagement leaving a trace is not the problem; staying within authorized scope is the prerequisite.

07

Where ffuf sits among similar tools #

There are several web-content discovery tools; ffuf's distinguishing traits are speed + FUZZ anywhere + powerful filters.

ToolLanguageCharacteristics
ffufGoFast. General fuzzer that puts FUZZ anywhere. Rich matchers/filters
gobusterGoFast. Simple, mode-based design (dir/dns/vhost)
feroxbusterRustFast + strong recursion. Good for digging deep automatically
wfuzzPythonffuf's ancestor. Flexible but slow; being replaced by ffuf
dirb / dirbusterC / JavaClassics. Slow but mature. Linger for learning/compatibility
▸ The rule for wielding ffuf well

(1) Open with a light list (common.txt) → (2) observe the shape of a miss and set filters (-fs / -fw / -ac) → (3) dig into directories that hit with recursion or a bigger list. That staged approach is the standard. Far more than raw speed, setting filters correctly so the output is readable is what matters — much more than firing the largest list at full force from the start.