Security Tools

Nuclei Vulnerability Scanner: Fast Template-Based Security Testing

How to install Nuclei, run vulnerability templates, write custom templates, and integrate the scanner into CI/CD pipelines for automated security testing.

March 9, 20266 min readShipSafer Team

Nuclei Vulnerability Scanner: Fast Template-Based Security Testing

Nuclei, built by ProjectDiscovery, is a fast, configurable vulnerability scanner that executes security checks using YAML templates. Unlike traditional scanners that embed logic in compiled code, Nuclei separates the scanning engine from the detection logic — making it trivially easy to add new checks, share them with the community, and customize detection for your specific targets. The template library contains over 9,000 community-contributed checks covering CVEs, misconfigurations, exposed panels, default credentials, and more. This guide covers installation, running templates, writing custom templates, and CI integration.

Installation

Go install (recommended):

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

Homebrew:

brew install nuclei

Docker:

docker pull projectdiscovery/nuclei:latest

Update templates after installation:

nuclei -update-templates

Templates are stored in ~/nuclei-templates/. Nuclei automatically updates them when running, but nuclei -update-templates forces an immediate refresh.

Basic Scanning

Scan a single target with all templates:

nuclei -u https://example.com

Scan a list of targets:

nuclei -list targets.txt

Filter by severity:

nuclei -u https://example.com -severity critical,high,medium

Filter by template tags:

nuclei -u https://example.com -tags cve,oast
nuclei -u https://example.com -tags misconfig,exposure
nuclei -u https://example.com -tags apache,nginx,iis

Run a specific template or directory:

nuclei -u https://example.com -t cves/2023/CVE-2023-44487.yaml
nuclei -u https://example.com -t cves/2023/
nuclei -u https://example.com -t ~/nuclei-templates/exposures/

Output formats:

nuclei -u https://example.com -o results.txt
nuclei -u https://example.com -json -o results.json
nuclei -u https://example.com -sarif-export results.sarif

Concurrency control. Nuclei runs many templates in parallel by default. Tune for aggressive scanning or to reduce server load:

# Reduce load on target
nuclei -u https://staging.example.com -c 5 -rate-limit 10

# Faster scanning (higher resource use)
nuclei -u https://staging.example.com -c 50 -rate-limit 500

Understanding Template Categories

The template library is organized by category:

  • cves/ — Checks for specific CVEs by year and ID. Regularly updated as new CVEs are published.
  • exposures/ — Detects exposed files, configs, and admin panels (.env files, Git repos, backup files, phpMyAdmin).
  • misconfigurations/ — Common server and application misconfigs (open redirects, CORS wildcards, debug endpoints).
  • default-logins/ — Tests default credentials against management interfaces (Jenkins, GitLab, Grafana, routers).
  • technologies/ — Technology fingerprinting (identify the stack before targeted testing).
  • network/ — TCP/UDP service checks (open databases, Redis without auth, exposed Memcached).
  • dns/ — DNS misconfigurations and subdomain takeover checks.
  • ssl/ — TLS configuration issues (expired certs, weak ciphers, BEAST/POODLE).

A practical recon + vulnerability workflow:

# First, fingerprint the technology stack
nuclei -u https://example.com -tags tech -silent

# Then run relevant CVEs and misconfigs
nuclei -u https://example.com -tags cve,misconfig -severity critical,high

Writing Custom Templates

Custom templates let you encode application-specific checks that the community library does not cover.

Template structure:

id: template-id

info:
  name: "Descriptive name for the check"
  author: "your-handle"
  severity: medium
  description: "What this template detects and why it matters"
  tags: custom,exposure

http:
  - method: GET
    path:
      - "{{BaseURL}}/admin/config"
      - "{{BaseURL}}/api/v1/internal/config"

    matchers-condition: and
    matchers:
      - type: status
        status:
          - 200

      - type: word
        words:
          - "database_password"
          - "db_secret"
        condition: or
        part: body

Example: Detect exposed Spring Boot Actuator endpoints:

id: spring-boot-actuator-exposed

info:
  name: "Spring Boot Actuator Endpoints Exposed"
  author: "security-team"
  severity: high
  description: >
    Spring Boot Actuator endpoints are publicly accessible, potentially
    exposing application internals, environment variables, and heap dumps.
  tags: springboot,exposure,misconfig

http:
  - method: GET
    path:
      - "{{BaseURL}}/actuator"
      - "{{BaseURL}}/actuator/env"
      - "{{BaseURL}}/actuator/heapdump"

    stop-at-first-match: true
    matchers-condition: and
    matchers:
      - type: status
        status:
          - 200

      - type: word
        words:
          - "_links"
          - "activeProfiles"
        part: body
        condition: or

Example: Check for a specific API endpoint returning internal data:

id: internal-user-data-leak

info:
  name: "Internal User Data Exposed in API Response"
  severity: critical
  tags: custom,api,exposure

http:
  - method: GET
    path:
      - "{{BaseURL}}/api/users?include=internal"

    headers:
      Accept: "application/json"

    matchers-condition: and
    matchers:
      - type: status
        status:
          - 200

      - type: word
        words:
          - "password_hash"
          - "ssn"
          - "credit_card"
        part: body
        condition: or

    extractors:
      - type: regex
        name: leaked-field
        regex:
          - '"(password_hash|ssn|credit_card)"\s*:'
        part: body

Testing templates before deployment:

nuclei -u https://staging.example.com -t custom-templates/ -debug

The -debug flag shows full request/response pairs, making it easy to understand why a template matches or does not match.

Out-of-Band (OOB) Testing with OAST

Nuclei integrates with interactsh for out-of-band detection — essential for blind vulnerabilities like Log4Shell, SSRF, and blind XSS where the payload effect is not reflected in the HTTP response.

# Use ProjectDiscovery's hosted interactsh
nuclei -u https://example.com -tags oast -interactsh-url https://oast.live

# Run your own interactsh server
interactsh-server -domain your-oob-domain.com
nuclei -u https://example.com -tags oast -interactsh-url https://your-oob-domain.com

Templates using {{interactsh-url}} inject an OOB callback URL. When the target makes a DNS or HTTP request to that URL (indicating the payload fired), Nuclei records the match.

CI/CD Integration

GitHub Actions

name: Nuclei Vulnerability Scan

on:
  schedule:
    - cron: '0 6 * * *'   # Daily at 6am UTC
  workflow_dispatch: {}

jobs:
  nuclei-scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Run Nuclei
        uses: projectdiscovery/nuclei-action@main
        with:
          target: https://staging.example.com
          flags: >
            -severity critical,high
            -tags cve,misconfig,exposure
            -exclude-tags dos
            -rate-limit 20
            -sarif-export nuclei-results.sarif
          github-report: true
          github-token: ${{ secrets.GITHUB_TOKEN }}

      - name: Upload SARIF results
        uses: github/codeql-action/upload-sarif@v3
        if: always()
        with:
          sarif_file: nuclei-results.sarif

The github-report: true option creates a GitHub issue with the scan report automatically.

Blocking PRs on New Critical Findings

For a security gate that fails PRs when new critical vulnerabilities are detected:

- name: Nuclei gate
  run: |
    nuclei \
      -u https://staging.example.com \
      -severity critical \
      -tags cve \
      -exclude-tags dos \
      -json \
      -o nuclei-output.json

    CRIT_COUNT=$(cat nuclei-output.json | jq '. | length' 2>/dev/null || echo 0)
    echo "Critical findings: $CRIT_COUNT"

    if [ "$CRIT_COUNT" -gt "0" ]; then
      echo "Build failed: critical vulnerabilities detected"
      cat nuclei-output.json | jq -r '.info.name + " — " + .host'
      exit 1
    fi

Safe Scanning Practices

Always exclude DoS templates. The -exclude-tags dos flag prevents templates that can crash or degrade the target service. Never run DoS templates against production.

Use -interactsh-disable in offline or air-gapped environments to prevent OOB callbacks that could leak target information.

Rate limit responsibly. Use -rate-limit 10 when scanning production-adjacent environments to avoid triggering WAF rules or alert fatigue in your monitoring.

Scope your scans. Use -include-hosts and -exclude-hosts to constrain scanning to in-scope assets:

nuclei -list all-hosts.txt \
  -exclude-hosts out-of-scope.txt \
  -severity critical,high \
  -exclude-tags dos,fuzz

Maintaining a Custom Template Repository

For teams with internal applications, maintain a private template repository:

# Add custom templates directory
nuclei -u https://example.com \
  -t ~/nuclei-templates/ \
  -t ~/company-templates/

# Or set in config
# ~/.config/nuclei/config.yaml
template-path:
  - ~/nuclei-templates
  - ~/company-templates

Store custom templates in version control, require peer review for new templates (especially those with severity: critical), and run automated template tests with nuclei -validate -t company-templates/ to catch YAML syntax errors before deployment.

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.