July 11, 2026
Mastering FFUF: The Complete Guide to Fast Web Fuzzing with FFUF (Fuzz Faster U Fool)
From directory brute-forcing to parameter mining, virtual host discovery and autocalibration—every flag explained with a working example…
By SHIx9
16 min read
From directory brute-forcing to parameter mining, virtual host discovery and autocalibration—every flag explained with a working example, organized the way the tool actually works.
Most people meet FFUF as a directory brute-forcer, run one command, and stop there. That's a mistake it means using maybe 15% of what the tool actually does.
FFUF (Fuzz Faster U Fool) is a general-purpose HTTP fuzzing engine written in Go. The core idea is simple: you give it a request template with a keyword inside it and a wordlist to substitute into that keyword. Where you place the keyword URL path, header, POST body or even the Host header determines what kind of attack you're running. Directory brute-forcing, virtual host discovery, parameter mining, and header fuzzing are all the same engine, just pointed at different parts of a request.
This guide walks through the tool section by section, the way you'd actually learn it: what it is, how requests are built, how responses are filtered, and where each attack surface lives. Every flag gets a working example nothing here is theoretical.
01 · What FFUF Actually Is
"General-purpose" is the key phrase. FFUF isn't a directory scanner with extra features bolted on it's a template-substitution engine. You give it a request shape with a keyword inside it, and a source of words to substitute in. The position of that keyword decides the attack type.
Two things make it fast in practice, not just in the name:
- Go's concurrency model. Each request runs in its own goroutine, scheduled by Go's runtime rather than relying on OS threads directly. That's why you can push 40–100+ concurrent requests without the overhead you'd see in thread-per-request tools written in other languages.
- Matcher and filter logic runs inline, not after collecting everything. Combined with autocalibration, FFUF discards noise as it goes instead of dumping thousands of lines for you to grep through afterward.
If you only ever run -w wordlist -u URL/FUZZ, you're skipping the part of the tool that separates it from a basic wordlist runner: the matcher/filter/autocalibration pipeline and the fact that FUZZ can go anywhere in a request.
02 · Installation
Three real installation paths pick whichever fits your setup.
Option A — Prebuilt binary (fastest):
# grab the latest release for your OS/arch from GitHub releases, then:
chmod +x ffuf
sudo mv ffuf /usr/local/bin/# grab the latest release for your OS/arch from GitHub releases, then:
chmod +x ffuf
sudo mv ffuf /usr/local/bin/Option B — Go install (if you have Go 1.16+):
go install github.com/ffuf/ffuf/v2@latest
# the same command re-run later updates itgo install github.com/ffuf/ffuf/v2@latest
# the same command re-run later updates itOption C — Kali / Debian package:
sudo apt update && sudo apt install ffufsudo apt update && sudo apt install ffufVerify with ffuf -V. On Kali, the wordlists you'll actually reach for live under /usr/share/seclists/ and /usr/share/wordlists/ SecLists' Discovery/Web-Content/ directory is the one you'll use most.
03 · Core Syntax & the FUZZ Keyword
Everything in FFUF revolves around one idea: place the literal string FUZZ anywhere in the request URL, header or POST body and FFUF substitutes each line of your wordlist into that exact position, fires the request, and evaluates the response against your matcher/filter rules.
ffuf -u https://target.com/FUZZ -w /path/to/wordlist.txtffuf -u https://target.com/FUZZ -w /path/to/wordlist.txtTwo flags are mandatory for any run: a target that contains FUZZ somewhere and a wordlist to feed it.
Custom keywords. You're not locked to the literal word FUZZ. Attach a keyword name to a wordlist with a colon, and reference that keyword in the request instead. This becomes essential the moment you fuzz more than one position at once:
ffuf -w wordlist.txt:MYKEYWORD -u https://target.com/MYKEYWORDffuf -w wordlist.txt:MYKEYWORD -u https://target.com/MYKEYWORDMental model: think of FFUF as "curl with a for-loop and a decision engine bolted on." Anywhere you'd put a value in a curl command — the path, a header or the body you can drop FUZZ instead, and the decision engine (matchers + filters) decides what's worth showing you.
04 · Directory & File Discovery
The most common entry point: enumerate hidden paths on a web server. There's no DNS-style zone transfer equivalent for URL paths, so brute-forcing is genuinely the only discovery method available.
ffuf -u http://target.com/FUZZ \
-w /usr/share/seclists/Discovery/Web-Content/common.txt \
-mc 200,301,302,403 -fc 404ffuf -u http://target.com/FUZZ \
-w /usr/share/seclists/Discovery/Web-Content/common.txt \
-mc 200,301,302,403 -fc 404Once a directory hits (say /admin returns 301), the natural next move is fuzzing inside it, either manually or with recursion (§06):
ffuf -u http://target.com/admin/FUZZ -w admin-panels.txt -mc 200,301,302ffuf -u http://target.com/admin/FUZZ -w admin-panels.txt -mc 200,301,302Wordlist choice is the actual skill. The flag syntax is trivial. What separates a useful scan from noise is the wordlist: raft-*, directory-list-2.3-*, or a target-specific list built from JS files, sitemap.xml, and robots.txt beats any generic list. Generic wordlists on a small custom app return almost nothing; a scraped, tech-stack-specific list finds real paths.
05 · Extension Fuzzing
The -e flag appends a comma-separated extension list to every FUZZ substitution. This effectively multiplies your wordlist by the number of extensions given.
Fingerprinting the default page's backend language:
ffuf -u http://10.201.83.65/indexFUZZ \
-w /usr/share/seclists/Discovery/Web-Content/web-extensions.txtffuf -u http://10.201.83.65/indexFUZZ \
-w /usr/share/seclists/Discovery/Web-Content/web-extensions.txtThis fuzzes the extension directly onto index useful for fingerprinting which server-side language is behind the default page (.php, .asp, .jsp, etc.) without guessing.
Standard extension fuzzing:
ffuf -u http://10.201.83.65/FUZZ \
-w wordlist.txt -e .php,.bak,.txt,.oldffuf -u http://10.201.83.65/FUZZ \
-w wordlist.txt -e .php,.bak,.txt,.oldThis tries every wordlist entry both bare and with each extension appended — admin, admin.php, admin.bak, admin.txt, admin.old.
DirSearch compatibility mode. If you're reusing wordlists built for dirsearch that contain the %EXT% placeholder token, pass -D alongside -e. FFUF then replaces %EXT% with each extension instead of blindly appending — this matters for entries like config.%EXT%.bak, where a naive append would break the pattern.
ffuf -u http://target.com/FUZZ \
-w dirsearch-wordlist.txt -e php,bak -Dffuf -u http://target.com/FUZZ \
-w dirsearch-wordlist.txt -e php,bak -D06 · Recursive Fuzzing
Recursion means when a discovered path looks like a directory, automatically kick off a new fuzzing job inside it no manual re-running required. The URL passed to -u must end in the FUZZ keyword for this to work.
ffuf -u http://target.com/FUZZ \
-w /usr/share/seclists/Discovery/Web-Content/common-dirs.txt \
-recursion -recursion-depth 2 \
-mc 200,301 -fc 404 \
-t 20 -o ffuf_rec.json -of jsonffuf -u http://target.com/FUZZ \
-w /usr/share/seclists/Discovery/Web-Content/common-dirs.txt \
-recursion -recursion-depth 2 \
-mc 200,301 -fc 404 \
-t 20 -o ffuf_rec.json -of json-recursion— enable recursive scanning (default: off)-recursion-depth— max levels deep.0means unlimited dangerous on large targets without other constraints.-recursion-strategy—defaultrecurses only on redirect responses (a 301 to a trailing slash implies "this is a directory").greedyrecurses into any matched result, which finds more but multiplies request volume fast.
# greedy strategy example — recurse into every match, not just redirects
ffuf -u http://target.com/FUZZ -w common-dirs.txt \
-recursion -recursion-depth 2 -recursion-strategy greedy -mc 200,301# greedy strategy example — recurse into every match, not just redirects
ffuf -u http://target.com/FUZZ -w common-dirs.txt \
-recursion -recursion-depth 2 -recursion-strategy greedy -mc 200,301Combinatorial cost: recursion depth compounds a wordlist of 5,000 words at depth 2 with even a handful of matched directories can spawn tens of thousands of additional requests. Always pair recursion with tight matchers/filters and a sane -recursion-depth, and consider -maxtime-job as a hard ceiling per recursive branch.
07 · Matchers — Show Results That Match
Matchers are an inclusion list: only responses satisfying at least one matcher condition are shown (logic is OR by default, configurable via -mmode). Here's a working example for every single matcher flag.
-mc — match by HTTP status code. Default: 200,204,301,302,307,401,403,405,500. Use all to match everything.
ffuf -u http://target.com/FUZZ -w wordlist.txt -mc 200,301,302ffuf -u http://target.com/FUZZ -w wordlist.txt -mc 200,301,302-ms — match by response size in bytes.
ffuf -u http://target.com/FUZZ -w wordlist.txt -ms 1024-2048ffuf -u http://target.com/FUZZ -w wordlist.txt -ms 1024-2048-mw — match by word count in the response body.
ffuf -u http://target.com/FUZZ -w wordlist.txt -mw 0,100-500ffuf -u http://target.com/FUZZ -w wordlist.txt -mw 0,100-500-ml — match by line count in the response body.
ffuf -u http://target.com/FUZZ -w wordlist.txt -ml 3,10-20ffuf -u http://target.com/FUZZ -w wordlist.txt -ml 3,10-20-mr — match against a regex on the response body.
ffuf -u http://target.com/FUZZ -w wordlist.txt -mr 'Welcome, [A-Za-z]+'ffuf -u http://target.com/FUZZ -w wordlist.txt -mr 'Welcome, [A-Za-z]+'-mt — match by time-to-first-byte in milliseconds, using > or <. Useful for blind, time-based SQL injection detection.
ffuf -u "http://target.com/search?q=FUZZ" -w sqli-payloads.txt -mt ">4000"ffuf -u "http://target.com/search?q=FUZZ" -w sqli-payloads.txt -mt ">4000"-mmode — Set operator across matcher rules: or (default) or and. Use and when a result should only count if it satisfies multiple matcher conditions simultaneously.
ffuf -u http://target.com/FUZZ -w wordlist.txt -mc 200 -ms 500-2000 -mmode andffuf -u http://target.com/FUZZ -w wordlist.txt -mc 200 -ms 500-2000 -mmode and-mr deserves particular attention — it's the difference between blind status-code fuzzing and actually reading response content. Use it to catch reflected values, debug strings, stack traces, or app-specific success markers that a status code alone won't reveal.
08 · Filters — Remove Noise
Filters are the inverse of matchers: an exclusion list. Anything matching a filter condition gets dropped from output, regardless of matcher rules. Here's a working example for every filter flag.
-fc — filter out by status code.
ffuf -u http://target.com/FUZZ -w wordlist.txt -fc 404,302ffuf -u http://target.com/FUZZ -w wordlist.txt -fc 404,302-fs — filter out by response size.
ffuf -u http://target.com/FUZZ -w wordlist.txt -fs 4242,1024-2048ffuf -u http://target.com/FUZZ -w wordlist.txt -fs 4242,1024-2048-fw — filter out by word count.
ffuf -u http://target.com/FUZZ -w wordlist.txt -fw 0,50-60ffuf -u http://target.com/FUZZ -w wordlist.txt -fw 0,50-60-fl — filter out by line count.
ffuf -u http://target.com/FUZZ -w wordlist.txt -fl 20-30ffuf -u http://target.com/FUZZ -w wordlist.txt -fl 20-30-fr — filter out anything matching a regex on the response body.
ffuf -u http://target.com/FUZZ -w wordlist.txt -fr '/\..*'ffuf -u http://target.com/FUZZ -w wordlist.txt -fr '/\..*'-ft — filter out by time-to-first-byte, using > or <.
ffuf -u http://target.com/FUZZ -w wordlist.txt -ft ">5000"ffuf -u http://target.com/FUZZ -w wordlist.txt -ft ">5000"-fmode — Set operator across filter rules: or (default) or and. Use and when you only want a result excluded if it matches every filter condition at once, rather than any single one.
ffuf -u http://target.com/FUZZ -w wordlist.txt -fs 4242 -fw 22 -fmode andffuf -u http://target.com/FUZZ -w wordlist.txt -fs 4242 -fw 22 -fmode andMatchers vs. filters which one to reach for? Functionally they overlap (excluding 404 -fc 404 vs. only matching -mc 200 often produces near-identical output) but the intent differs:
- Filters are for known noise a soft-404 page that always returns the same size, a WAF block page or a specific status you already know is irrelevant. You discover the noise pattern first, then filter it.
- Matchers are for known targets you know what "success" looks like before scanning starts (200s and 301s, a specific regex, a size range) and only want those.
Practical pattern: run a first pass with wide-open matchers (-mc all or the default set) and no filters, just to see the response shape of the target. Note the size/word count of the "nothing here" response. Then re-run with -fs <that size> this is manual autocalibration, and understanding it makes the automated version (§09) far less of a black box.
09 · Auto-Calibration
Autocalibration automates exactly the manual pattern described above. Before the real scan starts, FFUF sends a handful of requests using random, non-existent paths, records the response signature (size, word count, status), and automatically builds filters to exclude anything matching that signature. It solves the "soft 404" problem for servers that return HTTP 200 with a generic error page instead of a real 404.
ffuf -u http://target.com/FUZZ -w wordlist.txt -acffuf -u http://target.com/FUZZ -w wordlist.txt -ac-ach — per-host autocalibration recalibrates separately for each distinct host in scope. Important when fuzzing across multiple targets or vhosts in one run.
ffuf -u https://FUZZH.target.com/FUZZ -w hosts.txt:FUZZH -w common.txt:FUZZ -achffuf -u https://FUZZH.target.com/FUZZ -w hosts.txt:FUZZH -w common.txt:FUZZ -ach-ack — customize which placeholder value FFUF probes with.
ffuf -u http://target.com/FUZZ -w wordlist.txt -ac -ack "randomtest123"ffuf -u http://target.com/FUZZ -w wordlist.txt -ac -ack "randomtest123"-acc — supply your own custom calibration strings instead of relying on FFUF's random-string probe.
ffuf -u http://target.com/FUZZ -w wordlist.txt -acc "nonexistent1,nonexistent2"ffuf -u http://target.com/FUZZ -w wordlist.txt -acc "nonexistent1,nonexistent2"Where it breaks: autocalibration fails when the FUZZ keyword is embedded in a hostname rather than a path FFUF tries to build a probe URL by substituting a random string into the same position, and a nonsense hostname doesn't resolve. If you're fuzzing hostnames via -w hosts.txt:HOST -u https://HOST/ skip -ac and calibrate manually with -fs/-fw instead.
10 · Parameter Fuzzing
Two distinct goals live under "parameter fuzzing," and mixing them up wastes scan time: finding parameter names an endpoint accepts versus finding parameter values once you already know the name.
Finding parameter names (GET):
ffuf -u "https://target.com/search.php?FUZZ=test" \
-w params.txt -mc 200,302 -fc 404 \
-t 40 -o out.json -of jsonffuf -u "https://target.com/search.php?FUZZ=test" \
-w params.txt -mc 200,302 -fc 404 \
-t 40 -o out.json -of jsonFUZZ sits where the parameter name goes; the value (test) is a harmless placeholder. A response that differs in size/status from the baseline signals the parameter is recognized by the app.
Finding parameter values, once the name is known:
ffuf -u "https://target.com/search.php?test=FUZZ" \
-w params.txt -mc 200,302 -fc 404ffuf -u "https://target.com/search.php?test=FUZZ" \
-w params.txt -mc 200,302 -fc 404Isolating error-revealing responses. Instead of guessing status codes, hunt for text that only appears when the app actually processes a parameter it recognizes:
ffuf -u "https://target.com/search.php?FUZZ=test" \
-w params.txt -mr "Invalid|No such|Exception|token|required"ffuf -u "https://target.com/search.php?FUZZ=test" \
-w params.txt -mr "Invalid|No such|Exception|token|required"POST body parameter fuzzing (form-encoded):
ffuf -u "https://target.com/account/settings" -X POST \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=admin&FUZZ=1" \
-w params.txt -mc 200,302 -fs 1024ffuf -u "https://target.com/account/settings" -X POST \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=admin&FUZZ=1" \
-w params.txt -mc 200,302 -fs 1024JSON body parameter fuzzing:
ffuf -u "https://api.target.com/v1/resource" -X POST \
-H "Content-Type: application/json" \
-d '{"FUZZ":"test"}' \
-w params.txtffuf -u "https://api.target.com/v1/resource" -X POST \
-H "Content-Type: application/json" \
-d '{"FUZZ":"test"}' \
-w params.txtFuzzing multiple positions at once dangerous but sometimes necessary:
ffuf -u "https://target.com/FUZZ.php?FUZZ2=test" \
-w pnames.txt:FUZZ -w pnames2.txt:FUZZ2 -t 20ffuf -u "https://target.com/FUZZ.php?FUZZ2=test" \
-w pnames.txt:FUZZ -w pnames2.txt:FUZZ2 -t 20The mistake almost everyone makes: FFUF does not set headers based on your -d content. Send JSON without an explicit Content-Type: application/json header, and the server will likely reject it as malformed form data before your fuzzing logic ever gets evaluated — every result will look identical, and you'll wrongly conclude the endpoint is dead.
11 · Multi-Wordlist Modes
When more than one -w is supplied (each with its own keyword), -mode controls how FFUF combines them. This is a real combinatorics decision, not a stylistic one.
clusterbomb (default) — every combination of every wordlist against every other, a full cross product. Request count: len(W1) × len(W2) × …
ffuf -request brute.txt -request-proto http \
-mode clusterbomb \
-w users.txt:HFUZZ -w pass.txt:WFUZZ -mc 200ffuf -request brute.txt -request-proto http \
-mode clusterbomb \
-w users.txt:HFUZZ -w pass.txt:WFUZZ -mc 200pitchfork — wordlists stepped through in lockstep, line N of W1 paired with line N of W2. Request count: min(len(W1), len(W2), …)
ffuf -u https://target.com/login -X POST \
-d "user=USERFUZZ&pass=PASSFUZZ" \
-w users.txt:USERFUZZ -w passwords.txt:PASSFUZZ \
-mode pitchforkffuf -u https://target.com/login -X POST \
-d "user=USERFUZZ&pass=PASSFUZZ" \
-w users.txt:USERFUZZ -w passwords.txt:PASSFUZZ \
-mode pitchforksniper — fuzzes one keyword position at a time while holding others static, useful for isolating which specific position causes a behavior change. Request count: sum of each wordlist length.
ffuf -u "https://target.com/FUZZ1/FUZZ2" \
-w path1.txt:FUZZ1 -w path2.txt:FUZZ2 \
-mode sniperffuf -u "https://target.com/FUZZ1/FUZZ2" \
-w path1.txt:FUZZ1 -w path2.txt:FUZZ2 \
-mode sniperClusterbomb explodes fast. Two 10,000-line wordlists in clusterbomb mode is 100,000,000 requests. Know which mode you actually need before you queue the job pitchfork for paired data (username/password lists that are already correlated) and clusterbomb only when you genuinely need every combination.
12 · Subdomain & Virtual Host Discovery
HTTP/1.1's Host header tells a server which virtual host (site) you want. Many servers host multiple domains on one IP (name-based virtual hosting), which makes Host-header fuzzing a legitimate discovery technique for unlisted apps, staging environments, and admin panels that never got a public DNS record.
DNS-resolvable subdomain fuzzing:
ffuf -u http://FUZZ.target.com \
-w subdomains.txt -t 10 --delay 150ms -mc 200,301ffuf -u http://FUZZ.target.com \
-w subdomains.txt -t 10 --delay 150ms -mc 200,301Host-header-based subdomain fuzzing (no DNS resolution required):
ffuf -u https://target/ -H "Host: FUZZ.target.com" \
-w subdomains.txt -t 10 --delay 150ms -mc 200,301ffuf -u https://target/ -H "Host: FUZZ.target.com" \
-w subdomains.txt -t 10 --delay 150ms -mc 200,301Virtual host discovery against a raw IP points every request at the same server while varying only the Host header, finding vhosts that were never given public DNS entries at all:
ffuf -u http://10.10.10.10/ \
-H "Host: FUZZ.company.com" \
-w subdomains.txt -mc 200,301,302 -fs 4242ffuf -u http://10.10.10.10/ \
-H "Host: FUZZ.company.com" \
-w subdomains.txt -mc 200,301,302 -fs 4242The -fs here filters out the default vhost's known response size, so only genuinely different vhosts survive to output.
Verifying a candidate manually:
curl -I -s -k -H "Host: found.target.com" https://10.10.10.10/curl -I -s -k -H "Host: found.target.com" https://10.10.10.10/Autocalibration + per-host matters here. Since every request hits the same server but the response differs by vhost, -ach is genuinely useful in this scenario a single global baseline won't reflect the response signature of each individual vhost.
13 · Auth, Headers & Raw Requests
Cookies, headers, and tokens:
ffuf -u https://target.com/FUZZ -w wordlist.txt \
-H "Authorization: Bearer <token>" \
-b "session=abcd1234; theme=dark"ffuf -u https://target.com/FUZZ -w wordlist.txt \
-H "Authorization: Bearer <token>" \
-b "session=abcd1234; theme=dark"-H can be repeated for multiple headers, and FUZZ can live inside a header value too useful for fuzzing custom headers like X-Forwarded-For, X-Api-Key, or a CSRF token field.
Fuzzing a header value directly:
ffuf -u https://target.com/ -w ip-list.txt \
-H "X-Forwarded-For: FUZZ" -mc 200ffuf -u https://target.com/ -w ip-list.txt \
-H "X-Forwarded-For: FUZZ" -mc 200Client certificates (mTLS): -cc (client cert) and -ck (client key) are required together.
ffuf -u https://target.com/FUZZ -w wordlist.txt \
-cc client.pem -ck client.keyffuf -u https://target.com/FUZZ -w wordlist.txt \
-cc client.pem -ck client.keyRaw request files — the most reliable method. Rather than reconstructing headers, cookies, and method flags by hand, capture a request from Burp Suite (right-click → Copy to file, or the raw request pane) and hand the file directly to FFUF. It parses method, headers, and body automatically — the only thing it can't infer is HTTP vs HTTPS, since that information doesn't live in the raw request text.
ffuf -request raw_req.txt -request-proto https -w wordlist.txtffuf -request raw_req.txt -request-proto https -w wordlist.txtDrop FUZZ anywhere inside the raw request file's URL line, headers, or body before saving it, exactly as you would in a normal -u command.
14 · Threads, Rate & Stealth
Concurrency and pacing determine both scan speed and how detectable/disruptive a scan is. These are separate levers — don't conflate thread count with request rate.
-t — concurrent worker threads (default: 40).
ffuf -u http://target.com/FUZZ -w wordlist.txt -t 100ffuf -u http://target.com/FUZZ -w wordlist.txt -t 100-p — delay between requests, fixed value or a range (adds jitter, helps evade simplistic rate-based WAF rules).
ffuf -u http://target.com/FUZZ -w wordlist.txt -p 0.1-0.5ffuf -u http://target.com/FUZZ -w wordlist.txt -p 0.1-0.5-rate — global cap on requests per second, more predictable than -t alone for staying under a hard rate limit.
ffuf -u http://target.com/FUZZ -w wordlist.txt -rate 20ffuf -u http://target.com/FUZZ -w wordlist.txt -rate 20-timeout — per-request timeout in seconds (default: 10).
ffuf -u http://target.com/FUZZ -w wordlist.txt -timeout 5ffuf -u http://target.com/FUZZ -w wordlist.txt -timeout 5-maxtime — hard ceiling on total run time.
ffuf -u http://target.com/FUZZ -w wordlist.txt -maxtime 600ffuf -u http://target.com/FUZZ -w wordlist.txt -maxtime 600-maxtime-job — hard ceiling per individual job, most relevant with recursion, where each recursive branch is its own job.
ffuf -u http://target.com/FUZZ -w wordlist.txt -recursion -maxtime-job 120ffuf -u http://target.com/FUZZ -w wordlist.txt -recursion -maxtime-job 120Rough thread guidance:
- 1–10 threads — stealthy, appropriate against WAF-protected or rate-limited targets
- 10–40 threads — balanced default for most authorized engagements
- 50–200 threads — aggressive, fine for local labs or CTF targets with no rate limiting
- 300+ threads — realistically a self-inflicted DoS risk against most production infrastructure
Early-exit controls:
-sf — stop automatically once ~95% of responses come back 403 (signals a WAF block rather than genuine results).
ffuf -u http://target.com/FUZZ -w wordlist.txt -sfffuf -u http://target.com/FUZZ -w wordlist.txt -sf-se — stop on spurious/transient errors (connection resets, timeout spikes).
ffuf -u http://target.com/FUZZ -w wordlist.txt -seffuf -u http://target.com/FUZZ -w wordlist.txt -se-sa — stop on all error conditions, the strictest combination of the above.
ffuf -u http://target.com/FUZZ -w wordlist.txt -saffuf -u http://target.com/FUZZ -w wordlist.txt -sa15 · Proxying & Burp Integration
Route FFUF's traffic through an intercepting proxy for inspection, or replay only the matched hits through a proxy for follow-up manual testing two different needs, two different flags.
Route every request through Burp (slow — full scan visibility):
ffuf -u http://target.com/FUZZ -w wordlist.txt -x http://127.0.0.1:8080ffuf -u http://target.com/FUZZ -w wordlist.txt -x http://127.0.0.1:8080Run the scan directly, replay only matches through Burp for manual follow-up:
ffuf -u http://target.com/FUZZ -w wordlist.txt \
-replay-proxy http://127.0.0.1:8080ffuf -u http://target.com/FUZZ -w wordlist.txt \
-replay-proxy http://127.0.0.1:8080-x accepts both HTTP and SOCKS5 proxy URLs. Full proxying through Burp is dramatically slower than a direct scan use -replay-proxy as the default workflow: let FFUF run at full speed, then only the hits you actually care about land in Burp's history for manual poking.
16 · Output & Automation
ffuf -u https://target.com/FUZZ -w wordlist.txt \
-o results.json -of jsonffuf -u https://target.com/FUZZ -w wordlist.txt \
-o results.json -of jsonAvailable output formats:
- json — default. Pipe into
jqfor scripting and chaining into other tools. - ejson — extended JSON, includes full request/response metadata, not just the match summary.
- html — shareable report for non-technical stakeholders.
- md — drop straight into engagement notes or writeups.
- csv / ecsv — spreadsheet analysis;
ecsvescapes fields safely for Excel. - all — writes every format simultaneously.
ffuf -u https://target.com/FUZZ -w wordlist.txt -o results.md -of md
ffuf -u https://target.com/FUZZ -w wordlist.txt -o results.csv -of csv
ffuf -u https://target.com/FUZZ -w wordlist.txt -o results -of allffuf -u https://target.com/FUZZ -w wordlist.txt -o results.md -of md
ffuf -u https://target.com/FUZZ -w wordlist.txt -o results.csv -of csv
ffuf -u https://target.com/FUZZ -w wordlist.txt -o results -of allReal-time JSON lines for pipelines. -json changes the live stdout stream itself (not the saved file) to newline-delimited JSON built for feeding a running scan into another process in real time rather than waiting for completion:
ffuf -u https://target.com/FUZZ -w wordlist.txt -json | jq -c 'select(.status==200)'ffuf -u https://target.com/FUZZ -w wordlist.txt -json | jq -c 'select(.status==200)'Per-result response bodies. -od writes each matched response body to its own file inside a directory — useful when you need to manually review response content for every hit rather than just the summary line.
ffuf -u https://target.com/FUZZ -w wordlist.txt -od responses/ffuf -u https://target.com/FUZZ -w wordlist.txt -od responses/17 · Config Files & Interactive Mode
Persistent defaults via ffufrc. FFUF auto-loads $XDG_CONFIG_HOME/ffuf/ffufrc if it exists, letting you set defaults (thread count, output format, common headers) without retyping them every run. Command-line flags always override the config file, except repeatable flags like -H, which get appended to what the config file already defines rather than replaced.
# use a specific config for a specific engagement instead of the global default
ffuf -config /path/to/engagement.conf -u https://target.com/FUZZ -w wordlist.txt# use a specific config for a specific engagement instead of the global default
ffuf -config /path/to/engagement.conf -u https://target.com/FUZZ -w wordlist.txtInteractive mode — pause and reconfigure mid-scan. Press ENTER during a running scan to drop into an interactive shell without killing the job:
fc / fl / fw / fs [value]— reconfigure the corresponding filter on the flyshow— print all current matches as if freshly discovered under the new filter statequeueshow / queuedel / queueskip— inspect and manage the queue of pending recursive jobsrestart— reset and restart the current job from scratch necessary because tightening filters can't recover results already discarded from memoryresume— continue the paused job (also triggered by pressing ENTER with no command)savejson [filename]— dump current matches to a file mid-run
One-way door: tightening a filter mid-scan permanently discards results that no longer match FFUF doesn't retain "negative" matches in memory. If you over-filter and want lost results back, restart re-runs the job from zero rather than un-filtering.
18 · Full Flag Reference
Every flag from this guide, grouped by category, for quick lookup.
HTTP options
-u— target URL, must contain FUZZ-X— HTTP method-H— header, repeatable-b/-cookie— cookie data-d/-data— request body-r— follow redirects-x— proxy URL (HTTP/SOCKS5)-replay-proxy— proxy for replaying matched requests only-cc/-ck— client cert / client key for mTLS-raw— don't URI-encode the request-timeout— per-request timeout in seconds-recursion/-recursion-depth/-recursion-strategy— recursive scanning controls-ignore-body— skip fetching response body content-http2— use HTTP/2-sni— target TLS SNI value
General options
-t— thread count-p— delay between requests-rate— requests per second cap-maxtime/-maxtime-job— runtime ceilings-c— colorize output-v— verbose output-s— silent/quiet mode-ac/-ach/-ack/-acc— autocalibration controls-sf/-se/-sa— early-exit conditions-noninteractive— disable the interactive console-json— newline-delimited JSON live output-V— version info-config— custom config file path
Input options
-w— wordlist path, optional:KEYWORD-e— extension list-D— DirSearch wordlist compatibility mode-enc— encoders for keyword values-ic— ignore wordlist comments-mode— clusterbomb / pitchfork / sniper-input-cmd/-input-num/-input-shell— dynamic input generation-request/-request-proto— raw HTTP request file
Matcher options
-mc— match status code-ms— match response size-mw— match word count-ml— match line count-mr— match regex-mt— match time-to-first-byte-mmode— matcher set operator (or/and)
Filter options
-fc— filter status code-fs— filter response size-fw— filter word count-fl— filter line count-fr— filter regex-ft— filter time-to-first-byte-fmode— filter set operator (or/and)
Output options
-o— output file-of— output format (json/ejson/html/md/csv/ecsv/all)-od— directory for per-result response bodies-or— skip creating output file if no results-debug-log— internal logging file-audit-log— full request/response audit log
19 · Real Methodology — Putting It Together
A sequence that reflects how an actual engagement uses FFUF, not just isolated flag demos:
1. Baseline the target. Run a wide-open scan with default matchers, no filters, small thread count. Note the response signature of "nothing here" size, word count, status. This tells you whether the target has soft-404s and whether autocalibration will even work cleanly.
2. Directory and file discovery. Run with -ac or manually tuned -fs/-fc, a target-appropriate wordlist, and -e for the detected tech stack's extensions. Save output with -o for later diffing.
3. Recurse selectively. Don't blanket-recurse the whole scan. Pick the directories that actually returned something interesting and recurse into those specifically, with a bounded depth.
4. Parameter mining on discovered endpoints. Once you have real endpoints (not guesses), fuzz parameter names against each, then values against confirmed names. Use -mr to catch app-specific error text rather than relying on status codes alone.
5. VHost/subdomain sweep, separately. This is a different attack surface from path fuzzing run it as its own pass with -ach if you're hitting multiple hosts, not bolted onto the directory scan.
6. Replay hits, not the whole scan. Use -replay-proxy so only confirmed matches land in Burp for manual exploitation keeps the scan fast and your proxy history clean.
20 · Common Mistakes
- Sending JSON via
-dwithoutContent-Type: application/jsonthe server rejects malformed input before your fuzz logic is ever evaluated, so every response looks identical. - Using clusterbomb mode with two large wordlists verify request count before launching, not after.
- Running
-recursionwith depth 0 and no-maxtimeeffectively unbounded, can run indefinitely on a deep or dynamically-generated site tree. - Using
-acwhen FUZZ is inside a hostname — the probe request can't resolve a nonsense hostname, so the job errors out instead of calibrating. - High thread count against a rate-limited or WAF-fronted target you'll trip block thresholds yourself, or just get blocked; slower, quieter scans often find more in practice.
- Treating filters and matchers as interchangeable without thinking about intent works by accident most of the time, breaks silently when the target's response shape changes mid-scan.
- Forgetting
-request-protowhen using-requestagainst an HTTPS target raw request files can't encode scheme; explicit beats assumed.
FFUF's real power isn't the wordlist runner most people use it as it's one substitution engine, combined with matchers, filters, and autocalibration, covering directory discovery, parameter mining, vhost enumeration, and header fuzzing without switching tools. Once the matcher/filter logic clicks, the rest of the flag set is just vocabulary.
Verified against ffuf v2.1.x source and README — github.com/ffuf/ffuf
Researcher: Sourov Hossen LinkedIn: linkedin.com/in/sourov-hossen-307655351 GitHub: github.com/shii9