Safeguard
AppSec

How to Run a ZAP Scan: OWASP ZAP for Practical Web App Testing

A hands-on guide to running a ZAP scan against your own web app, from the passive baseline to a full active scan, and how to wire it into CI.

Priya Mehta
Security Analyst
6 min read

A ZAP scan uses OWASP ZAP, the open-source web application scanner, to probe a running site for security issues, and the practical starting point is the passive baseline scan because it finds real problems without ever attacking the target. ZAP (the Zed Attack Proxy) is one of the most widely used dynamic application security testing tools, and it is free. The reason teams stall on it is not cost or capability; it is that "run a scan" can mean anything from a five-minute header check to an aggressive active scan that can take a site down, and mixing those up leads to either useless results or an incident.

Here is how to run each kind of ZAP scan deliberately, and how to make it part of your pipeline.

What ZAP Actually Does

ZAP sits between a client and your web application as an intercepting proxy. As traffic flows through, it observes requests and responses (passive scanning) and can optionally craft its own malicious-looking requests to test how the app responds (active scanning). It ships as a desktop application with a full GUI, and as headless automation images built for CI.

The critical distinction, and the one that determines whether a scan is safe to run, is passive versus active:

Passive scanning only inspects traffic that already happened. It never sends an attack payload. It flags things like missing security headers, cookies without the Secure or HttpOnly flags, and information disclosure. It is safe to run against anything, including production, because it changes nothing.

Active scanning sends crafted requests designed to trigger vulnerabilities: SQL injection probes, XSS payloads, path traversal attempts. It is genuinely attacking the target. You run it only against systems you own and, ideally, against a staging environment, never against production without a great deal of care, because it can create data, trigger side effects, and generate heavy load.

The Baseline Scan: Start Here

The ZAP baseline scan is the right first move. It spiders the site, runs passive scanning, and reports what it finds, without active attacks. The official automation image makes it a one-liner:

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

The -t flag is the target and -r writes an HTML report. The baseline scan runs quickly, finishes in minutes for most sites, and typically surfaces a batch of easy, real wins: a missing Content-Security-Policy, an X-Frame-Options gap, cache-control on sensitive responses. Because it is non-invasive, this is the version you can safely run on a schedule and even against production if you want continuous header monitoring.

The Full Active Scan

When you want ZAP to actually test for exploitable vulnerabilities, you move to the full scan, which adds active scanning on top of spidering and passive analysis:

docker run --rm -t ghcr.io/zaproxy/zaproxy:stable \
  zap-full-scan.py -t https://staging.example.com -r zap-full-report.html

This takes much longer, from tens of minutes to hours depending on the size of the app, and it will hammer the target with attack payloads. Point it at a dedicated test environment with representative data, not production and not a shared environment other people are using. Expect it to create records, submit forms, and generally behave like a determined attacker, because that is the point.

Two things make active scans far more useful. First, authentication: an unauthenticated scan only sees the login page, so configure ZAP with a valid session (via a context and authentication script, or by feeding it a session token) so it can reach the pages that actually hold sensitive functionality. Second, coverage: a spider alone misses a lot of a modern single-page app, so either use the AJAX spider or, better, replay a recorded set of real API calls through ZAP so it scans the endpoints your app genuinely uses.

Cutting Down the Noise

Any DAST tool, ZAP included, produces false positives, and a wall of alerts nobody triages is worse than no scan at all. ZAP supports a configuration file where you mark specific rules as ignored, warned, or failed. Committing that file alongside your app turns the scan from a firehose into a curated gate:

docker run --rm -t ghcr.io/zaproxy/zaproxy:stable \
  zap-baseline.py -t https://staging.example.com \
  -c zap-rules.conf -r zap-report.html

Start permissive, review each finding once, mark the genuine false positives as ignored with a comment explaining why, and let the rest fail the build. Over a few iterations you converge on a signal you trust.

Running ZAP in CI

The reason ZAP ships headless automation scripts is so it can run on every relevant change rather than as an occasional manual exercise. A common pattern: deploy the app to an ephemeral staging environment in the pipeline, run the baseline scan against it on every pull request as a fast gate, and run the heavier full scan nightly or before a release when a longer runtime is acceptable.

Keep the scanner image pinned to a known tag rather than latest, so a scanner update does not silently change your results mid-week, and archive the HTML report as a build artifact so findings are reviewable after the fact.

DAST is one layer, and it is strongest when paired with the others. ZAP tests the running app from the outside; software composition analysis looks at the dependencies inside it, and static analysis reads the source. Safeguard's DAST capability follows the same run-it-in-the-pipeline model, and pairing dynamic scanning with dependency analysis covers both the exploitable-behavior and known-vulnerable-component angles. Our security academy walks through building a layered testing program if you want to see how the pieces fit.

FAQ

Is it safe to run a ZAP scan against production?

A baseline (passive) scan is safe against production because it only inspects existing traffic and sends no attacks. A full (active) scan sends real attack payloads and can create data or cause load, so run it only against a staging environment you own.

What is the difference between the ZAP baseline and full scan?

The baseline scan spiders the site and does passive analysis only, finishing in minutes without attacking. The full scan adds active scanning that sends crafted attack requests, takes much longer, and must be pointed at a test environment.

Do I need to authenticate ZAP for useful results?

For anything behind a login, yes. An unauthenticated scan only reaches public pages. Configure ZAP with a valid session or authentication context so it can scan the authenticated functionality where most sensitive vulnerabilities live.

How do I handle ZAP false positives?

Use a rules configuration file to mark confirmed false positives as ignored, with a comment explaining the decision, while letting genuine findings fail the build. Review new alerts once and tune iteratively until the results are trustworthy.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.