OWASP ZAP for Web Application Security Testing: A Practitioner's Guide
OWASP ZAP (Zed Attack Proxy) is the most widely used open-source web application security scanner in the world. It is free, actively maintained by the OWASP foundation and a large community of contributors, and — unlike Burp Suite Pro — it has a first-class story for CI/CD integration. If you are building a security testing pipeline, or if budget is a constraint, ZAP deserves serious consideration. If you are already a Burp power user, ZAP's automation capabilities are worth understanding even if you stick with Burp for manual work.
This guide covers ZAP the way practitioners actually use it: proxy interception, the traditional and AJAX spiders, active scan configuration, the fuzzer, scripting, the REST API, and how to wire ZAP into a GitHub Actions pipeline. We will also be direct about where ZAP falls short compared to Burp Suite Pro, because knowing the tool's limits is as important as knowing its capabilities.
ZAP vs Burp Suite: The Honest Comparison
The question comes up constantly, so let us address it upfront. ZAP and Burp Pro are both HTTP proxy-based security testing tools. They share a lot of conceptual ground. The differences that matter in practice:
Where ZAP wins:
- Cost: ZAP is completely free. Burp Pro is ~$499/year per user. For teams, developer security testing, or organizations with tight budgets, this is decisive.
- CI/CD automation: ZAP was designed with headless, API-driven operation in mind. The official Docker images, the REST API, and the Automation Framework make pipeline integration genuinely straightforward. Burp Enterprise does this but costs significantly more.
- AJAX Spider: ZAP's AJAX Spider uses a real browser engine (via Selenium/HtmlUnit) to crawl JavaScript-heavy single-page applications. It is more capable at SPA crawling than Burp's built-in crawler in many scenarios.
- Scriptability: ZAP exposes a comprehensive REST API and supports scripts in multiple languages (JavaScript, Python, Ruby, Groovy). The scripting model is more open than Burp's Java-only extension API.
- Community and transparency: Open source means you can read the scanner code, understand exactly what checks are running, and contribute fixes. Burp's detection logic is a black box.
Where Burp Pro wins:
- Manual testing UX: Burp's Repeater, Intruder, and HTTP History workflow is more polished and faster for interactive manual testing. ZAP's UI has improved but still trails Burp on day-to-day ergonomics.
- Scanner accuracy: Burp's scanner generally produces fewer false positives and finds more true positives on complex applications. ZAP's scanner is good but generates more noise.
- Collaborator / OAST: Burp Collaborator for out-of-band detection is tightly integrated and reliable. ZAP has no equivalent built-in — you need to integrate an external service like interactsh.
- Extensions ecosystem: The BApp Store has deeper integrations (Param Miner, JWT Editor, Turbo Intruder) that do not have ZAP equivalents of the same quality.
The practical conclusion: use ZAP when you need automation at scale or budget is a constraint; use Burp for deep manual testing during an engagement. Many professional teams use both.
Installation: Desktop GUI vs Daemon Mode
ZAP ships in two primary forms. Understanding which to use matters before you start.
Desktop GUI
The standard installer at zaproxy.org installs the full GUI application. It looks and behaves similarly to Burp: a proxy listener, a sites tree showing the application structure, a history of all proxied requests, and tabs for the various tools. Use the GUI when you are doing manual testing, exploring an application interactively, or setting up and testing scan configurations before automating them.
On Linux, the GUI requires a display server. On headless servers, use the daemon mode or Docker image instead.
Daemon mode
ZAP can run headlessly with its REST API exposed. Start it with:
zap.sh -daemon -host 0.0.0.0 -port 8090 \
-config api.key=YOUR_API_KEY \
-config api.addrs.addr.name=.* \
-config api.addrs.addr.regex=true
In daemon mode, ZAP exposes a REST API at http://localhost:8090. Every action you can take in the GUI — start a spider, run an active scan, retrieve alerts — has a corresponding API endpoint. This is the mode you use in CI/CD pipelines.
Docker
The official Docker images are the cleanest way to run ZAP in automated contexts. Two images matter:
ghcr.io/zaproxy/zaproxy:stable— the stable release, suitable for production pipelinesghcr.io/zaproxy/zaproxy:nightly— latest features, more updates, slightly less stable
docker run -u zap -p 8090:8090 \
ghcr.io/zaproxy/zaproxy:stable \
zap.sh -daemon -host 0.0.0.0 -port 8090 \
-config api.key=changeme
The Docker images include all ZAP add-ons pre-installed and are the recommended approach for pipeline use.
CA Certificate and Proxy Setup
Like Burp, ZAP acts as a man-in-the-middle proxy for HTTPS. You need to install ZAP's root CA certificate in your browser's trust store before HTTPS traffic will intercept cleanly.
In the GUI: Tools → Options → Dynamic SSL Certificates → Save. This exports the CA certificate as a PEM file. Import it into your browser:
- Firefox: Settings → Privacy & Security → Certificates → Import
- Chrome/Edge: Settings → Privacy & Security → Manage Certificates → Authorities → Import
ZAP's default proxy listener is localhost:8080 — the same default as Burp. If you run both tools simultaneously, change one of them. ZAP's listener port is configured under Tools → Options → Local Proxies.
For command-line and API-driven use, you can retrieve the CA certificate from ZAP's REST API:
curl "http://localhost:8090/OTHER/core/other/rootcert/?apikey=YOUR_API_KEY" \
-o zap-root-ca.cer
Spider: Mapping the Application
ZAP has two distinct crawlers. Understanding when to use each is important because they serve different application architectures.
Traditional Spider
The traditional spider parses HTML responses to find links, forms, and resources. It follows <a href> tags, submits forms with default values, and recursively visits discovered URLs. It is fast and works well for server-rendered applications where most content is in the HTML source.
To start a spider scan via the GUI: right-click a site in the Sites tree → Attack → Spider. Configure the scope, maximum depth (default 5), and number of concurrent threads, then start it. The spider populates the Sites tree with discovered endpoints.
Via the REST API:
# Start a spider scan
curl "http://localhost:8090/JSON/spider/action/scan/?apikey=YOUR_API_KEY&url=https://target.example.com&maxChildren=10&recurse=true"
# Poll for status (returns 0-100)
curl "http://localhost:8090/JSON/spider/view/status/?apikey=YOUR_API_KEY&scanId=0"
# Get discovered URLs when complete
curl "http://localhost:8090/JSON/spider/view/results/?apikey=YOUR_API_KEY&scanId=0"
AJAX Spider
Modern web applications built with React, Vue, Angular, or any SPA framework render most of their content through JavaScript. The traditional spider misses this content because it only parses static HTML. The AJAX Spider solves this by using an actual browser engine (Selenium with Chrome, Firefox, or HtmlUnit) to render pages and click through the application.
The AJAX Spider is slower — it is driving a real browser — but it discovers endpoints that the traditional spider cannot find. For any modern application, run the AJAX Spider in addition to the traditional spider.
# Start AJAX spider (uses the configured browser)
curl "http://localhost:8090/JSON/ajaxSpider/action/scan/?apikey=YOUR_API_KEY&url=https://target.example.com&inScope=false"
# Check status
curl "http://localhost:8090/JSON/ajaxSpider/view/status/?apikey=YOUR_API_KEY"
# Get number of results
curl "http://localhost:8090/JSON/ajaxSpider/view/numberOfResults/?apikey=YOUR_API_KEY"
In headless environments, configure the AJAX Spider to use HtmlUnit (no external browser dependency) or ensure Chrome/Firefox and the appropriate WebDriver are available in your Docker container. The zaproxy/zaproxy Docker image includes Firefox for this purpose.
Spider configuration tips
A few settings that make a meaningful difference:
- Maximum children to crawl: limits the number of children crawled per node — useful to prevent the spider from going infinitely deep into parameter-heavy URLs like paginated content
- Scope: set the target scope before running the spider so it does not follow external links off your target domain
- Login handling: configure a login script or authentication method (see the Authentication section below) before spidering so the crawler can reach authenticated content
- POST forms: enable "Post Form" in spider options to have the spider submit discovered forms, which reveals additional endpoints and parameters
Active Scanning: Configuration and Scan Policies
ZAP's active scanner sends attack payloads to the application to probe for vulnerabilities. Unlike passive scanning (which only observes traffic), active scanning is detectable and should only be run against targets you own or have explicit authorization to test.
Scan policies
Scan policies define which tests run and at what intensity. ZAP ships with a default policy, but creating custom policies for specific use cases significantly improves both scan speed and result quality.
To manage scan policies: Analyze → Scan Policy Manager → Add.
For each category of tests (SQL Injection, XSS, Path Traversal, etc.), you configure:
- Threshold: how confidently ZAP must believe an issue exists before reporting it. Options are Off, Low, Medium, High. Low threshold = more findings, more false positives. High threshold = fewer findings, higher confidence.
- Strength: how many payloads ZAP sends per test point. Low, Medium, High, Insane. Higher strength finds more vulnerabilities but takes significantly longer and generates much more traffic. Medium is the practical default for most engagements.
Practical policy recommendations:
- Fast policy for CI/CD: disable expensive checks (XPath injection, LDAP injection), set strength to Low, threshold to Medium. Aim for a scan that completes in under 10 minutes.
- Thorough policy for scheduled scans: all checks enabled, Medium strength, Medium threshold. Accept longer scan times for better coverage.
- API policy: disable client-side checks (XSS DOM-based, JavaScript injection), enable injection checks and auth tests. APIs do not render HTML, so client-side tests are noise.
Running an active scan
Active scans are run against URLs or nodes in the Sites tree. In the GUI: right-click a node → Attack → Active Scan. Select your scan policy, configure the scope, and start.
Via the API:
# Start active scan with a specific policy
curl "http://localhost:8090/JSON/ascan/action/scan/?apikey=YOUR_API_KEY&url=https://target.example.com&recurse=true&inScopeOnly=false&scanPolicyName=Default+Policy"
# Check scan progress
curl "http://localhost:8090/JSON/ascan/view/status/?apikey=YOUR_API_KEY&scanId=0"
# Get scan results (all alerts)
curl "http://localhost:8090/JSON/core/view/alerts/?apikey=YOUR_API_KEY&baseurl=https://target.example.com&start=0&count=100"
Insertion points
ZAP's active scanner tests parameters it discovers. For REST APIs where parameters are in the URL path (e.g., /api/users/123), configure ZAP to treat path segments as injection points. Under Tools → Options → Active Scan → Input Vectors, enable "URL path elements" for path-based API testing.
For JSON bodies, enable "JSON" and "XML" input vectors to ensure ZAP tests parameters within structured request bodies, not just form fields and URL parameters.
Passive Scanning
Passive scanning runs automatically on all traffic that flows through ZAP's proxy. It does not send any additional requests — it simply analyzes the requests and responses it sees and flags issues it can detect without probing. This makes it safe to run continuously and costs nothing in terms of traffic to the target.
Common passive scanner findings that matter:
- Missing or misconfigured security headers (CSP, HSTS, X-Frame-Options, X-Content-Type-Options)
- Session cookies without
HttpOnlyorSecureflags - Information disclosure in response headers (server version, X-Powered-By, stack traces)
- Mixed content (HTTPS page loading HTTP resources)
- Insecure cross-origin resource sharing (CORS) configurations
- Potentially sensitive data in URLs (tokens, passwords passed as GET parameters)
- Timestamp disclosure in responses
Passive scanner rules are add-ons — the full library is installed via the ZAP Marketplace (Help → Check for Updates). Install the "Passive Scanner Rules" add-on to get the complete ruleset. Most are enabled by default; individual rules can be toggled under Analyze → Passive Scan Rules.
Fuzzer for Parameter Testing
ZAP's Fuzzer is the functional equivalent of Burp Intruder — it sends a request repeatedly with payloads substituted into specified positions. Unlike Burp Intruder in Community edition, ZAP's Fuzzer is not rate-limited.
Setting up a fuzz attack
Right-click any request in the History tab → Attack → Fuzz. This opens the Fuzzer dialog with the request displayed. Select the text you want to fuzz (a parameter value, a header value, a URL segment) and click "Add."
ZAP ships with a library of built-in fuzzing payloads under the "Fuzz Files" section — these include lists for XSS, SQL injection, directory traversal, and common credentials. You can also load custom wordlists (any text file, one payload per line) or use the "Regex" type to generate payloads from a pattern.
For a SQL injection probe on a parameter:
GET /api/products?id=§1§ HTTP/1.1
Host: target.example.com
# ZAP Fuzzer payload sources to add:
# - File Fuzzers > jbrofuzz > Injection > SQL
# - File Fuzzers > jbrofuzz > Injection > SQL Injection
# - Your own custom payloads file
Fuzzer processors and message processors
ZAP's Fuzzer supports processors that transform payloads before sending them. Useful for testing WAF bypasses: add a Base64 Decode processor, a URL Encode processor, or a Script processor that applies a custom transformation. Chain multiple processors to test encoded injection strings.
Message processors run logic on each response. Use the "Regex" message processor to flag responses matching specific patterns (e.g., SQL error strings, stack traces), or write a script processor to apply custom logic to determine if a response indicates a successful injection.
Manual Interception with Break Points
ZAP's equivalent of Burp's Intercept mode is Break Points. When a break point is active, ZAP holds matching requests or responses for manual inspection and modification before forwarding them.
The simplest break point is the global break (the round green button in the toolbar) — this holds every request and response. More useful in practice are conditional break points:
- Break on requests to a specific URL or URL pattern
- Break when a request contains a specific header or parameter
- Break on responses with a specific status code
To add a conditional break point: Breakpoints tab → Add. Configure the criteria (URL, request/response, HTTP method) and enable it. When ZAP intercepts a matching message, it appears in the Break tab where you can edit headers, modify the body, or drop the request entirely.
The workflow for using break points is the same as Burp's intercept: navigate to the target endpoint in your browser, let ZAP capture the request, edit the values you want to test, click "Submit" to forward the modified request, then disable the break point. Keeping break points enabled continuously makes normal browsing unusable.
Authentication and Authenticated Scanning
Testing only unauthenticated functionality misses the majority of vulnerabilities in most applications. Getting ZAP to maintain an authenticated session requires configuring ZAP's authentication and session handling.
Form-based authentication
Under ZAP → Contexts (right-click the target in the Sites tree → Include in Context), configure authentication:
- Set Authentication Method to "Form-based Authentication"
- Point ZAP at the login URL and specify the username and password field names
- Configure the "Logged In" and "Logged Out" indicators — regex patterns that ZAP uses to detect whether the session is still valid (e.g., the presence of a logout link, or the absence of a login form)
- Add a User under the context with the test credentials
ZAP will then automatically re-authenticate when it detects the session has expired, allowing the spider and active scanner to operate on authenticated endpoints continuously.
Script-based authentication
For applications with more complex authentication flows — multi-step login, OAuth, TOTP — use a script-based authentication method. Write a JavaScript or Python script that performs the login sequence and sets the appropriate session token. This is more work to set up but handles any authentication mechanism.
// ZAP authentication script (JavaScript)
function authenticate(helper, paramsValues, credentials) {
var loginUrl = paramsValues.get("Login URL");
var username = credentials.getParam("username");
var password = credentials.getParam("password");
var msg = helper.prepareMessage();
var requestBody = "username=" + username + "&password=" + password;
msg.setRequestBody(requestBody);
msg.getRequestHeader().setURI(new URI(loginUrl, false));
msg.getRequestHeader().setMethod(HttpRequestHeader.POST);
msg.getRequestHeader().setHeader("Content-Type", "application/x-www-form-urlencoded");
helper.sendAndReceive(msg);
return msg;
}
function getRequiredParamsNames() { return ["Login URL"]; }
function getOptionalParamsNames() { return []; }
function getCredentialsParamsNames() { return ["username", "password"]; }
Scripts: Extending ZAP's Capabilities
ZAP's scripting engine is one of its most powerful features and where it genuinely differentiates from Burp Community. Scripts can extend nearly every part of ZAP's behavior.
Script types
- Active Scan Rules: custom vulnerability checks that run as part of the active scanner. Write a check for a vulnerability specific to your application or framework.
- Passive Scan Rules: passive checks that analyze proxied traffic. Detect custom security anti-patterns without sending any additional requests.
- Proxy scripts: intercept and modify all proxied traffic. Use for automatically modifying headers, adding authentication tokens, or transforming request/response bodies.
- HTTP Sender scripts: similar to proxy scripts but triggered by ZAP's internal tools (spider, active scanner) in addition to proxied traffic.
- Fuzzer processors: custom payload transformation logic for the Fuzzer.
- Authentication scripts: custom login flows (described above).
- Standalone scripts: run on demand for any automation task.
Zest scripts
Zest is ZAP's domain-specific scripting language, expressed as JSON. It is designed to be human-readable and embeds directly into ZAP without requiring external language runtimes. Zest scripts can record and replay sequences of HTTP requests with assertions — effectively a lightweight functional testing framework for security checks.
{
"title": "Check for sensitive information in API response",
"description": "Tests that user endpoint does not leak password hashes",
"prefix": "https://target.example.com",
"statements": [
{
"url": "https://target.example.com/api/v1/users/1",
"method": "GET",
"assertions": [
{
"rootExpression": {
"code": 200,
"not": false,
"elementType": "ZestExpressionStatusCode"
}
},
{
"rootExpression": {
"value": "password",
"variableName": "response.body",
"not": true,
"elementType": "ZestExpressionRegex"
}
}
],
"elementType": "ZestRequest"
}
]
}
Python and JavaScript scripts
For more complex logic, ZAP supports Python (via Jython) and JavaScript (via Nashorn or GraalVM JS). This opens up the full language ecosystem — you can import libraries, make external calls, and write production-quality script logic. Install the relevant scripting add-on from the ZAP Marketplace before using Python or JavaScript scripts.
ZAP REST API: Headless Control
ZAP's REST API exposes the full ZAP feature set over HTTP. Every GUI action has an API equivalent. The API is what makes ZAP scriptable and CI/CD-friendly.
API structure
The API is organized into components (core, spider, ascan, pscan, fuzzer, etc.) and three verb types:
/JSON/component/action/name/— performs an action (start a scan, clear a context, etc.)/JSON/component/view/name/— retrieves information (scan status, alerts, results)/OTHER/component/other/name/— retrieves non-JSON data (HTML reports, certificates)
The API key must be included as a query parameter (apikey=) or in the X-ZAP-API-Key header on every request.
A complete scan via API
A basic automated scan workflow against an authenticated target:
#!/bin/bash
ZAP_HOST="http://localhost:8090"
API_KEY="changeme"
TARGET="https://target.example.com"
# 1. Set the target in scope
curl -s "$ZAP_HOST/JSON/context/action/newContext/?apikey=$API_KEY&contextName=target" > /dev/null
curl -s "$ZAP_HOST/JSON/context/action/includeInContext/?apikey=$API_KEY&contextName=target®ex=https://target.example.com.*" > /dev/null
# 2. Run traditional spider
SPIDER_ID=$(curl -s "$ZAP_HOST/JSON/spider/action/scan/?apikey=$API_KEY&url=$TARGET&recurse=true" | jq -r '.scan')
while true; do
STATUS=$(curl -s "$ZAP_HOST/JSON/spider/view/status/?apikey=$API_KEY&scanId=$SPIDER_ID" | jq -r '.status')
echo "Spider status: $STATUS%"
[ "$STATUS" -eq 100 ] && break
sleep 5
done
# 3. Run active scan
SCAN_ID=$(curl -s "$ZAP_HOST/JSON/ascan/action/scan/?apikey=$API_KEY&url=$TARGET&recurse=true" | jq -r '.scan')
while true; do
STATUS=$(curl -s "$ZAP_HOST/JSON/ascan/view/status/?apikey=$API_KEY&scanId=$SCAN_ID" | jq -r '.status')
echo "Active scan status: $STATUS%"
[ "$STATUS" -eq 100 ] && break
sleep 10
done
# 4. Generate HTML report
curl -s "$ZAP_HOST/OTHER/core/other/htmlreport/?apikey=$API_KEY" -o zap-report.html
# 5. Get JSON alerts for CI/CD processing
curl -s "$ZAP_HOST/JSON/core/view/alerts/?apikey=$API_KEY&baseurl=$TARGET&start=0&count=9999" -o zap-alerts.json
echo "High severity alerts: $(jq '[.alerts[] | select(.risk=="High")] | length' zap-alerts.json)"
echo "Medium severity alerts: $(jq '[.alerts[] | select(.risk=="Medium")] | length' zap-alerts.json)"
ZAP Automation Framework
The Automation Framework (introduced in ZAP 2.11) provides a structured, YAML-based way to define and run complete scan workflows. It is the recommended approach for CI/CD integration, replacing the older Baseline, API, and Full scan scripts.
An Automation Framework plan defines a sequence of jobs — spider, passive scan, active scan, report generation — along with their configuration. The plan is stored as a YAML file and executed with:
zap.sh -cmd -autorun /path/to/automation-plan.yaml
Example plan for a CI/CD scan:
env:
contexts:
- name: "Target"
urls:
- "https://target.example.com"
includePaths:
- "https://target.example.com.*"
parameters:
failOnError: true
progressToStdout: true
jobs:
- type: spider
parameters:
context: "Target"
url: "https://target.example.com"
maxDuration: 2
maxChildren: 10
- type: ajaxSpider
parameters:
context: "Target"
url: "https://target.example.com"
maxDuration: 2
- type: passiveScan-wait
parameters:
maxDuration: 5
- type: activeScan
parameters:
context: "Target"
policy: "Default Policy"
- type: report
parameters:
template: "traditional-html"
reportDir: "/zap/reports"
reportFile: "zap-report"
- type: outputSummary
parameters:
format: Long
summaryFile: "/zap/reports/zap-summary.json"
ZAP in CI/CD: GitHub Actions Integration
OWASP provides official GitHub Actions for ZAP that make pipeline integration straightforward. Three actions cover the main use cases:
zaproxy/action-baseline@v0.12.0— passive scan only, fast, suitable for PR checkszaproxy/action-api-scan@v0.9.0— active scan optimized for APIs, requires an OpenAPI/RAML spec or GraphQL schemazaproxy/action-full-scan@v0.10.0— full spider + active scan, use for scheduled scans not PR checks
Baseline scan in a pull request workflow
name: ZAP Security Scan
on:
pull_request:
branches: [main]
jobs:
zap-scan:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Deploy to staging
run: ./deploy-staging.sh
env:
STAGING_URL: ${{ secrets.STAGING_URL }}
- name: ZAP Baseline Scan
uses: zaproxy/action-baseline@v0.12.0
with:
target: ${{ secrets.STAGING_URL }}
rules_file_name: .zap/rules.tsv
cmd_options: '-a'
- name: Upload ZAP Report
uses: actions/upload-artifact@v4
if: always()
with:
name: zap-report
path: report_html.html
The rules.tsv file lets you configure pass/warn/fail thresholds per alert rule ID. This is important for tuning out known accepted risks without disabling checks globally:
# .zap/rules.tsv format: rule_id\taction\tparameter
# Actions: IGNORE, WARN, FAIL
10020\tIGNORE\t# Anti-clickjacking header — acceptable on internal tools
10021\tWARN\t # X-Content-Type-Options missing
10038\tFAIL\t # Content Security Policy header missing
API scan with OpenAPI spec
- name: ZAP API Scan
uses: zaproxy/action-api-scan@v0.9.0
with:
target: https://api.target.example.com/v3/openapi.json
format: openapi
cmd_options: '-a -z "-config scanner.attackStrength=Medium"'
The API scan action loads your OpenAPI specification, generates requests for every endpoint and method defined in the spec, and runs an active scan against them. This is significantly more effective than scanning an API without a spec, because ZAP knows the expected parameters, types, and response schemas.
Common ZAP Findings and What They Mean
Understanding what ZAP actually reports — and which findings deserve your immediate attention — saves significant triage time.
High severity findings worth taking seriously
- SQL Injection: ZAP tests a broad range of injection patterns. High-confidence SQL injection findings are generally real. Low-confidence findings need manual verification — ZAP sometimes flags time-based injection candidates that are actually slow responses for other reasons.
- Path Traversal: When ZAP finds a path traversal, verify manually — some are false positives triggered by applications that reflect file paths in responses without actually allowing traversal.
- Remote File Inclusion / Remote OS Command Injection: High-severity, rare. When ZAP finds these, treat them as critical and verify immediately.
- External Redirect: ZAP detects open redirectors by injecting external URLs into redirect parameters. These are almost always real findings when ZAP reports them at High confidence.
Medium severity findings that need context
- Absence of Anti-CSRF Tokens: ZAP flags forms without CSRF tokens. Valid in many cases, but modern frameworks using SameSite cookies may have acceptable protection without explicit tokens.
- Cross-Domain Misconfiguration: CORS misconfiguration is worth investigating — ZAP's detection of overly permissive CORS policies is generally reliable.
- Content Security Policy Header Not Set: Accurate but low operational impact on its own. Note it, include in report, but do not treat it as a P1.
- Weak Authentication Method: ZAP flags basic authentication over HTTP. Legitimate if the application is actually using HTTP basic auth — verify it is not also HTTPS-accessible.
Findings that are usually noise
- X-Frame-Options Header Not Set: Mostly superseded by CSP
frame-ancestorsdirective. Informational for most modern applications. - Information Disclosure - Suspicious Comments: ZAP flags HTML comments it considers suspicious. Most are developer comments of no security value. Review quickly, almost always IGNORE.
- Timestamp Disclosure: Timestamps in responses rarely represent exploitable information disclosure. Flag as informational, move on.
Where ZAP Falls Short Compared to Burp Pro
Being direct about ZAP's limitations prevents wasted effort on the wrong tool for a given job.
Manual testing ergonomics: ZAP's request editor, HTTP History search, and manual modification workflow are functional but noticeably less polished than Burp. If you are doing intensive manual testing — hours of Repeater-style iteration, complex multi-request attack chains — Burp's UI is significantly more efficient.
No built-in out-of-band (OAST) detection: ZAP cannot detect blind SSRF, blind XXE, or DNS-based vulnerabilities without external tooling. You need to integrate interactsh or a similar service manually. Burp Collaborator handles this seamlessly in Pro.
Scanner false positive rate: ZAP generates more false positives than Burp's scanner on complex applications. Budget time for manual triage of ZAP results, especially in the medium-severity range. Burp Scanner's results tend to be higher confidence.
JavaScript execution in active scan: Burp's scanner executes JavaScript when probing for DOM-based XSS. ZAP's active scanner is more limited here — it finds reflected and stored XSS reliably but DOM XSS detection requires manual follow-up or custom scripts.
Intruder equivalent speed: ZAP's Fuzzer is fast, but Burp's Turbo Intruder extension for high-speed fuzzing and race condition testing has no equivalent in ZAP. For race condition testing specifically, Burp is the better tool.
Extension ecosystem quality: ZAP's add-ons are extensive but the quality and maintenance vary more than Burp's commercially-backed BApp Store. Burp extensions like Param Miner and JWT Editor have no ZAP equivalents of the same caliber.
None of these are reasons to avoid ZAP — they are reasons to understand the tool's positioning. ZAP at scale for automated continuous scanning is excellent. ZAP for a five-day manual pentest engagement is a step down from Burp Pro.
ZAP's strongest argument is not that it matches Burp feature-for-feature — it does not. Its argument is that it is free, open, deeply automatable, and good enough for continuous scanning across an application fleet that would be prohibitively expensive to cover with Burp Enterprise licenses. That is a compelling case for the majority of security programs.
The teams getting the most value from web application security invest in both: ZAP or a purpose-built automated scanner running continuously in CI/CD to catch the common, well-defined vulnerabilities on every deploy, and Burp (or Burp-trained practitioners) for the deep manual work on critical endpoints and business logic. The goal is coverage at scale with depth where it matters.
Ironimo automates continuous web app security scanning — start your first scan free.
Get professional-grade scanning powered by Kali Linux tooling without managing ZAP infrastructure, writing Automation Framework YAML, or triaging scanner noise manually.
Start free scan