taptools/blog/Common Regex Mistakes
RegexMay 16, 2026 · 7 min read

Common Regex Mistakes Developers Make

Regex bugs are hard to spot and can cause everything from incorrect validation to server crashes. These are the most common mistakes and how to fix them.

Mistake 1 — Missing anchors (^ and $)

Without anchors a regex matches anywhere in the string — not just the full string. This causes validation to pass for strings that should be rejected.

// ❌ Missing anchors — matches substring
const digitRegex = /d{4}/
digitRegex.test('abc1234xyz')  // true! (matches 1234 inside)

// ✅ With anchors — matches full string only
const digitRegexAnchored = /^d{4}$/
digitRegexAnchored.test('abc1234xyz')  // false ✅
digitRegexAnchored.test('1234')        // true ✅

// Real world impact
const zipCode = /d{5}/  // ❌ Missing anchors
zipCode.test('not-a-zipcode-12345')  // true — wrong!

const zipCodeFixed = /^d{5}(-d{4})?$/  // ✅
zipCodeFixed.test('not-a-zipcode-12345') // false ✅

Mistake 2 — ReDoS — catastrophic backtracking

ReDoS (Regular Expression Denial of Service) occurs when a malicious input causes a regex to take exponential time to evaluate. This can crash your server or make it unresponsive.

// ❌ Vulnerable regex — catastrophic backtracking
const vulnerable = /^(a+)+$/

// Malicious input
const input = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaab'
vulnerable.test(input)  // Takes exponential time! 💥

// ✅ Fixed — possessive quantifier or atomic group
// In JavaScript use a linear alternative
const safe = /^a+$/  // Simple, no backtracking issue

// Real world example — email regex ReDoS
// ❌ Vulnerable
/^([a-zA-Z0-9])(([\-.]|[_]+)?([a-zA-Z0-9]+))*(@){1}[a-z0-9]+[.]{1}(([a-z]{2,3})|([a-z]{2,3}[.]{1}[a-z]{2,3}))$/

// ✅ Use a simple pragmatic pattern instead
/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$/

Mistake 3 — Greedy vs lazy matching

const html = '<b>bold</b> and <b>more bold</b>'

// ❌ Greedy — matches as much as possible
/<b>.*</b>/.exec(html)[0]
// Returns: '<b>bold</b> and <b>more bold</b>'
// Matched too much!

// ✅ Lazy — matches as little as possible
/<b>.*?</b>/.exec(html)[0]
// Returns: '<b>bold</b>'
// Correct!

// The ? after a quantifier makes it lazy
// .*  = greedy (match as much as possible)
// .*? = lazy   (match as little as possible)

Mistake 4 — Not escaping special characters

// ❌ Unescaped dot matches ANY character
const version = /1.2.3/
version.test('1X2Y3')  // true! (dot = any char)

// ✅ Escaped dot matches literal dot
const versionFixed = /1.2.3/
versionFixed.test('1X2Y3')  // false ✅
versionFixed.test('1.2.3')  // true ✅

// Characters that need escaping in regex:
// . * + ? ^ $ { } [ ] | ( ) // Escape them with backslash: . * + etc.

// Helper function to escape any string for regex
function escapeRegex(string) {
  return string.replace(/[.*+?^${}()|[]\]/g, '\$&')
}

Mistake 5 — Using regex when you shouldn't

Regex is powerful but not always the right tool. For some validations a simple string check or a dedicated library is cleaner, faster and less error-prone.

// ❌ Regex for simple checks
const startsWithHttp = /^https?:///
startsWithHttp.test(url)

// ✅ String method — simpler and faster
url.startsWith('http://') || url.startsWith('https://')

// ❌ Complex email regex
/^[a-zA-Z0-9.!#$%&'*+/=?^_{|}~-]+@.../.test(email)

// ✅ Dedicated library for complex validation
import { isEmail } from 'validator'
isEmail(email)  // handles all RFC edge cases

Test your regex patterns safely

Our regex tester helps you spot mistakes with real-time match highlighting.

Open Regex Tester →