ffuf

The ffuf cheat sheet covers web fuzzing for directories, files, vhosts, parameters, POST data, APIs, GraphQL, and JWT - with filtering, evasion, and automation techniques.

#Quick Reference

#6 Most-Used One-Liners

# 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

#What Am I Fuzzing?

                  [ 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)

#Command Builder

ffuf Command Builder

Wordlist Cheatsheet

Path (under /usr/share/seclists/) Contents Use case
Discovery/Web-Content/common.txt 4k entries Fast dir enum
Discovery/Web-Content/directory-list-2.3-medium.txt 220k entries Thorough dir enum
Discovery/DNS/subdomains-top1million-5000.txt 5k entries Subdomain / vhost fuzzing
Fuzzing/LFI/LFI-Jhaddix.txt LFI payloads LFI testing
Fuzzing/SQLi/Generic-SQLi.txt SQLi payloads SQLi testing
Discovery/Web-Content/raft-large-files.txt File names File discovery

#Core Options

#HTTP Flags

Flag Default Description
-u <URL> Target URL with FUZZ placeholder (required)
-X <method> GET 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> 10 Request timeout in seconds
-x <url> Proxy URL - HTTP or SOCKS5
-replay-proxy <url> Send matched results only to this proxy (Burp)
-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> https Protocol to use with -request file

#General & Output Flags

Flag Default Description
-t <n> 40 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

#Input Flags

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> clusterbomb 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 default (follow redirects) or greedy (all matches)

#Stop & Error Control Flags

Flag Description
-sa Stop on all error cases (implies -sf and -se)
-sf Stop when >95% of responses return 403 Forbidden
-se Stop on spurious errors (network issues)
-or Do not create output file if no results found

#Filtering & Matching

#Filter & Match Reference

Flag Type Matches On Notes
-mc <codes> Match HTTP status codes Default: 200,204,301,302,307,401,403,405,500; use all to match everything
-ms <n> Match Response size (bytes)
-mw <n> Match Word count in response
-ml <n> Match Line count in response
-mr <regex> Match Regex in response body Prefix (?i) for case-insensitive
-mt <expr> Match Response time (ms) Use >500 or <100 syntax
-fc <codes> Filter HTTP status codes
-fs <n> Filter Response size Comma-separated or ranges
-fw <n> Filter Word count
-fl <n> Filter Line count
-fr <regex> Filter Regex in response body
-ft <expr> Filter Response time (ms)
-mmode <op> Mode Matcher logic and = all must match; or = any match (default)
-fmode <op> Mode Filter logic and = all must match; or = any match (default)

#Filtering Examples

# 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

#Auto-Calibration

# -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.

#Multi-Mode & Encoders

#Multi-Wordlist Modes

# 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

#Payload Encoders

# 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

#Using Raw HTTP Request Files

# 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.

#API Fuzzing

#REST Endpoint Discovery

# 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>"

#HTTP Method Fuzzing

# 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

#JSON Body Fuzzing

# 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

#Header Fuzzing

# 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

#GraphQL Fuzzing

#Introspection Endpoint Discovery

# 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

#Operation & Field Fuzzing

# 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

#JWT Claim Fuzzing

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

#JWT Token Generation Helper

# 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

#Authentication Bypass Fuzzing

# 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

#Access Control & Header Injection

# 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

#Discovery Techniques

#Directory & File Fuzzing

# 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 & Subdomain Discovery

# 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

#Parameter Discovery

# 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

#Advanced Techniques

#Proxy Integration & Burp Suite

# 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.

#Rate Limiting & Stealth

# 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

#Output & Automation

# 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/

#Input from External Commands

# 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

#Interactive Mode Commands

Press ENTER during a running ffuf scan to enter interactive mode.

Command Description
help Show all interactive commands
fc <codes> Set filter by HTTP status codes
fl <n> Set filter by line count
fw <n> Set filter by word count
fs <n> Set filter by response size
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 Exit ffuf

#Config File (.ffufrc)

#Example .ffufrc

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"

#Per-Project Config Override

# 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"

#Wordlist Reference

#SecLists Paths by Category

Category Wordlist Path Notes
Dirs (small) /usr/share/seclists/Discovery/Web-Content/raft-small-directories.txt ~17k entries
Dirs (medium) /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt ~30k entries
Dirs (large) /usr/share/seclists/Discovery/Web-Content/raft-large-directories.txt ~62k entries
Files (medium) /usr/share/seclists/Discovery/Web-Content/raft-medium-files.txt ~17k entries
Files (large) /usr/share/seclists/Discovery/Web-Content/raft-large-files.txt ~37k entries
Quick hits /usr/share/seclists/Discovery/Web-Content/quickhits.txt High-value targets
Common /usr/share/seclists/Discovery/Web-Content/common.txt General purpose
Big /usr/share/seclists/Discovery/Web-Content/big.txt ~20k, broad coverage
Parameters /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt GET/POST param names
API endpoints /usr/share/seclists/Discovery/Web-Content/api/api-endpoints.txt Generic API paths
API (wild) /usr/share/seclists/Discovery/Web-Content/api/api-seen-in-wild.txt Real-world endpoints
API objects /usr/share/seclists/Discovery/Web-Content/api/objects.txt API resource nouns
API actions /usr/share/seclists/Discovery/Web-Content/api/actions.txt API action verbs
GraphQL /usr/share/seclists/Discovery/Web-Content/graphql.txt GraphQL paths
Subdomains (5k) /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt Fast vhost enum
Subdomains (20k) /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt Deeper vhost enum
Subdomains (100k) /usr/share/seclists/Discovery/DNS/bitquark-subdomains-top100000.txt Bug bounty scale
DNS combined /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

#Tool Comparison

#ffuf vs gobuster vs feroxbuster

Feature ffuf gobuster feroxbuster
Language Go Go Rust
Speed Very fast Fast Very fast
Fuzzing (any position) Yes - full FUZZ keyword 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 Yes - default on
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 All-purpose fuzzing, APIs, auth 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.

#Output Parsing

#JSON Output & jq

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

#Grep & Text Parsing

# 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

#Real-World Scenarios

#CTF / Quick Web Recon

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

#Bug Bounty Recon Pipeline

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

#Authenticated Fuzzing

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

#403 Bypass Fuzzing

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

#Troubleshooting

#Common Issues

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

#Debug & Verbose

# 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

#Also See

#Cyber Aurelien Guidi