Writing Custom Nuclei Templates: From YAML Basics to Advanced Matchers
Nuclei's power comes from its template library, but the real productivity gain is writing your own templates for target-specific checks. When you find a vulnerability class that affects multiple endpoints or organizations, a custom template lets you scan thousands of targets in minutes. This guide covers everything from template structure basics to advanced multi-step flows.
Template Structure
Nuclei Template Categories Reference
| Template Category | Matchers | Severity | Example CVE/Use | Notes |
|---|---|---|---|---|
| HTTP request | Status code, body regex, header match | Info-Critical | CVE-2022-22965 Spring4Shell | Most common type |
| DNS template | CNAME record match | Medium-High | Subdomain takeover detection | Fast passive check |
| Network (TCP) | Banner regex, port match | Medium-High | Redis no-auth, Elasticsearch | Raw socket connection |
| Headless (browser) | DOM text, JavaScript eval | High | DOM XSS, JavaScript-heavy apps | Slow but powerful |
| Workflow | Chain multiple templates | Varies | Login + admin check chain | Multi-step testing |
| Code template | External tool execution | Varies | Run nmap, ffuf from template | Requires code: true |
| File template | Local file content match | Info | Scan local files for secrets | Offline scanning |
Nuclei Template YAML Tip
Use the flow: field (introduced in Nuclei v2.9) to create conditional template logic — for example, only run an exploitation request if a prior detection request matches. This reduces false positives and makes templates more targeted.
Every Nuclei template has three required sections: id, info, and at least one protocol block. Here's the minimal skeleton:
id: my-template-id
info:
name: My Custom Check
author: yourhandle
severity: high
description: What this template detects
tags: custom,sqli,auth
requests:
- method: GET
path:
- "{{BaseURL}}/endpoint"
matchers:
- type: word
words:
- "vulnerable string"
HTTP Matchers
Matchers determine whether a response constitutes a finding. Nuclei supports five matcher types:
Status Matcher
matchers:
- type: status
status:
- 200
- 302
Word Matcher
Matches literal strings in the response. Specify part to target body, header, or the full response:
matchers:
- type: word
part: body
words:
- "root:x:0:0"
- "/bin/bash"
condition: and # both words must appear
Regex Matcher
matchers:
- type: regex
part: body
regex:
- "(?i)sql syntax.*mysql"
- "Warning.*mysql_.*"
- "valid MySQL result"
Header Matcher
matchers:
- type: word
part: header
words:
- "X-Powered-By: PHP/5"
Binary Matcher
Matches specific byte sequences in binary responses — useful for detecting specific file signatures.
DSL Expressions for Complex Matching
DSL matchers let you write logic that can't be expressed with simple word or regex matching. They support a JavaScript-like expression language:
matchers:
- type: dsl
dsl:
# Match only if response time exceeds 5 seconds (time-based blind SQLi)
- "duration >= 5"
# Match 200 response with body length greater than 1000
- "status_code == 200 && len(body) > 1000"
# Match if response body contains "error" but header doesn't have WAF signature
- "contains(body, 'error') && !contains(all_headers, 'cf-ray')"
Extractors for Dynamic Values
Extractors pull values from responses for use in subsequent requests — essential for multi-step templates:
extractors:
- type: regex
name: csrf_token
part: body
regex:
- 'name="csrf_token" value="([a-zA-Z0-9]+)"'
group: 1 # capture group 1
- type: json
name: auth_token
part: body
json:
- ".token" # JSONPath expression
Multi-Step Templates: Login Then Act
Multi-step templates chain requests, passing extracted values forward. This is how you test authenticated endpoints:
id: auth-idor-check
info:
name: Authenticated IDOR Check
severity: high
requests:
- raw:
- |
POST /api/login HTTP/1.1
Host: {{Hostname}}
Content-Type: application/json
{"username":"{{username}}","password":"{{password}}"}
extractors:
- type: json
name: token
json:
- ".access_token"
internal: true # don't report this extraction, just pass it forward
- raw:
- |
GET /api/users/1 HTTP/1.1
Host: {{Hostname}}
Authorization: Bearer {{token}}
matchers:
- type: word
part: body
words:
- '"email"'
- '"phone"'
condition: and
Fuzzing Templates with Payloads
Use the payloads key to inject lists of values into template variables:
id: sqli-error-fuzz
info:
name: SQLi Error-Based Fuzzing
severity: high
requests:
- method: GET
path:
- "{{BaseURL}}/search?q={{injection}}"
payloads:
injection:
- "'"
- "''"
- "' OR '1'='1"
- "1 AND 1=2"
- "' AND SLEEP(5)--"
matchers-condition: or
matchers:
- type: regex
regex:
- "(?i)sql syntax.*mysql"
- "(?i)warning.*mysql"
- type: dsl
dsl:
- "duration >= 5"
Use the Nuclei Template Builder to generate template scaffolding for common vulnerability classes and test matcher logic against sample responses before running at scale.
Rate Limiting Configuration
Avoid burning targets or getting rate-limited yourself. Add rate limiting at the template level:
requests:
- method: GET
path:
- "{{BaseURL}}/api/{{endpoint}}"
race-condition: false
race-count: 1
max-redirects: 3
pipeline: false
unsafe: false
req-condition: false
Run templates with global rate limiting flags:
nuclei -t ./my-templates/ -l targets.txt -rate-limit 10 # requests per second
-c 5 # concurrent templates
-timeout 10 # seconds per request
-retries 1
Running Templates Against Targets
# Single target, single template
nuclei -t sqli-error-fuzz.yaml -u https://example.com
# Multiple targets from file
nuclei -t ./custom-templates/ -l targets.txt -o results.txt
# Run with specific tags
nuclei -l targets.txt -tags sqli,xss,idor -severity high,critical
# Resume interrupted scan
nuclei -l targets.txt -t ./templates/ -resume resume.cfg
Template Submission and Community
If your template identifies a generic vulnerability class (not target-specific), submit it to the official nuclei-templates repository. Follow their contribution guidelines: include a verified proof-of-concept URL in the template comments, use the correct severity, and test against a known-vulnerable instance before submitting.
Pair your Nuclei workflow with the Pentest Findings Documenter to convert Nuclei JSON output into structured report findings automatically.
Level up your security testing
Install the CLI
npx payload-playgroundExplore All Tools
Encoding, hashing, JWT & more
Browse Cheat Sheets
Quick-reference payload guides