Nikto

Nikto throws a long list of known checks at a web server and tells you what stuck: dangerous files, default pages, stale server banners, risky HTTP methods, and common misconfigurations. Reach for it right after a port scan turns up HTTP or HTTPS, when you want a fast, zero-config first map of the low-hanging issues. It is loud, and every hit is a lead rather than proof, so treat findings as starting points.

Mental model: nikto -h URL [options]. Point it at a confirmed web service; every refinement is one flag away.

New to web scanning? Core concepts
  • Target: a host plus a scheme and port. Give a full URL, or split it across -h, -ssl, and -p
  • Check class / plugin: a family of tests (files, misconfig, info disclosure, injection, software ID) chosen with -Tuning
  • Finding: a + line, often tagged with an OSVDB-#### or test id; a lead to verify, not a confirmed flaw
  • Banner / fingerprint: the Server and X-Powered-By headers drive version checks, so a hidden or faked banner skews them
  • Soft 404: a site that answers 200 for missing pages; it fools Nikto’s negative detection and floods the results
  • Output format (-Format): txt, csv, xml, htm, json, or nbe for later parsing or a report
  • Tuning digit: every class has an id (1 files, 2 misconfig, 3 info-disc, 9 SQLi, b software ID); a leading x inverts the selection
When to reach for Nikto (and when not)

Reach for it to get a fast first read on a web server you just found: surface default files, forgotten admin pages, outdated banners, missing security headers, and risky methods, or to confirm a hardening change actually removed a default file. It is zero-config and quick to a first answer.

Reach for something else when you need content discovery of hidden paths (ffuf, gobuster), deep app-aware scanning with a crawler (Burp or ZAP scanner), maintained template-driven checks (nuclei), stealth (Nikto is loud by design), or a real TLS audit (sslscan, testssl.sh).

Install, update, verify#

sudo apt install -y nikto                     # Debian / Ubuntu / Kali
sudo dnf install -y nikto                     # Fedora / RHEL
brew install nikto                            # macOS
sudo apt install --only-upgrade -y nikto      # update (or: nikto -update)

nikto -Version        # tool, plugin, and database versions
command -v nikto       # confirm it is on PATH
nikto -dbcheck         # sanity-check the check databases

For the freshest checks without a system package, clone upstream (git clone https://github.com/sullo/nikto) and run program/nikto.pl, or use the sullo/nikto Docker image. Some packaged builds disable -update; the clone (git pull) or the image keeps you current.

Flags you’ll actually use#

Skim these first; the cheat sheet below is these flags combined.

FlagDoesFlagDoes
-hTarget host or URL-idBasic auth user:pass
-sslForce HTTPS/TLS-PauseSeconds between requests
-pPort, or comma list-maxtimeCap total scan time
-oWrite report to a file-timeoutPer-request timeout (s)
-FormatReport format (txt/csv/xml/htm)-useragentSet the User-Agent
-TuningPick check classes (x inverts)-evasionIDS evasion techniques
-DisplayConsole verbosity (V, 1-4)-no404Skip the 404 guessing pass
-vhostSet the Host header-updateRefresh check databases
-useproxyRoute through a proxy-VersionVersion + DB versions

Cheat sheet#

Each block starts bare and adds one flag at a time, so you can stop at the line that does the job.

# Build up a scan: stop at the line you need
nikto -h TARGET_IP                          # 1. auto-detect port 80, run all checks
nikto -h http://TARGET_IP/                  # 2. + explicit scheme and path
nikto -h http://TARGET_IP:8080/             # 3. + non-standard port in the URL
nikto -h http://TARGET_IP/ -o scans/web.txt # 4. + save the report

# HTTPS / TLS
nikto -h DOMAIN -ssl                        # force TLS by hostname
nikto -h https://TARGET_IP/                 # scheme in the URL does the same
nikto -h TARGET_IP -p 443 -ssl              # explicit TLS port

# Ports
nikto -h TARGET_IP -p 8080                  # single non-standard port
nikto -h TARGET_IP -p 80,443,8080           # several ports in one run

# Virtual hosts
nikto -h TARGET_IP -vhost app.lab.local     # name-based vhost by IP

# Tune the checks (cut noise / focus)
nikto -h URL -Tuning 123b                   # files, misconfig, info-disc, SW-ID only
nikto -h URL -Tuning x6                      # everything EXCEPT the DoS class
nikto -h URL -Tuning 9                       # only the SQLi class

# Output formats
nikto -h URL -o scans/web.txt               # plain text
nikto -h URL -o scans/web.xml -Format xml    # XML
nikto -h URL -o scans/web.csv -Format csv    # CSV for spreadsheets
nikto -h URL -o scans/web.html -Format htm   # shareable HTML

# Auth and identity
nikto -h URL -id admin:admin                # HTTP basic auth
nikto -h URL -useragent 'Mozilla/5.0'       # custom User-Agent
nikto -h URL -vhost DOMAIN                   # Host header

# Pace and bound (fragile gear)
nikto -h URL -Pause 2                        # 2s between requests
nikto -h URL -maxtime 180s                   # stop after 3 minutes
nikto -h URL -timeout 5                       # per-request timeout

# Proxy and evasion
nikto -h URL -useproxy http://127.0.0.1:8080 # route through Burp/ZAP
nikto -h URL -evasion 1                       # IDS evasion (URI encoding)

# False-positive control
nikto -h URL -404string 'Not Found'         # define what a missing page looks like
nikto -h URL -no404                          # skip the 404 baseline pass entirely
nikto -h URL -Display V                       # verbose: watch each request

# Capture the run
nikto -h URL 2>&1 | tee scans/run.txt        # scroll + save at once
grep '^+ ' scans/web.txt                      # pull only the findings

Command breakdowns#

Each block below explains one situation, the exact command, and what to expect back.

Scan a web server after a port scan confirms it#

nikto -h http://TARGET_IP/ -o scans/basic.txt

Prints a summary block (IP, port, banner, start time), runs the checks, lists findings marked +, and ends with a request count and end time. -o saves the report so you never re-run a loud scan just to get the output back.

Scan an HTTPS service by hostname#

nikto -h DOMAIN -ssl -o scans/https.txt

-ssl forces TLS when you pass a bare hostname. A full https://TARGET_IP/ URL does the same without the flag. Verify any certificate finding with a dedicated TLS tool.

Scan a web app on a non-standard port#

nikto -h http://TARGET_IP:8080/ -o scans/8080.txt

Put the port in the URL, or use -p 8080. Lab admin panels and dev servers love odd ports, so do not assume 80.

Scan several ports on one host in one run#

nikto -h TARGET_IP -p 80,443,8080 -o scans/multi.txt

One section per responding port. Good for quick triage of a single box that exposes more than one web service.

Reach a name-based virtual host#

nikto -h TARGET_IP -vhost app.lab.local -o scans/vhost.txt

-vhost sets the Host header so the checks run against the named site even though you connect by IP. Without it, an IP that serves a different default site hides the real app.

Cut the noise by tuning to specific check classes#

nikto -h http://TARGET_IP/ -Tuning 123b -o scans/tuned.txt

Each digit selects a class: 1 interesting files, 2 misconfig and defaults, 3 info disclosure, b software identification. Fewer classes means a faster, quieter, more focused scan.

Run everything except the denial-of-service checks#

nikto -h http://TARGET_IP/ -Tuning x6 -o scans/safe.txt

The leading x inverts the selection, so x6 runs every class but the DoS one (6). Use it on fragile targets you do not want to knock over.

Route the scan through Burp or ZAP#

nikto -h http://TARGET_IP/ -useproxy http://127.0.0.1:8080

Every request lands in your proxy history, so you can inspect exactly what Nikto sent and replay any interesting probe by hand.

Scan a site behind HTTP basic auth#

nikto -h http://TARGET_IP/ -id admin:admin -o scans/auth.txt

-id user:pass supplies HTTP basic credentials so the checks run against the protected area instead of bouncing off 401s. Lab or placeholder creds only.

Save a machine-readable report#

nikto -h http://TARGET_IP/ -o scans/report.xml -Format xml

XML (or -Format csv, json, htm) is easy to parse or import later. Nikto also infers the format from the -o file extension, so -o report.csv alone usually works.

Pace and bound a scan on fragile gear#

nikto -h http://TARGET_IP/ -Pause 2 -maxtime 180s -o scans/gentle.txt

-Pause 2 waits two seconds between requests; -maxtime 180s stops the whole scan at three minutes. Together they keep a delicate app alive and the run bounded.

Save the run and watch it live at once#

nikto -h http://TARGET_IP/ 2>&1 | tee scans/nikto-initial.txt

tee scrolls the output in your terminal and captures it to the file in one shot, so you see progress and still keep the log.

Pull only the actionable findings#

grep '^+ ' scans/basic.txt | tee notes/findings.txt

The + lines are the findings; everything else is summary and progress. This strips a long log down to the leads worth chasing.

Tame a soft-404 that floods every check#

nikto -h http://TARGET_IP/ -404string 'Not Found' -o scans/clean.txt

When a site answers 200 for missing pages, teach Nikto what a negative looks like with -404string (matching the body text a missing page returns) or -404code for a distinctive status. -no404 instead disables the baseline pass entirely, for when those probes themselves trip a WAF.

Update the check databases#

nikto -update

Pulls fresh plugin and database files so newer default files and fingerprints get checked. If a packaged build has -update disabled, use the git clone (git pull) or docker pull sullo/nikto instead.

Evade a basic IDS on an authorized test#

nikto -h http://TARGET_IP/ -evasion 1 -o scans/evade.txt

-evasion obfuscates each request; 1 is random URI encoding. It can slip past naive signatures but not a modern WAF, and it does nothing about Nikto’s overall volume.

Reading the output#

  • The header block (+ Target IP / Hostname / Port, + Server:, + Start Time) sets the scope and the detected banner. Confirm the port and scheme are what you meant to hit.
  • + finding lines are the results, one lead each, for example + OSVDB-3268: /icons/: Directory indexing found. Open the path yourself before acting on it.
  • Server: and X-Powered-By banners drive the version findings; a hidden or spoofed banner makes an “appears to be outdated” note unreliable.
  • Method findings (PUT, TRACE, or DELETE reported as allowed) come from an OPTIONS response; confirm they actually work with a manual request.
  • The footer (+ N requests: X error(s) and Y item(s) reported) says the scan finished and how much it sent. A tiny item count on a real app often means a soft 404 or a WAF, not a clean server.

Troubleshooting#

ProblemCauseFix
command not foundNot installed / stale PATHReinstall, or run program/nikto.pl from the clone
No web server foundWrong port or schemeConfirm with a port scan; add -ssl or -p
Connection refusedService down or filteredRe-check host and port; confirm the service is up
TLS/SSL handshake errorTLS not forced or odd certAdd -ssl, or use the full https:// URL
Findings floodSoft 404 / catch-all pageSet -404string or -404code; narrow with -Tuning
Scan never finishesHuge site or slow linkAdd -maxtime, narrow -Tuning, lower -timeout
Empty outputWAF/IDS dropping probesSlow with -Pause, change -useragent, confirm scope
Auth pages skippedNo credentials suppliedAdd -id user:pass (lab creds only)
-update failsPackaged build disables itUse the git clone or Docker image instead

Gotchas#

  • Nikto brands every request. The default User-Agent literally contains Nikto, so a WAF can block on that string alone. -useragent changes it, but the request volume and patterns still read as a scanner.
  • Tuning is additive and x inverts it. -Tuning 9 runs only the SQLi class; -Tuning x9 runs everything except it. Getting this backwards silently scans the wrong thing.
  • A soft 404 defeats Nikto’s negative detection. When the app returns 200 for missing pages, every guessed file “exists”. Teach it a real negative with -404string 'Not Found' before trusting the flood.
  • -Format HTML is spelled htm. Or drop -Format and let Nikto infer the format from the -o extension (.xml, .csv, .html, .json).
  • A clean run is not a clean app. Nikto checks a fixed list and does not crawl or brute force paths, so “0 item(s) reported” only means the known checks did not trip. Follow up with content discovery.

Pairs with#

Run nmap -sV first to find which ports speak HTTP and on which scheme, then point Nikto at each. whatweb fingerprints the stack so its banner findings make sense, and ffuf or gobuster cover the hidden paths Nikto never brute forces. Send it through Burp or ZAP with -useproxy to inspect and replay requests, and use sslscan or testssl.sh for the real TLS audit Nikto only hints at. When you need maintained, deeper checks, step up to nuclei or a full app scanner; Nikto stays the fastest way to a first answer.

Find us elsewhere

Merch, stickers, and moreSupport the work at the Solvere Labs shop