Practical patterns: validation, logs and URLs
Realistic patterns for the values people actually validate and extract, and an honest account of where each one stops being enough.
Email, URLs and identifiers
// a deliberately loose email check: reject the obvious, do not claim RFC conformance
const EMAIL = /^[^\s@]+@[^\s@.]+(?:\.[^\s@.]+)+$/;
EMAIL.test('[email protected]'); // true
EMAIL.test('a@@example.com'); // false
EMAIL.test('a [email protected]'); // false
// an absolute HTTP URL, without trying to parse the query string
const URL_RE = /^https?:\/\/[^\s/?#]+(?:[/?#][^\s]*)?$/i;
URL_RE.test('https://example.com/a?b=1'); // true
URL_RE.test('ftp://example.com'); // false
// a slug, useful for routing and for generating file names
const SLUG = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
SLUG.test('regex-practical-patterns'); // true- Deliverability is decided by sending mail and reading bounces, not by a pattern. A strict email regex rejects valid addresses far more often than it catches typos.
new URL(value)in JavaScript andurllib.parsein Python understand URLs, including relative ones and percent-encoding. Use regex for the shape and a parser for the parts.- Tighten with length limits in code: a 400-character slug passes the pattern above.
Dates, IP addresses and CSV fields
// ISO date, shape only; validate the day count in code
const ISO = /^(\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/;
ISO.exec('2026-09-18').slice(1); // [ '2026', '09', '18' ]
ISO.test('2026-13-01'); // false, month 13
// IPv4: the octet group must forbid values above 255
const IPV4 = /^(?:(?:25[0-5]|2[0-4]\d|1?\d?\d)\.){3}(?:25[0-5]|2[0-4]\d|1?\d?\d)$/;
IPV4.test('192.168.0.1'); // true
IPV4.test('256.1.1.1'); // false
// one CSV field, quoted form first so the alternation order matters
const FIELD = /(?:^|,)(?:"([^"]*)"|([^",]*))/g;| Value | Regex is good for | Use a parser when |
|---|---|---|
| ISO date | the shape and the month range | you care that 2026-02-30 is not a real date |
| IPv4 / IPv6 | IPv4 dotted quads; IPv6 is impractical | the address must be validated or normalised |
| CSV | one simple field or a quick split | fields contain commas, quotes or newlines — use a CSV library |
| Credit card | digit grouping and length | you need a Luhn check and a BIN lookup |
| Phone number | a light shape check | you need country rules — use a phone-number library |
Alternation is ordered, so (?:25[0-5]|2[0-4]\d|1?\d?\d) must list the strict branches first. Putting 1?\d?\d at the front would let 2 match, then fail the following \. and force backtracking on every octet.
Extracting from log lines
import re
LINE = re.compile(r'''
\A
(?P<ip>\S+) \s+
(?P<ident>\S+) \s+
(?P<user>\S+) \s+
\[(?P<time>[^\]]+)\] \s+
"(?P<method>[A-Z]+) \s+ (?P<path>\S+) \s+ (?P<proto>[^"]+)" \s+
(?P<status>\d{3}) \s+
(?P<size>\d+|-)
\s*\z
''', re.VERBOSE)
line = '10.0.0.7 - alice [18/Sep/2026:10:00:00 +0000] "GET /learn/ajax/ HTTP/1.1" 200 5123'
row = LINE.match(line).groupdict()
row['status'], row['path'] # ('200', '/learn/ajax/')💡
Anchor log patterns at both ends and make every field explicit. A pattern that silently accepts a truncated line produces empty groups rather than an error, and empty groups in an analytics pipeline look exactly like real zero values.
FAQ
Should validation happen on the client, the server, or both?
Always on the server, where it is a security boundary. Client-side checks are a convenience for the user. Share the intent, not necessarily the pattern, because the two engines and the two versions of the data differ.
Why does my IPv4 pattern accept 999.1.1.1?
The octet group is too permissive. Each octet needs the ordering
25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d and must be anchored between the dots, or a larger number is matched partially.Related
Regex in Python When not to use regex
Last refreshed 2026-09-18.