
OWASP ZAP in CI Without Wrecking Your Pipeline
A baseline scan on every pull request, a full scan nightly. Getting that split wrong is why teams turn DAST off.
Most teams that adopt dynamic scanning turn it off within a month. Not because it finds nothing — because they wired it in the way that makes every pull request take forty minutes and fail on findings nobody can action.
The split between a fast check and a deep scan is the whole trick.
Two scans, two purposes
Baseline, on every pull request. ZAP crawls the running application passively and reports what it observes without attacking anything. It finishes in a couple of minutes, and it catches the regressions that actually happen: a missing security header, a cookie that lost its Secure flag, a debug endpoint that shipped, mixed content.
docker run --rm -t ghcr.io/zaproxy/zaproxy:stable \
zap-baseline.py -t https://staging.example.com -I
Full active scan, nightly. This one sends actual attack payloads and takes as long as it takes. It never blocks a developer because it never runs in their loop.
Getting this backwards — active scanning on pull requests — is the single most common reason DAST gets abandoned.
Making the results actionable
Use a rules configuration file to set each alert to IGNORE, WARN or FAIL deliberately. Only a small set should fail the build. Everything else warns, and you promote rules to failing as you fix the backlog.
The goal is a build that stays green until something new is introduced. A perpetually red build teaches everyone to ignore red.
Scan something that resembles production
A staging environment with representative data and the same configuration. Scanning a local dev server with debug mode on produces findings that do not exist in production and misses the ones that do.
Authenticate the scanner if the interesting parts of the application are behind login — which they always are. That is more setup than the unauthenticated scan and it is where the real findings live.
The boundary
Only scan systems you own or are contracted to test, and let the client know before an active scan runs against their staging environment. Active scanning submits forms, follows destructive links, and can fill a database with junk.
I have seen a scan send several hundred test enquiries into a client's CRM. Once.
Resources
- Repo: zaproxy/zaproxy
- Docs: zaproxy.org/docs
- Video walkthroughs: YouTube: OWASP ZAP CI/CD
- Static counterpart: Semgrep custom rules
Need this built properly?
I build secure, fast, bilingual platforms for clients across Egypt, Saudi Arabia, the UAE and Kuwait.


