Writing Nuclei Templates: A Practical Guide for Security Teams (2026)

Published: April 8, 2026 Reading time: 14 minutes

📢 Affiliate Disclosure: This site contains affiliate links to Amazon. We earn a commission when you purchase through our links at no additional cost to you.

The biggest advantage Nuclei has over traditional scanners isn't the 9,000+ community templates — it's that you can write your own in YAML in under 10 minutes. When a new CVE drops, you don't wait for a vendor to ship a detection plugin. You write a template, test it, and scan your entire infrastructure before most teams have finished reading the advisory.

This guide covers everything you need to write production-quality Nuclei templates: the YAML structure, HTTP/DNS/network request types, matchers and extractors, multi-step workflows, and real-world examples you can adapt for your own scanning needs. If you've read our Nuclei vs Traditional Scanners comparison, this is the natural next step.

1. Anatomy of a Nuclei Template

Every Nuclei template has the same basic structure: metadata at the top, one or more request blocks in the middle, and matchers at the bottom. Here's the minimal skeleton:

id: example-check

info:
  name: Example Vulnerability Check
  author: your-name
  severity: medium
  description: Checks for example vulnerability
  tags: example,misconfiguration

http:
  - method: GET
    path:
      - "{{BaseURL}}/target-path"
    matchers:
      - type: word
        words:
          - "vulnerable-string"

The Four Required Sections

  • id — Unique identifier for the template. Use lowercase with hyphens. Convention: cve-YYYY-NNNNN for CVEs, product-issue-description for everything else.
  • info — Metadata: name, author, severity (info/low/medium/high/critical), description, and tags. Tags are how Nuclei filters templates — use them generously.
  • Request block — One of: http, dns, network, file, headless. Defines what Nuclei sends to the target.
  • matchers — Conditions that determine whether the response indicates a vulnerability. If all matchers pass, Nuclei reports a finding.

Severity Guidelines

Severity When to Use Example
info Technology detection, version fingerprinting Detecting Apache version from headers
low Information disclosure, missing headers Missing X-Frame-Options header
medium Misconfigurations, exposed panels Exposed admin panel without auth
high Auth bypass, data exposure, XSS Unauthenticated API endpoint returning user data
critical RCE, SQLi, SSRF to internal services Remote code execution via deserialization

2. Writing HTTP Templates

HTTP templates are the most common type. They send HTTP requests and analyze the response.

Basic GET Request

http:
  - method: GET
    path:
      - "{{BaseURL}}/.env"
    matchers:
      - type: word
        words:
          - "DB_PASSWORD"
          - "APP_KEY"
        condition: or

This checks if a .env file is publicly accessible — a common misconfiguration that exposes database credentials and API keys.

POST Request with Body

http:
  - method: POST
    path:
      - "{{BaseURL}}/api/login"
    headers:
      Content-Type: application/json
    body: '{"username":"admin","password":"admin"}'
    matchers-condition: and
    matchers:
      - type: status
        status:
          - 200
      - type: word
        words:
          - "token"
          - "session"
        condition: or

This tests for default credentials on a login endpoint. The matchers-condition: and means both matchers must pass — the response must be HTTP 200 AND contain "token" or "session".

Multiple Paths

http:
  - method: GET
    path:
      - "{{BaseURL}}/admin"
      - "{{BaseURL}}/administrator"
      - "{{BaseURL}}/wp-admin"
      - "{{BaseURL}}/cpanel"
    matchers:
      - type: status
        status:
          - 200
          - 301
          - 302

Nuclei sends a request to each path. If any returns a 200/301/302, it's flagged. This is useful for admin panel discovery.

3. Matchers: How Nuclei Decides "Vulnerable"

Matchers are the core of every template. They define the conditions that indicate a vulnerability. Nuclei supports six matcher types:

Word Matcher

The most common. Checks if the response body contains specific strings.

matchers:
  - type: word
    words:
      - "root:x:0:0"
    part: body

Regex Matcher

For pattern matching when exact strings aren't predictable.

matchers:
  - type: regex
    regex:
      - "(?i)mysql.*error"
      - "(?i)postgresql.*error"
      - "(?i)ora-[0-9]{5}"

This catches database error messages from MySQL, PostgreSQL, and Oracle — indicators of SQL injection.

Status Matcher

Checks the HTTP status code.

matchers:
  - type: status
    status:
      - 200

Binary Matcher

For matching hex patterns in binary responses (useful for file type detection).

DSL Matcher

The most powerful. Uses Nuclei's expression language for complex conditions.

matchers:
  - type: dsl
    dsl:
      - "status_code == 200 && contains(body, 'admin') && content_length > 1000"

Combining Matchers

Use matchers-condition to control how multiple matchers combine:

  • and (default) — All matchers must pass
  • or — Any matcher passing triggers a finding
matchers-condition: and
matchers:
  - type: status
    status:
      - 200
  - type: word
    words:
      - "phpinfo()"
  - type: word
    words:
      - "PHP Version"
    part: body

4. Extractors: Pulling Data from Responses

Extractors capture specific data from responses — version numbers, tokens, internal paths. They're essential for fingerprinting and multi-step workflows.

Regex Extractor

extractors:
  - type: regex
    part: body
    group: 1
    regex:
      - "Server: Apache/([0-9.]+)"

This extracts the Apache version number from the Server header. The group: 1 captures the first regex group.

JSON Extractor

extractors:
  - type: json
    part: body
    json:
      - ".version"
      - ".build.number"

Using Extracted Values in Subsequent Requests

Extractors can feed values into later requests using internal: true:

extractors:
  - type: regex
    name: csrf_token
    internal: true
    part: body
    group: 1
    regex:
      - 'name="csrf_token" value="([^"]+)"'

The extracted csrf_token can then be used as in the next request.

5. Multi-Step Workflows

Real-world vulnerability testing often requires multiple requests: authenticate first, then test the protected endpoint. Nuclei handles this with request pipelines.

http:
  - raw:
      - |
        POST /api/login HTTP/1.1
        Host: {{Hostname}}
        Content-Type: application/json

        {"username":"admin","password":"admin"}
      - |
        GET /api/admin/users HTTP/1.1
        Host: {{Hostname}}
        Authorization: Bearer {{token}}

    extractors:
      - type: json
        name: token
        internal: true
        part: body
        json:
          - ".access_token"

    matchers:
      - type: dsl
        dsl:
          - "status_code_2 == 200 && contains(body_2, 'email')"

This template: (1) logs in with default credentials, (2) extracts the access token, (3) uses it to access the admin users endpoint, (4) flags if the admin endpoint returns user data. The _2 suffix references the second request's response.

6. DNS and Network Protocol Templates

DNS Template

id: dns-zone-transfer

info:
  name: DNS Zone Transfer Check
  severity: high
  tags: dns,misconfiguration

dns:
  - name: "{{FQDN}}"
    type: AXFR
    matchers:
      - type: word
        words:
          - "IN A"
          - "IN MX"
        condition: or

Network (TCP) Template

id: redis-unauth

info:
  name: Redis Unauthenticated Access
  severity: critical
  tags: network,redis,misconfiguration

network:
  - host:
      - "{{Hostname}}"
    port: 6379
    inputs:
      - data: "INFO\r\n"
    read-size: 2048
    matchers:
      - type: word
        words:
          - "redis_version"

This connects to Redis on port 6379, sends the INFO command, and checks if it responds without authentication — a critical misconfiguration that exposes the entire database.

7. Real-World Examples

Example 1: Exposed Git Repository

id: git-config-exposure

info:
  name: Git Config Exposure
  author: security-team
  severity: medium
  description: Checks for exposed .git/config files
  tags: exposure,git,misconfiguration

http:
  - method: GET
    path:
      - "{{BaseURL}}/.git/config"
    matchers-condition: and
    matchers:
      - type: status
        status:
          - 200
      - type: word
        words:
          - "[core]"
          - "[remote"
        condition: or

Example 2: WordPress User Enumeration

id: wp-user-enum

info:
  name: WordPress User Enumeration
  severity: low
  tags: wordpress,enum

http:
  - method: GET
    path:
      - "{{BaseURL}}/wp-json/wp/v2/users"
    matchers-condition: and
    matchers:
      - type: status
        status:
          - 200
      - type: word
        words:
          - '"slug"'
          - '"name"'
        condition: and
    extractors:
      - type: json
        json:
          - ".[].slug"

Example 3: CORS Misconfiguration

id: cors-wildcard-check

info:
  name: CORS Wildcard on Authenticated Endpoint
  severity: high
  tags: cors,misconfiguration

http:
  - method: GET
    path:
      - "{{BaseURL}}/api/user/profile"
    headers:
      Origin: https://evil.com
    matchers-condition: and
    matchers:
      - type: word
        part: header
        words:
          - "Access-Control-Allow-Origin: *"
      - type: status
        status:
          - 200

8. Testing and Debugging Templates

Validate Syntax

nuclei -t your-template.yaml -validate

Checks YAML syntax and template structure without sending any requests.

Debug Mode

nuclei -t your-template.yaml -u https://target.com -debug

Shows the full HTTP request sent and response received. Essential for understanding why matchers aren't triggering.

Test Against Vulnerable Apps

Never test templates against production systems you don't own. Use intentionally vulnerable applications:

  • DVWA (Damn Vulnerable Web Application) — Classic, covers SQLi, XSS, file inclusion
  • Juice Shop — Modern OWASP Top 10 coverage, Node.js-based
  • WebGoat — OWASP's official training app
  • VulnHub — Downloadable vulnerable VMs for network-level testing

Common Debugging Issues

  • Template not matching — Check part setting. Default is body. If you're matching headers, set part: header.
  • YAML parse errors — Indentation matters. Use 2 spaces, never tabs. Strings with special characters need quoting.
  • Regex not capturing — Test your regex separately at regex101.com. Nuclei uses Go's RE2 syntax (no lookaheads).
  • Multi-step variables empty — Ensure the extractor has internal: true and the variable name matches exactly.

9. Best Practices

Template Quality Checklist

  1. Minimize false positives — Use multiple matchers with and condition. A status code alone is never enough.
  2. Set accurate severity — Overstating severity erodes trust. An exposed version number is info, not medium.
  3. Add references — Include CVE IDs, advisory URLs, and CWE numbers in the info block.
  4. Tag generously — Tags like cve, owasp-top10, wordpress, misconfiguration help teams filter scans.
  5. Test on known-vulnerable targets — Confirm the template triggers on a vulnerable target AND doesn't trigger on a patched one.
  6. Keep templates focused — One template, one vulnerability. Don't combine unrelated checks.
  7. Use extractors for evidence — Extract the specific vulnerable version, exposed path, or leaked data. This helps triage.
  8. Document the remediation — Add a remediation field in the info block so the scan report is actionable.

Organizing Your Template Library

custom-templates/
├── cves/           # CVE-specific detection
├── misconfig/      # Configuration issues
├── exposed-panels/ # Admin panels, dashboards
├── takeovers/      # Subdomain takeover checks
├── internal/       # Company-specific checks
└── workflows/      # Multi-template workflows

Keep your custom templates in a separate directory from the community templates. This makes updates clean — you can pull the latest community templates without overwriting your custom work.

Bottom Line

Nuclei templates are YAML files that define what to send and what to look for. The learning curve is shallow — if you can write a curl command, you can write a Nuclei template. Start with the community templates repository, read 5-10 templates in the misconfiguration folder to understand the patterns, then write your first custom template for something specific to your environment.

The security teams that get the most value from Nuclei aren't the ones running the default template set — they're the ones writing custom templates for their own infrastructure, their own tech stack, and the specific vulnerabilities that matter to their business.

For more on how Nuclei compares to traditional scanners, see our Nuclei vs Traditional Scanners comparison. For CI/CD integration, check our ZAP in GitHub Actions guide — the same patterns apply to Nuclei.

Advertisement