Web Security

How to Scan Your Website for Vulnerabilities: Free and Paid Tools

A practical guide to web vulnerability scanning using OWASP ZAP, Nikto, and Nuclei, including how to run authenticated scans, set up continuous scanning in CI/CD, and triage findings effectively.

December 15, 20257 min readShipSafer Team

Web vulnerability scanning — Dynamic Application Security Testing (DAST) — is the practice of probing a running application for security weaknesses from the outside, the same way an attacker would. Unlike static analysis (SAST), which examines source code, DAST exercises your application's actual runtime behavior: how it responds to malformed input, whether it exposes sensitive headers, how it handles authentication edge cases. This guide covers the primary free and open-source tools and how to use them effectively.

OWASP ZAP (Zed Attack Proxy)

OWASP ZAP is the most widely used open-source web application security scanner. It operates as a local HTTP proxy that you route your browser traffic through, recording all requests and responses. It then applies a comprehensive suite of active and passive security checks.

Installation:

# Using Docker (recommended — avoids Java version conflicts)
docker pull ghcr.io/zaproxy/zaproxy:stable

# Or install the desktop application from zaproxy.org

Passive scanning (non-invasive, suitable for production):

Passive scanning analyzes HTTP traffic without sending additional requests. It checks for issues like:

  • Missing security headers
  • Session tokens in URLs
  • Cookies without Secure/HttpOnly flags
  • Information disclosure in responses

Run a baseline passive scan:

docker run -t ghcr.io/zaproxy/zaproxy:stable \
  zap-baseline.py \
  -t https://example.com \
  -r zap-report.html \
  -l WARN

The -l WARN flag reports only WARN and FAIL findings. This is the appropriate level for a first scan.

Active scanning (invasive — run only against test/staging environments):

Active scanning sends attack payloads to your application to detect vulnerabilities including SQL injection, XSS, path traversal, and server-side request forgery. Never run active scans against production without explicit authorization and a maintenance window.

docker run -t ghcr.io/zaproxy/zaproxy:stable \
  zap-full-scan.py \
  -t https://staging.example.com \
  -r zap-full-report.html \
  -I  # Don't fail on warnings

Authenticated scanning:

Most application vulnerabilities are in authenticated areas. Scanning only the public-facing pages misses the majority of your attack surface. ZAP supports authenticated scanning via:

  1. Form-based authentication (username/password):
# zap-auth.py (ZAP automation framework script)
import time
from zapv2 import ZAPv2

zap = ZAPv2(proxies={'http': 'http://127.0.0.1:8080', 'https': 'http://127.0.0.1:8080'})

# Access login page
zap.urlopen('https://staging.example.com/login')
time.sleep(2)

# Set up form-based authentication
zap.authentication.set_authentication_method(
    1,  # context ID
    'formBasedAuthentication',
    'loginUrl=https://staging.example.com/api/auth/login'
    '&loginRequestData=email%3D%7B%25username%25%7D%26password%3D%7B%25password%25%7D'
)
  1. Script-based authentication (for complex login flows like OAuth, MFA):

Create a ZAP authentication script that handles your specific login flow, including CSRF token extraction and multi-step forms.

  1. Header-based authentication (API keys, Bearer tokens):

For APIs, use a replacer rule to inject an Authorization header into all requests:

Replacer Rule: Request Header, Authorization, Bearer YOUR_TEST_TOKEN

ZAP CI/CD Integration:

# .github/workflows/zap-scan.yml
- name: Run ZAP Baseline Scan
  uses: zaproxy/action-baseline@v0.12.0
  with:
    target: 'https://staging.example.com'
    rules_file_name: '.zap/rules.tsv'
    cmd_options: '-I'  # Don't fail on warnings
    issue_title: 'ZAP Scan Report'
    token: ${{ secrets.GITHUB_TOKEN }}
    fail_action: true

The GitHub Action automatically creates or updates a GitHub Issue with the scan report.

Nikto

Nikto is a lightweight command-line scanner focused on detecting outdated software, default files, and misconfigurations. It is faster and simpler than ZAP but less comprehensive for application-layer issues.

Nikto excels at detecting:

  • Outdated web server versions
  • Default/example files (test.php, phpinfo.php, .env, backup files)
  • Dangerous HTTP methods (PUT, DELETE enabled where they should not be)
  • Directory listing enabled
  • Known vulnerable CGI scripts
  • Insecure server configurations
# Basic scan
nikto -h https://example.com

# Scan with specific plugins
nikto -h https://example.com -Plugins "apache_expect_xss,headers"

# Output to HTML
nikto -h https://example.com -Format html -output nikto-report.html

# Scan with custom port and SSL
nikto -h example.com -port 8443 -ssl

# Scan through a proxy
nikto -h https://example.com -useproxy http://127.0.0.1:8080

Using Docker:

docker run --rm frapsoft/nikto -h https://example.com

Nikto is intentionally noisy — it produces many false positives and many informational findings that are not genuine vulnerabilities. Treat its output as a checklist to investigate, not a definitive vulnerability list.

Nuclei

Nuclei (by ProjectDiscovery) takes a template-based approach to vulnerability scanning. Instead of a fixed set of checks, Nuclei uses YAML templates that describe specific vulnerabilities or checks. The community maintains thousands of templates, and the tool is updated daily with new checks for CVEs, misconfigurations, and exposed sensitive files.

Installation:

# Using Go
go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest

# Using Homebrew
brew install nuclei

# Using Docker
docker pull projectdiscovery/nuclei:latest

Update templates:

nuclei -update-templates

Basic scan:

# Scan with all default templates
nuclei -u https://example.com

# Scan with specific severity levels
nuclei -u https://example.com -severity critical,high,medium

# Scan for specific categories
nuclei -u https://example.com -tags cve,misconfig,exposure

# Output to JSON for processing
nuclei -u https://example.com -json -o nuclei-findings.json

Scanning for specific vulnerability classes:

# Check for CVEs specifically
nuclei -u https://example.com -tags cve -severity critical,high

# Check for exposed admin panels and sensitive files
nuclei -u https://example.com -tags panel,exposure

# Check for misconfigurations
nuclei -u https://example.com -tags misconfig

# Check for outdated JavaScript libraries (via JS file analysis)
nuclei -u https://example.com -tags tech

Nuclei in CI/CD:

# .github/workflows/nuclei-scan.yml
- name: Nuclei Vulnerability Scan
  uses: projectdiscovery/nuclei-action@main
  with:
    target: https://staging.example.com
    flags: "-severity critical,high -tags cve,misconfig"
    sarif-export: results.sarif

- name: Upload SARIF results
  uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: results.sarif

Uploading SARIF results to GitHub Code Scanning displays findings directly in GitHub's Security tab and PR checks.

Running Authenticated Scans Effectively

The key to useful DAST results is scanning the authenticated portions of your application. Here are practical approaches for each tool in authenticated mode:

Cookie-based authentication (most web apps):

  1. Log in to your application in a browser
  2. Export the session cookie value
  3. Provide it to the scanner:
# Nikto with session cookie
nikto -h https://staging.example.com \
  -C "session=abc123; csrf_token=xyz789"

# Nuclei with custom headers
nuclei -u https://staging.example.com \
  -H "Cookie: session=abc123" \
  -H "Authorization: Bearer TEST_TOKEN"

Creating a dedicated test account: Use a separate test account for scanning, not production credentials. The account should have typical user permissions. Run additional scans with admin-level credentials to test admin functionality.

Scope and exclusions: Always configure scanners to avoid:

  • Delete or destructive actions (DELETE /api/users/{id})
  • Email-sending endpoints (to avoid spamming test users)
  • Payment processing endpoints (Stripe test mode is acceptable, but be careful)
  • Heavy computational endpoints that could DoS your staging environment

Interpreting and Triaging Findings

DAST tools produce a large volume of findings, many of which are false positives. Effective triage requires:

Classify by exploitability, not just severity. A "Critical" finding that requires specific server-side state to exploit may be lower priority than a "Medium" SQL injection that is trivially exploitable.

Verify before reporting. Manually attempt to reproduce each finding before treating it as confirmed. Automated tools frequently flag things that are not actually exploitable in context.

Common false positives:

  • "SQL injection detected" from scanners that mistake error messages for SQL errors
  • "XSS detected" from scanners that cannot distinguish between sanitized and unsanitized output
  • "Security header missing" on API endpoints where the header is not applicable (HSTS on an API that is only called by your server, for example)

Findings worth immediate action:

  • Authentication bypasses (authenticated pages accessible without credentials)
  • Actual SQL injection (verify by exfiltrating a known value)
  • Directory traversal with actual file reads
  • Exposed admin interfaces
  • Exposed .git, .env, or backup files
  • Hard-coded credentials in JavaScript files

Setting Up Continuous Scanning

One-time scans are useful for baseline assessments. Continuous scanning catches regressions introduced by new deployments.

Recommended pipeline:

  1. On every PR to staging: Run ZAP baseline scan (passive) and Nuclei with critical/high templates. Fail the PR if new critical findings appear.
  2. Weekly: Run ZAP full scan against staging with authenticated credentials. Report findings to your security backlog.
  3. Monthly: Run Nikto against staging to check for newly exposed configuration issues.

The goal is not to block every deployment — it is to ensure new vulnerabilities are detected within days of introduction, not months.

vulnerability scanning
OWASP ZAP
Nikto
Nuclei
DAST
web security
penetration testing

Check Your Security Score — Free

See exactly how your domain scores on DMARC, TLS, HTTP headers, and 25+ other automated security checks in under 60 seconds.