ffuf tutorial: fuzzing directories, subdomains/vhosts and GET/POST parameters, how to filter noise with -fc/-fs and calibrate with -mc/-ac, SecLists wordlists and when to use gobuster or dirsearch as alternatives.
Gorka El Bochi
Founder of BBLABS
Quick answer: ffuf (Fuzz Faster U Fool) is a web fuzzer written in Go, blazing fast, that discovers hidden content by replacing the word
FUZZwith each line of a wordlist. It's used to find directories, files, subdomains (vhosts) and parameters. The key isn't launching it, but filtering the noise with-fc,-fsor auto-calibration.
In web recon, a lot of the interesting stuff isn't linked: admin panels, old API endpoints, backup files, staging environments. Fuzzing consists of trying thousands of routes and parameters to discover what you can't see at first glance. ffuf does it very fast and with an excellent filtering system.
The central concept is the keyword FUZZ: you put it where you want ffuf to iterate and it substitutes each entry of the wordlist. It fits naturally into a broader recon flow, after having enumerated domains and subdomains.
Why ffuf and not a browser or a crawler? Because the crawler only follows links that exist in the HTML, and what's interesting in bug bounty is usually precisely what is not linked. Fuzzing blindly tries plausible routes and parameters and discovers that invisible surface. It's one of the first techniques that gives you real findings, and ffuf makes it so fast that you can launch it on every target without it being a bottleneck.
The most common use: discovering routes. You put FUZZ in the path:
ffuf -w /usr/share/seclists/Discovery/Web-Content/common.txt \
-u https://target.com/FUZZ
ffuf fires a request for each word and shows you the status code, size, words and lines of each response. To look for specific extensions, use -e:
ffuf -w wordlist.txt -u https://target.com/FUZZ -e .php,.bak,.old,.zip,.txt
This tests route, route.php, route.bak, etc. The .bak, .old and .zip files often hide source code or juicy backups.
Raw fuzzing returns hundreds of junk responses (404 pages that respond 200, redirects, error templates). Learning to filter is what separates the noise from the findings:
-mc (match code): shows only these codes. E.g. -mc 200,301,302,401,403.-fc (filter code): hides these codes. E.g. -fc 404.-fs (filter size): hides responses of this size in bytes. Perfect when the custom 404 always weighs the same.-fw / -fl: filters by number of words or lines.-ac (auto-calibration): ffuf fires a few fake requests at the start, learns what an "empty" response looks like and filters automatically. The easy button when you don't know what to filter.A realistic example, filtering the custom 404 that always measures 1234 bytes:
ffuf -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt \
-u https://target.com/FUZZ \
-fs 1234 -mc all -t 60
With -t you control the threads (40 by default). Don't overdo it: hammering a small server with 200 threads is a fast way to take it down or get banned.
Vhost fuzzing looks for virtual hosts that respond on the same IP but only if you send the correct Host header (they don't appear via DNS). You put FUZZ in the header:
ffuf -w subdomains.txt \
-u https://target.com \
-H "Host: FUZZ.target.com" \
-fs 0
Since all invalid vhosts usually return the same default page, filter by its size with -fs (or use -ac). The vhosts that respond differently are candidates for internal or staging environments. This complements the passive subdomain enumeration I cover in advanced recon techniques.
ffuf also discovers hidden parameters, the ones the app processes but doesn't document and that often hide IDOR or debug flags.
GET parameter (parameter name):
ffuf -w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt \
-u "https://target.com/api?FUZZ=test" \
-fs 42
POST values (for example, brute-forcing an ID in a form body):
ffuf -w ids.txt \
-u https://target.com/api/user \
-X POST \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "id=FUZZ" \
-mc 200
You can have several wordlists at once with their own keyword names (-w users.txt:USER -w pass.txt:PASS) and use USER and PASS in the same request. With -mode clusterbomb or pitchfork you control how they combine: clusterbomb tries every combination (ideal for credential brute-forcing), pitchfork pairs line with line.
ffuf shows you, per finding: the word, the status code, the size (Size), the words (Words) and the lines (Lines). Learning to read them avoids chasing false positives:
-fs/-fw save the day: you filter by the constant size of that error page./admin that redirects to /login confirms a panel exists.The mental discipline is: what deviates from the target's base behavior? That's what deserves a manual look.
When you discover an interesting directory (/api/, /admin/), you want to fuzz inside it without relaunching by hand. Recursion automates it:
ffuf -w wordlist.txt -u https://target.com/FUZZ \
-recursion -recursion-depth 2 -mc 200,301,403
ffuf enters each found directory and keeps fuzzing to the indicated depth. Use it carefully: recursion multiplies the number of requests very fast.
For fine cases, matching by regex is more powerful than by code or size:
-mr (match regex): shows responses whose body meets an expression, e.g. -mr "admin|dashboard|token".-fr (filter regex): hides those that meet a known error pattern.And on pace, the ethical part: don't hammer the target. Control the volume with -rate (requests per second) and -p (random delay between requests):
ffuf -w wordlist.txt -u https://target.com/FUZZ -rate 50 -p 0.1
Aggressive fuzzing can take down a small server or get you banned from the program. On delicate targets, lower the rate and give notice if the program asks for it.
Fuzzing is only as good as your wordlist. The reference is SecLists. Some I use daily:
Discovery/Web-Content/raft-medium-directories.txt and raft-medium-files.txt — a signal/size balance.Discovery/Web-Content/common.txt — fast for a first pass.Discovery/DNS/subdomains-top1million-5000.txt — subdomains/vhosts.Discovery/Web-Content/burp-parameter-names.txt — parameter names.Save the results with -o output.json -of json to process them later or chain them with other tools.
ffuf doesn't live in isolation: it's a piece within the recon flow. A very productive pattern is fuzzing many hosts at once. With the keyword in the domain itself, you iterate over a list of live subdomains that came from your enumeration:
ffuf -w live-hosts.txt:HOST -w routes.txt:ROUTE \
-u https://HOST/ROUTE -mc 200,301,403 -o findings.json -of json
That way, a /backup/ directory that only exists on a forgotten subdomain shows up without you searching for it host by host. The output JSON can be filtered with jq, fed to Nuclei or sent to your notes.
The discipline that makes the difference: don't dump the output and forget it. Manually review each interesting 200/403 in Burp, because the real finding is almost never "the route exists", but what's inside it. Fuzzing takes you to the door; opening it is manual work.
There's no single correct tool; it's worth knowing the alternatives:
In practice, many hunters use all three depending on the situation. ffuf is the one that yields the most when the scenario goes beyond standard.
Which wordlist do I use if I don't know where to start?
For directories, SecLists' raft-medium-directories.txt is an excellent balance between coverage and time. For a fast first pass, common.txt. Adjust based on what you see.
All my results come out with the same size, what do I do?
It's the target's custom 404 responding 200. Filter it with -fs <that size> or use -ac so ffuf calibrates on its own. It's the step that frustrates beginners most and the one that unlocks the most results.
Does ffuf fuzz real subdomains (DNS)?
ffuf's vhost fuzzing discovers virtual hosts via the Host header, which don't appear via DNS. For subdomain enumeration via DNS/public sources, subfinder or amass are better; ffuf complements by finding the "hidden" vhosts on the same IP.
Is fuzzing legal?
Only with permission. It generates a lot of traffic and touches routes the owner doesn't expose on purpose. Do it in your labs or within the scope of a program that authorizes it.
-t sensibly and -p (delay) if the target is sensitive.-x http://127.0.0.1:8080 to review interesting findings by hand.To practice the full flow —discover surface, understand what you found and exploit it— train with labs that replicate real reports and follow an ordered path. Fuzzing with ffuf is one of the first skills that yields results: the sooner you master it, the sooner you start finding things others don't see.
hunters training
labs from real reports
completions
in bounties practiced
Create your free account and practice on labs based on real reports that paid out thousands of euros. The Academy is free forever.
No card · free Academy · cancel anytime
Everything you need to build a professional bug bounty setup: from choosing your operating system to automating reconnaissance.
Take your reconnaissance phase to the next level with advanced techniques for subdomain enumeration, JavaScript file analysis, GitHub dorking and automation.
Burp Suite tutorial from scratch: what it is, installing and configuring the proxy (127.0.0.1:8080 + CA certificate), Proxy/Intercept, Repeater, Intruder, Decoder, useful extensions, Community vs Pro and a real hunting workflow.