The ffuf cheat sheet covers web fuzzing for directories, files, vhosts, parameters, POST data, APIs, GraphQL, and JWT - with filtering, evasion, and automation techniques.
# 1. Directory brute-force with auto-calibration
ffuf -u http://target.com/FUZZ -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt -ac -c
# 2. File discovery with extensions
ffuf -u http://target.com/FUZZ -w /usr/share/seclists/Discovery/Web-Content/raft-medium-files.txt -e .php,.bak,.txt,.conf,.log -ac -c
# 3. Vhost enumeration (filter noise by size)
ffuf -u http://target.com -H "Host: FUZZ.target.com" -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -fs 4242 -c
# 4. GET parameter name discovery
ffuf -u "http://target.com/page.php?FUZZ=1" -w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt -fs 1234 -c
# 5. Login brute-force (filter wrong-password redirect)
ffuf -u http://target.com/login -X POST -d "user=admin&pass=FUZZ" -H "Content-Type: application/x-www-form-urlencoded" -w /usr/share/wordlists/rockyou.txt -fc 302 -t 50
# 6. API endpoint discovery
ffuf -u http://target.com/api/v1/FUZZ -w /usr/share/seclists/Discovery/Web-Content/api/api-endpoints.txt -mc 200,201,204,400,401,403,405 -c
[ Start: What are you fuzzing? ]
|
┌────────────────────┼────────────────────┐
| | |
[ Paths/Dirs ] [ Parameters ] [ Vhosts ]
| | |
ffuf -u http://t/FUZZ ?FUZZ=1 (name) -H "Host: FUZZ.t"
-w dirs.txt =FUZZ (value) -w subdomains.txt
| | -fs <default_size>
[ Add -e for ext ] [ POST body? ]
.php .bak .txt -X POST -d
| "param=FUZZ"
[ Recursive? ] |
-recursion [ JSON API? ]
-recursion-depth 3 Content-Type:
application/json
|
┌─────────┴─────────┐
| |
[ REST API ] [ GraphQL ]
/api/v1/FUZZ /graphql
method fuzzing introspection
header fuzzing operation fuzz
|
[ Auth header? ]
-H "Authorization:
Bearer FUZZ"
(JWT claim fuzz)
| Flag | Default | Description |
|---|---|---|
-u <URL> |
Target URL with |
|
-X <method> |
HTTP method | |
-d <data> |
POST body data (-data-binary preserves encoding) |
|
-H <header> |
Add header "Name: Value" (repeatable) |
|
-b <cookie> |
Cookie string "NAME=VAL; NAME2=VAL2" |
|
-r |
false | Follow redirects |
-http2 |
false | Use HTTP/2 protocol |
-timeout <n> |
Request timeout in seconds | |
-x <url> |
Proxy URL - HTTP or SOCKS5 | |
-replay-proxy <url> |
Send |
|
-cc <file> |
Client TLS certificate file | |
-ck <file> |
Client TLS key file | |
-sni <host> |
TLS SNI hostname override | |
-raw |
false | Do not encode URI (raw payload injection) |
-ignore-body |
false | Skip fetching response body (speed up header-only checks) |
-request <file> |
Load raw HTTP request from file | |
-request-proto <p> |
Protocol to use with -request file |
| Flag | Default | Description |
|---|---|---|
-t <n> |
Concurrent threads | |
-p <delay> |
Delay between requests; range: 0.1-2.0 |
|
-rate <n> |
Max requests per second | |
-maxtime <n> |
Total run time limit (seconds) | |
-maxtime-job <n> |
Per-job time limit (seconds) | |
-c |
false | Colorize output |
-v |
false | Verbose: show full URL + redirect location |
-s |
false | Silent mode |
-json |
false | Newline-delimited JSON on stdout |
-o <file> |
Write output to file | |
-od <dir> |
Directory to store matched response bodies | |
-of <fmt> |
Format: json ejson html md csv ecsv all |
|
-config <file> |
Load config from file | |
-debug-log <file> |
Write internal logs to file | |
-audit-log <file> |
Write all requests + responses to file | |
-noninteractive |
false | Disable interactive console |
| Flag | Default | Description |
|---|---|---|
-w <path[:KW]> |
Wordlist file, optional keyword (e.g. list.txt:FUZZ) |
|
-e <exts> |
Comma-separated extensions appended to FUZZ | |
-mode <mode> |
Multi-wordlist mode: clusterbomb pitchfork sniper |
|
-D |
false | DirSearch compat mode (use with -e) |
-ic |
false | Ignore wordlist comment lines |
-enc <enc> |
Encoder for keyword: FUZZ:urlencode FUZZ:b64encode |
|
-input-cmd <cmd> |
Shell command generating payloads (requires -input-num) |
|
-input-num <n> |
Number of inputs from -input-cmd |
|
-input-shell <sh> |
Shell to use for -input-cmd |
|
-recursion |
false | Recursive scanning (URL must end with FUZZ) |
-recursion-depth <n> |
Max recursion depth | |
-recursion-strategy |
default (follow redirects) or greedy (all matches) |
| Flag | Description |
|---|---|
-sa |
-sf and -se) |
-sf |
Stop when >95% of responses return |
-se |
Stop on spurious errors (network issues) |
-or |
Do not create output file if no results found |
| Flag | Type | Matches On | Notes |
|---|---|---|---|
-mc <codes> |
HTTP status codes | Default: 200,204,301,302,307,401,403,405,500; use all to match everything |
|
-ms <n> |
Response size (bytes) | ||
-mw <n> |
Word count in response | ||
-ml <n> |
Line count in response | ||
-mr <regex> |
Regex in response body | Prefix (?i) for case-insensitive |
|
-mt <expr> |
Response time (ms) | Use >500 or <100 syntax |
|
-fc <codes> |
HTTP status codes | ||
-fs <n> |
Response size | Comma-separated or ranges | |
-fw <n> |
Word count | ||
-fl <n> |
Line count | ||
-fr <regex> |
Regex in response body | ||
-ft <expr> |
Response time (ms) | ||
-mmode <op> |
Matcher logic | and = all must match; or = any match (default) |
|
-fmode <op> |
Filter logic | and = all must match; or = any match (default) |
# Show only 200 responses
ffuf -u http://target.com/FUZZ -w wordlist.txt -mc 200
# Filter out 404 and 400
ffuf -u http://target.com/FUZZ -w wordlist.txt -fc 404,400
# Filter by exact response size (remove identical error pages)
ffuf -u http://target.com/FUZZ -w wordlist.txt -fs 1234
# Filter by multiple sizes (comma-separated)
ffuf -u http://target.com/FUZZ -w wordlist.txt -fs 1234,5678
# Match by regex - finds pages mentioning admin
ffuf -u http://target.com/FUZZ -w wordlist.txt -mr "(?i)admin|dashboard"
# Filter pages that contain "not found" text
ffuf -u http://target.com/FUZZ -w wordlist.txt -fr "(?i)not found|page not found"
# Time-based match for slow SQL injection responses (>2000ms)
ffuf -u "http://target.com/item?id=FUZZ" -w sqli.txt -mt ">2000"
# Combine matchers: 200 AND response size > 500 (use -mmode and)
ffuf -u http://target.com/FUZZ -w wordlist.txt -mc 200 -ms 500 -mmode and
# -ac: auto-detect and filter the baseline "not found" response
ffuf -u http://target.com/FUZZ -w wordlist.txt -ac
# -ach: calibrate independently per host (useful with vhost fuzzing)
ffuf -u http://target.com/FUZZ -w wordlist.txt -ach
# -acc: add custom strings for calibration baseline (implies -ac)
ffuf -u http://target.com/FUZZ -w wordlist.txt -acc "thisdoesnotexist123" -acc "randomgarbage456"
# -acs: calibration strategy - "basic" (default) or "advanced"
ffuf -u http://target.com/FUZZ -w wordlist.txt -ac -acs advanced
# Combine auto-calibration with manual filter for tight control
ffuf -u http://target.com/FUZZ -w wordlist.txt -ac -fc 404 -fs 0
Auto-calibration sends a few probing requests with random values, then sets filters based on the baseline responses. It removes the need to manually determine the 404 size. Use -ach when fuzzing targets that serve different content per host.
# CLUSTERBOMB (default): all combinations - W1*W2 total requests
ffuf -u http://target.com/FUZZ1/FUZZ2 \
-w dirs.txt:FUZZ1 \
-w files.txt:FUZZ2
# PITCHFORK: lockstep - pairs line 1 with line 1, etc.
# Use for username:password pairs from breach data
ffuf -u http://target.com/login -X POST \
-d "user=USER&pass=PASS" \
-w usernames.txt:USER \
-w passwords.txt:PASS \
-H "Content-Type: application/x-www-form-urlencoded" \
-mode pitchfork -fc 401
# SNIPER: one wordlist, injected at each position in sequence
ffuf -u http://target.com/FUZZ1/FUZZ2 \
-w payloads.txt \
-mode sniper
# URL-encode the payload (e.g. for path traversal or SQLi)
ffuf -u "http://target.com/FUZZ" -w traversal.txt -enc "FUZZ:urlencode"
# Base64-encode the payload
ffuf -u "http://target.com/api?token=FUZZ" -w tokens.txt -enc "FUZZ:b64encode"
# Chain encoders - URL-encode then base64
ffuf -u "http://target.com/FUZZ" -w payloads.txt -enc "FUZZ:urlencode b64encode"
# Multiple keywords with different encoders
ffuf -u "http://target.com/path?id=ID&token=TOK" \
-w ids.txt:ID \
-w tokens.txt:TOK \
-enc "ID:urlencode" \
-enc "TOK:b64encode"
Available encoders: urlencode, urldoubleencode, b64encode, md5, sha256
# Save a Burp Suite request to a file, replace value with FUZZ:
# POST /login HTTP/1.1
# Host: target.com
# Content-Type: application/json
#
# {"username":"FUZZ","password":"password123"}
ffuf -request req.txt -request-proto http -w usernames.txt -mc 200
# HTTPS target
ffuf -request req.txt -request-proto https -w wordlist.txt -ac
# Multiple FUZZ positions in the request file
# Replace multiple values: USER and PASS
ffuf -request req.txt -request-proto https \
-w usernames.txt:USER \
-w passwords.txt:PASS \
-mode pitchfork
Using -request is the cleanest way to fuzz complex authenticated requests captured in Burp.
# Discover API endpoints under /api/v1/
ffuf -u http://target.com/api/v1/FUZZ \
-w /usr/share/seclists/Discovery/Web-Content/api/api-endpoints.txt \
-mc 200,201,204,400,401,403,405 -c
# Version fuzzing - discover active API versions
ffuf -u http://target.com/api/FUZZ/users \
-w versions.txt \
-mc 200,201,400,401,403
# Nested resource fuzzing (clusterbomb)
ffuf -u http://target.com/api/v1/FUZZ1/FUZZ2 \
-w /usr/share/seclists/Discovery/Web-Content/api/objects.txt:FUZZ1 \
-w /usr/share/seclists/Discovery/Web-Content/api/actions.txt:FUZZ2 \
-mc 200,201,204,400,401,403,405 -ac
# IDOR - fuzz numeric resource IDs
ffuf -u http://target.com/api/v1/users/FUZZ \
-w /usr/share/seclists/Fuzzing/4-digits-0000-9999.txt \
-mc 200 -H "Authorization: Bearer <your_token>"
# Fuzz HTTP methods on a known endpoint
ffuf -u http://target.com/api/v1/users \
-w methods.txt \
-X FUZZ \
-mc 200,201,204,400,401,403,405,500
# Quick inline method list (no wordlist file needed)
for m in GET POST PUT DELETE PATCH HEAD OPTIONS TRACE CONNECT; do
printf "$m\n"
done | ffuf -u http://target.com/api/v1/users \
-w -:FUZZ \
-X FUZZ \
-mc 200,201,204,400,405,500 -c
# Method + endpoint combo (clusterbomb)
ffuf -u http://target.com/api/v1/ENDPOINT \
-w endpoints.txt:ENDPOINT \
-w methods.txt:METHOD \
-X METHOD \
-mode clusterbomb \
-mc 200,201,204,400,401,403,405
Methods wordlist content: GET POST PUT DELETE PATCH OPTIONS HEAD TRACE CONNECT
# Fuzz a JSON field value
ffuf -u http://target.com/api/v1/login \
-X POST \
-H "Content-Type: application/json" \
-d '{"username":"FUZZ","password":"password"}' \
-w /usr/share/seclists/Usernames/xato-net-10-million-usernames.txt \
-mc 200 -c
# Fuzz a JSON field name (key discovery)
ffuf -u http://target.com/api/v1/user/profile \
-X PUT \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-d '{"FUZZ":"test"}' \
-w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt \
-mc 200,400 -fr '"error"' -c
# Mass assignment: fuzz privilege escalation fields
ffuf -u http://target.com/api/v1/user/update \
-X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-d '{"FUZZ":"true"}' \
-w priv-params.txt \
-mc 200 -c
# Fuzz Authorization header values (token discovery)
ffuf -u http://target.com/api/v1/admin \
-H "Authorization: Bearer FUZZ" \
-w api-tokens.txt \
-mc 200 -c
# Fuzz X-API-Key header
ffuf -u http://target.com/api/v1/data \
-H "X-API-Key: FUZZ" \
-w /usr/share/seclists/Discovery/Web-Content/api/api-seen-in-wild.txt \
-mc 200,201 -c
# Fuzz custom security headers (auth bypass attempts)
ffuf -u http://target.com/admin \
-H "FUZZ: 127.0.0.1" \
-w /usr/share/seclists/Discovery/Web-Content/BurpSuite-ParamMiner/uppercase-headers.txt \
-mc 200 -fc 403 -c
# X-Forwarded-For IP spoofing (bypass IP allowlisting)
ffuf -u http://target.com/admin \
-H "X-Forwarded-For: FUZZ" \
-w ips.txt \
-mc 200 -fc 403 -c
# Content-Type fuzzing - discover parsers that change behavior
ffuf -u http://target.com/api/v1/upload \
-X POST \
-d "data=test" \
-H "Content-Type: FUZZ" \
-w /usr/share/seclists/Miscellaneous/web-all-content-types.txt \
-mc 200,201 -c
# Common GraphQL endpoint paths
ffuf -u http://target.com/FUZZ \
-w /usr/share/seclists/Discovery/Web-Content/graphql.txt \
-mc 200,400,405 -c
# Also try these manually after discovery:
# /graphql /graphiql /api/graphql /v1/graphql
# /graphql/console /playground /api/explorer
# Probe for introspection - POST a basic introspection query
curl -s -X POST http://target.com/graphql \
-H "Content-Type: application/json" \
-d '{"query":"{ __schema { types { name } } }"}' | jq .
# Confirm endpoint is live (any GraphQL error = valid endpoint)
ffuf -u http://target.com/FUZZ \
-X POST \
-H "Content-Type: application/json" \
-d '{"query":"{__typename}"}' \
-w graphql-paths.txt \
-mr '"data"' -c
# Fuzz GraphQL query operation names
ffuf -u http://target.com/graphql \
-X POST \
-H "Content-Type: application/json" \
-d '{"query":"{ FUZZ { id name } }"}' \
-w /usr/share/seclists/Discovery/Web-Content/api/objects.txt \
-mr '"data"' \
-fr '"errors"' -c
# Fuzz mutation names
ffuf -u http://target.com/graphql \
-X POST \
-H "Content-Type: application/json" \
-d '{"query":"mutation { FUZZ }"}' \
-w /usr/share/seclists/Discovery/Web-Content/api/actions.txt \
-mc 200 -fr '"Unknown"' -c
# Fuzz field names on a known type (e.g. User)
ffuf -u http://target.com/graphql \
-X POST \
-H "Content-Type: application/json" \
-d '{"query":"{ user(id:1) { FUZZ } }"}' \
-w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt \
-mr '"data"' -fr '"Cannot query field"' -c
# Fuzz with authentication token
ffuf -u http://target.com/graphql \
-X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-d '{"query":"{ FUZZ { id } }"}' \
-w objects.txt \
-mr '"data"' -c
JWT fuzzing targets the payload claims to find privilege escalation, IDOR via sub/user_id, or broken authorization logic. You need a valid baseline token.
# Step 1 - decode existing JWT to understand claim structure
echo "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0IiwicGxhbiI6ImZyZWUifQ.sig" \
| cut -d. -f2 | base64 -d 2>/dev/null | jq .
# Output: {"sub":"1234","plan":"free"}
# Step 2 - fuzz the Authorization header with a pre-built token wordlist
ffuf -u http://target.com/api/v1/admin \
-H "Authorization: Bearer FUZZ" \
-w jwt-tokens.txt \
-mc 200 -c
# Fuzz sub claim (IDOR via user ID in JWT)
# Generate tokens with different sub values using a script, then:
ffuf -u http://target.com/api/v1/profile \
-H "Authorization: Bearer FUZZ" \
-w generated-jwts.txt \
-mc 200 -c
# Fuzz role/plan claim via pre-generated tokens
# python3 gen_jwts.py --claim role --values admin,superuser,staff > role-tokens.txt
ffuf -u http://target.com/api/v1/admin \
-H "Authorization: Bearer FUZZ" \
-w role-tokens.txt \
-mc 200 -fc 401,403 -c
# gen_jwts.py - generate JWT tokens with fuzzed claims (requires PyJWT)
# pip install PyJWT
import jwt, sys, argparse
SECRET = "your_known_secret" # or use "" for none-alg testing
parser = argparse.ArgumentParser()
parser.add_argument("--claim", required=True)
parser.add_argument("--values", required=True)
args = parser.parse_args()
base_payload = {"sub": "1234", "plan": "free"}
for val in args.values.split(","):
base_payload[args.claim] = val
token = jwt.encode(base_payload, SECRET, algorithm="HS256")
print(token)
# Generate tokens and feed into ffuf
python3 gen_jwts.py --claim role --values "admin,superuser,staff,root,moderator" > role-tokens.txt
ffuf -u http://target.com/api/admin \
-H "Authorization: Bearer FUZZ" \
-w role-tokens.txt \
-mc 200 -c
# Fuzz session cookie value (session fixation / brute-force)
ffuf -u http://target.com/dashboard \
-b "PHPSESSID=FUZZ" \
-w /usr/share/seclists/Fuzzing/alphanum-case.txt \
-mc 200 -fs 4242 -c
# Fuzz a specific cookie parameter name
ffuf -u http://target.com/dashboard \
-H "Cookie: FUZZ=admin" \
-w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt \
-mc 200 -fc 302,401,403 -c
# Fuzz cookie role/privilege value
ffuf -u http://target.com/admin \
-H "Cookie: session=abc123; role=FUZZ" \
-w roles.txt \
-mc 200 -fc 302,403 -c
# Path traversal bypass via header
ffuf -u http://target.com/admin \
-H "X-Original-URL: FUZZ" \
-w /usr/share/seclists/Discovery/Web-Content/raft-small-directories.txt \
-mc 200 -c
# Fuzz admin path via X-Rewrite-URL
ffuf -u http://target.com/ \
-H "X-Rewrite-URL: FUZZ" \
-w /usr/share/seclists/Discovery/Web-Content/quickhits.txt \
-mc 200 -c
# Basic auth brute-force via Authorization header
ffuf -u http://target.com/admin \
-H "Authorization: Basic FUZZ" \
-w /usr/share/seclists/Miscellaneous/BasicAuthenticationCredentials.txt \
-mc 200 -c
# Fuzz password reset tokens
ffuf -u "http://target.com/reset?token=FUZZ" \
-w /usr/share/seclists/Fuzzing/alphanum-case-extra.txt \
-mc 200,302 -c
# Basic directory fuzzing
ffuf -u http://target.com/FUZZ \
-w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt \
-ac -c
# File fuzzing with common extensions
ffuf -u http://target.com/FUZZ \
-w /usr/share/seclists/Discovery/Web-Content/raft-medium-files.txt \
-e .php,.html,.bak,.txt,.conf,.log,.xml,.json \
-ac -c
# DirSearch compatibility mode (-D appends extensions without dot duplication)
ffuf -u http://target.com/FUZZ \
-w wordlist.txt \
-D -e php,html,bak,txt \
-ac -c
# Recursive fuzzing with depth limit
ffuf -u http://target.com/FUZZ \
-w /usr/share/seclists/Discovery/Web-Content/raft-small-directories.txt \
-recursion -recursion-depth 3 \
-ac -e .php,.html -c
# Greedy recursion - recurse into all matches, not just redirects
ffuf -u http://target.com/FUZZ \
-w wordlist.txt \
-recursion -recursion-strategy greedy \
-recursion-depth 2 -ac -c
# VHost enumeration via Host header (internal apps on shared IP)
ffuf -u http://10.10.10.100 \
-H "Host: FUZZ.target.com" \
-w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt \
-fs 4242 -c
# Subdomain-style URL fuzzing
ffuf -u http://FUZZ.target.com \
-w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt \
-ac -c
# Large subdomain list for bug bounty
ffuf -u http://FUZZ.target.com \
-w /usr/share/seclists/Discovery/DNS/bitquark-subdomains-top100000.txt \
-ac -t 100 -c
# Per-host autocalibration (each vhost gets its own baseline)
ffuf -u http://10.10.10.100 \
-H "Host: FUZZ.target.com" \
-w subdomains.txt \
-ach -c
# GET parameter name fuzzing
ffuf -u "http://target.com/page.php?FUZZ=test" \
-w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt \
-fs 1234 -c
# GET parameter value fuzzing (IDOR)
ffuf -u "http://target.com/profile?id=FUZZ" \
-w /usr/share/seclists/Fuzzing/4-digits-0000-9999.txt \
-mc 200 -c
# POST parameter name fuzzing
ffuf -u http://target.com/api/action \
-X POST \
-d "FUZZ=test" \
-H "Content-Type: application/x-www-form-urlencoded" \
-w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt \
-fs 1234 -c
# Route all traffic through Burp Suite
ffuf -u http://target.com/FUZZ -w wordlist.txt \
-x http://127.0.0.1:8080 -c
# Route only MATCHES to Burp (keeps Burp history clean)
ffuf -u http://target.com/FUZZ -w wordlist.txt \
-replay-proxy http://127.0.0.1:8080 -ac -c
# HTTPS target through Burp (ignore cert errors)
ffuf -u https://target.com/FUZZ -w wordlist.txt \
-x http://127.0.0.1:8080 -c
# Note: add Burp's CA cert to your system trust store for clean HTTPS proxying
# SOCKS5 proxy (e.g. through SSH tunnel or Tor)
ffuf -u http://target.com/FUZZ -w wordlist.txt \
-x socks5://127.0.0.1:1080 -c
-replay-proxy is preferred over -x when you want Burp history to contain only the interesting hits, not thousands of 404s.
# Hard rate limit (50 req/s)
ffuf -u http://target.com/FUZZ -w wordlist.txt -rate 50
# Random delay 0.5-3 seconds per request (IDS/WAF evasion)
ffuf -u http://target.com/FUZZ -w wordlist.txt -p 0.5-3.0
# Low-and-slow: 5 threads + delay
ffuf -u http://target.com/FUZZ -w wordlist.txt -t 5 -p 1.0-5.0 -rate 2
# Realistic browser User-Agent
ffuf -u http://target.com/FUZZ -w wordlist.txt \
-H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"
# Randomize User-Agent from a list using input-cmd
ffuf -u http://target.com/FUZZ \
-w wordlist.txt \
-H "User-Agent: $(shuf -n1 user-agents.txt)" \
-rate 10 -p 0.2-1.0
# Stop if the target starts blocking (>95% 403s)
ffuf -u http://target.com/FUZZ -w wordlist.txt -sf -rate 20
# JSON output - parse with jq
ffuf -u http://target.com/FUZZ -w wordlist.txt -o results.json -of json
# Extract just the URLs from JSON output
jq -r '.results[].url' results.json
# Extract URL + status + size
jq -r '.results[] | "\(.status) \(.length) \(.url)"' results.json | sort -n
# Filter results by status in jq
jq -r '.results[] | select(.status==200) | .url' results.json
# Newline-delimited JSON to stdout (pipe to other tools)
ffuf -u http://target.com/FUZZ -w wordlist.txt -json \
| jq -r 'select(.status==200) | .url'
# HTML report
ffuf -u http://target.com/FUZZ -w wordlist.txt -o report.html -of html
# Generate all formats at once
ffuf -u http://target.com/FUZZ -w wordlist.txt -o results -of all
# Save matched response bodies to directory
ffuf -u http://target.com/FUZZ -w wordlist.txt -od ./responses/ -ac
# Pipe results into nuclei for immediate vuln scanning
ffuf -u http://target.com/FUZZ -w wordlist.txt -json \
| jq -r 'select(.status==200) | .url' \
| nuclei -t /path/to/templates/
# Generate sequential IDs with seq
ffuf -u "http://target.com/api/v1/user/FUZZ" \
-input-cmd "seq 1 10000" \
-input-num 10000 \
-mc 200 -c
# Generate GUIDs with Python
ffuf -u "http://target.com/api/items/FUZZ" \
-input-cmd "python3 -c \"import uuid; [print(uuid.uuid4()) for _ in range(1000)]\"" \
-input-num 1000 \
-mc 200 -c
# Read from stdin (use - as wordlist with pipe)
cat wordlist.txt | ffuf -u http://target.com/FUZZ -w -
# Generate JWT tokens on the fly
ffuf -u http://target.com/api/admin \
-H "Authorization: Bearer FUZZ" \
-input-cmd "python3 gen_jwts.py --claim role --values admin,superuser,staff,root" \
-input-num 4 \
-mc 200 -c
Press ENTER during a running ffuf scan to enter interactive mode.
| Command | Description |
|---|---|
help |
Show all interactive commands |
fc <codes> |
|
fl <n> |
|
fw <n> |
|
fs <n> |
|
rate <n> |
Adjust requests per second on the fly |
show |
Print all current results |
savejson <file> |
Save current results to JSON file |
restart |
Restart the current job from scratch |
resume |
Resume paused job |
quit |
The config file lives at $XDG_CONFIG_HOME/ffuf/ffufrc (typically ~/.config/ffuf/ffufrc). Command-line flags override config values; -H flags append to config headers.
[http]
# Default proxy for all scans (comment out when not needed)
# proxyurl = "http://127.0.0.1:8080"
followredirects = false
timeout = 10
method = "GET"
[general]
# Always colorize output
colors = true
# Default thread count
threads = 40
# Auto-calibration on by default
autocalibration = true
autocalibration_strategy = "basic"
# Stop when flooded with 403s
stopon403 = true
# Rate limit (0 = unlimited)
rate = 0
# Non-interactive for scripted runs
noninteractive = false
[input]
# Default to ignoring wordlist comments
ignorewordlistcomments = true
inputmode = "clusterbomb"
[output]
# Default output format
outputformat = "json"
# Do not create empty output files
outputcreateemptyfile = false
[matcher]
# Match these status codes by default
status = "200,204,301,302,307,401,403,405,500"
mode = "or"
[filter]
# No default filters - set per-scan
mode = "or"
# Use a custom config file for a specific engagement
ffuf -config /path/to/engagement.ffufrc -u http://target.com/FUZZ -w wordlist.txt
# Override a config file setting from command line
# (command-line always wins except -H which appends)
ffuf -config ~/.config/ffuf/ffufrc \
-u http://target.com/FUZZ \
-w wordlist.txt \
-rate 100 # overrides config rate value
# Engagement-specific config example: slow/stealthy preset
# slow.ffufrc
# [general]
# rate = 5
# threads = 5
# [http]
# proxyurl = "http://127.0.0.1:8080"
| Category | Wordlist Path | Notes |
|---|---|---|
/usr/share/seclists/Discovery/Web-Content/raft-small-directories.txt |
~17k entries | |
/usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt |
~30k entries | |
/usr/share/seclists/Discovery/Web-Content/raft-large-directories.txt |
~62k entries | |
/usr/share/seclists/Discovery/Web-Content/raft-medium-files.txt |
~17k entries | |
/usr/share/seclists/Discovery/Web-Content/raft-large-files.txt |
~37k entries | |
/usr/share/seclists/Discovery/Web-Content/quickhits.txt |
High-value targets | |
/usr/share/seclists/Discovery/Web-Content/common.txt |
General purpose | |
/usr/share/seclists/Discovery/Web-Content/big.txt |
~20k, broad coverage | |
/usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt |
GET/POST param names | |
/usr/share/seclists/Discovery/Web-Content/api/api-endpoints.txt |
Generic API paths | |
/usr/share/seclists/Discovery/Web-Content/api/api-seen-in-wild.txt |
Real-world endpoints | |
/usr/share/seclists/Discovery/Web-Content/api/objects.txt |
API resource nouns | |
/usr/share/seclists/Discovery/Web-Content/api/actions.txt |
API action verbs | |
/usr/share/seclists/Discovery/Web-Content/graphql.txt |
GraphQL paths | |
/usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt |
Fast vhost enum | |
/usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt |
Deeper vhost enum | |
/usr/share/seclists/Discovery/DNS/bitquark-subdomains-top100000.txt |
Bug bounty scale | |
/usr/share/seclists/Discovery/DNS/combined_subdomains.txt |
Merged mega-list | |
| Usernames | /usr/share/seclists/Usernames/xato-net-10-million-usernames.txt |
10M usernames |
| Usernames (short) | /usr/share/seclists/Usernames/top-usernames-shortlist.txt |
Top 100 usernames |
| Passwords | /usr/share/wordlists/rockyou.txt |
Classic password list |
| Content types | /usr/share/seclists/Miscellaneous/web-all-content-types.txt |
MIME type fuzzing |
| LFI paths | /usr/share/seclists/Fuzzing/LFI/LFI-Jhaddix.txt |
Path traversal |
| SQLi | /usr/share/seclists/Fuzzing/SQLi/Generic-SQLi.txt |
SQL injection payloads |
| Feature | gobuster | ||
|---|---|---|---|
| Language | Go | Go | Rust |
| Speed | Very fast | Fast | Very fast |
| Fuzzing (any position) | No - path/dns/vhost modes only | Limited | |
| Header fuzzing | Yes | No | No |
| POST body fuzzing | Yes | No | Limited |
| JSON body fuzzing | Yes | No | No |
| Multiple wordlists | Yes (clusterbomb/pitchfork/sniper) | No | No |
| Encoders | Yes (-enc) |
No | No |
| Auto-calibration | Yes (-ac) |
No | No |
| Recursion | Yes | DNS only | |
| Regex match/filter | Yes | No | No |
| Time-based filter | Yes (-mt/-ft) |
No | No |
| Raw request file | Yes (-request) |
No | No |
| Interactive mode | Yes | No | Yes |
| Replay proxy | Yes | No | No |
| Output formats | json/html/md/csv/all | json/csv | json |
| Best for | Simple dir/dns brute-force | Recursive dir brute-force |
Use gobuster for simple fast directory/DNS enumeration in CTFs. Use feroxbuster when you want automatic recursion without configuration. Use ffuf for everything else - especially API, parameter, header, and POST fuzzing.
ffuf's JSON output (-o results.json -of json) is the most powerful format for post-processing. Use jq to extract exactly what you need.
# Save results as JSON
ffuf -u http://target.com/FUZZ -w wordlist.txt -o results.json -of json -ac
# Extract all found URLs
jq -r '.results[] | .url' results.json
# Filter by status code
jq -r '.results[] | select(.status == 200) | .url' results.json
# Filter by response size range
jq -r '.results[] | select(.length > 500 and .length < 10000) | "\(.status) \(.length) \(.url)"' results.json
# Sort by response size (find outliers)
jq -r '.results[] | "\(.length)\t\(.status)\t\(.url)"' results.json | sort -n
# Extract unique content lengths (spot anomalies)
jq -r '.results[] | .length' results.json | sort -n | uniq -c | sort -rn
# Pipe results into other tools
jq -r '.results[] | .url' results.json | httpx -silent -mc 200
jq -r '.results[] | .url' results.json | nuclei -silent
# CSV output for spreadsheets
ffuf -u http://target.com/FUZZ -w wordlist.txt -o results.csv -of csv -ac
# All output formats at once
ffuf -u http://target.com/FUZZ -w wordlist.txt -o results -of all -ac
# Creates: results.json, results.csv, results.html, results.md
# Quick grep on default output (use -c for colors)
ffuf -u http://target.com/FUZZ -w wordlist.txt -ac -c 2>&1 | grep "200"
# Pipe to tee for live output + save
ffuf -u http://target.com/FUZZ -w wordlist.txt -ac -c 2>&1 | tee ffuf_output.txt
# Extract URLs from text output
grep -oP 'http[s]?://[^\s]+' ffuf_output.txt
# Count results per status code
ffuf -u http://target.com/FUZZ -w wordlist.txt -ac -o results.json -of json
jq -r '.results[] | .status' results.json | sort | uniq -c | sort -rn
Full web reconnaissance workflow for CTF or initial pentest recon. Run in order.
# Step 1: Directory discovery with auto-calibration
ffuf -u http://target.com/FUZZ -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt -ac -c -o dirs.json -of json
# Step 2: File discovery with common extensions
ffuf -u http://target.com/FUZZ -w /usr/share/seclists/Discovery/Web-Content/raft-medium-files.txt -e .php,.txt,.bak,.conf,.log,.old,.zip,.sql -ac -c -o files.json -of json
# Step 3: Subdomain / VHost discovery
ffuf -u http://target.com -H "Host: FUZZ.target.com" -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -ac -c -o vhosts.json -of json
# Step 4: Parameter discovery on interesting endpoints
ffuf -u "http://target.com/api/endpoint?FUZZ=test" -w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt -ac -c -o params.json -of json
# Step 5: Merge and review all results
for f in dirs.json files.json vhosts.json params.json; do
echo "=== $f ===" && jq -r '.results[] | "\(.status) \(.url)"' "$f" 2>/dev/null
done
Chained workflow with other tools for bug bounty reconnaissance.
# Subfinder -> httpx -> ffuf pipeline
subfinder -d target.com -silent | httpx -silent -o live_hosts.txt
# Fuzz each live host for directories
while read host; do
domain=$(echo "$host" | sed 's|https\?://||')
ffuf -u "${host}/FUZZ" \
-w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt \
-ac -c -mc 200,301,302,403 \
-o "ffuf_${domain}.json" -of json \
-rate 50 -t 20
done < live_hosts.txt
# Find hidden API endpoints across all hosts
cat ffuf_*.json | jq -r '.results[] | select(.url | test("api|v[0-9]|graphql|rest|swagger")) | .url'
# Quick nuclei scan on discovered endpoints
cat ffuf_*.json | jq -r '.results[] | .url' | nuclei -silent -severity medium,high,critical
Fuzz behind login - pass cookies or tokens from authenticated session.
# With session cookie
ffuf -u http://target.com/admin/FUZZ \
-w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt \
-b "PHPSESSID=abc123; token=xyz789" \
-ac -c -mc 200,301,302
# With Authorization header (Bearer token)
ffuf -u http://target.com/api/FUZZ \
-w /usr/share/seclists/Discovery/Web-Content/api-endpoints.txt \
-H "Authorization: Bearer eyJhbGci..." \
-ac -c
# With multiple headers (API key + content type)
ffuf -u http://target.com/api/v2/FUZZ \
-w wordlist.txt \
-H "X-API-Key: secretkey123" \
-H "Content-Type: application/json" \
-ac -c
# POST fuzzing with auth
ffuf -u http://target.com/api/users \
-w /usr/share/seclists/Usernames/top-usernames-shortlist.txt \
-X POST \
-H "Authorization: Bearer eyJhbGci..." \
-H "Content-Type: application/json" \
-d '{"username":"FUZZ"}' \
-ac -c
When a path returns 403 Forbidden, try bypass techniques.
# Path traversal bypasses
ffuf -u http://target.com/FUZZ/admin \
-w /usr/share/seclists/Fuzzing/403-bypass.txt \
-mc 200,301,302 -c
# Header-based bypasses (X-Forwarded-For, X-Original-URL, etc.)
ffuf -u http://target.com/admin \
-w /usr/share/seclists/Discovery/Web-Content/common.txt \
-H "X-Forwarded-For: 127.0.0.1" \
-H "X-Original-URL: /FUZZ" \
-ac -c -mc 200
# Case and encoding variations
ffuf -u http://target.com/FUZZ \
-w - <<EOF
/admin
/Admin
/ADMIN
/%61dmin
/admin/
/admin/.
/./admin
//admin
/admin%20
/admin%09
EOF
| Problem | Solution |
|---|---|
| Too many results (false positives) | Use -ac (auto-calibrate) or add -fs, -fc, -fw filters |
| No results at all | Check URL spelling, try without filters, verify wordlist exists |
| Connection refused | Check target is up, try with -timeout 10 |
| Slow performance | Increase threads -t 50, use -rate to control speed |
| WAF blocking | Add -rate 10 -p 0.5-1.0, use -H "User-Agent: ..." |
| SSL errors | Add -k to skip certificate verification |
| Redirects not followed | -r follows redirects, -recursion for recursive mode |
| Binary/garbage output | Add -c for color, pipe through less -R |
# Verbose mode (show all responses including filtered)
ffuf -u http://target.com/FUZZ -w wordlist.txt -v
# Debug mode with request/response details
ffuf -u http://target.com/FUZZ -w wordlist.txt -debug-log debug.txt
# Silent mode (no banner, minimal output)
ffuf -u http://target.com/FUZZ -w wordlist.txt -s
# Show only the URLs (for piping)
ffuf -u http://target.com/FUZZ -w wordlist.txt -s -ac | tee urls.txt