URL Regex
A pragmatic http(s) link gate and a strict host-checking variant — CI-verified match rows, six language spellings, live tester preloaded.
Pattern variants
Pragmatic
/^https?:\/\/[^\s/$.?#][^\s]*$/i
What it matches
http:// or https:// in any case, then any whitespace-free rest — a quick shape gate for links.
What it deliberately doesn't
Scheme-less strings, other schemes (ftp:), anything containing spaces — and it never checks the host is plausible.
Strict
/^https?:\/\/(?:[A-Za-z0-9]+(?:-+[A-Za-z0-9]+)*\.)+[A-Za-z]{2,}(?::\d{1,5})?(?:[/?#][^\s]*)?$/
What it matches
A lowercase http(s) scheme, a dotted LDH host with a letters-only TLD, then an optional port and optional path, query or fragment.
What it deliberately doesn't
localhost and IP hosts, credentials before the host, uppercase schemes and internationalized domains.
Verified vectors
| Input | Pragmatic | Strict | Note |
|---|---|---|---|
"https://www.example.com/products?page=2" | ✓ matches | ✓ matches | |
"http://example.com" | ✓ matches | ✓ matches | |
"https://sub.example.co.uk/a/b?q=1#frag" | ✓ matches | ✓ matches | deep path, query and fragment |
"https://example.com:8443/health" | ✓ matches | ✓ matches | explicit port |
"HTTPS://EXAMPLE.COM/PATH" | ✓ matches | ✗ no match | uppercase scheme — strict wants the canonical form |
"http://localhost:3000" | ✓ matches | ✗ no match | dev URL without a dotted TLD |
"http://192.168.0.1/admin" | ✓ matches | ✗ no match | IP host |
"https://user:pass@example.com" | ✓ matches | ✗ no match | credentials in the URL |
"example.com" | ✗ no match | ✗ no match | no scheme |
"ftp://example.com" | ✗ no match | ✗ no match | different scheme |
"https://exa mple.com" | ✗ no match | ✗ no match | space in the host |
"https://.example.com" | ✗ no match | ✗ no match | empty first label |
"https://example.com/path with space" | ✗ no match | ✗ no match | space in the path |
Usage by language
JavaScript
Machine-verified/^https?:\/\/[^\s/$.?#][^\s]*$/i
/^https?:\/\/(?:[A-Za-z0-9]+(?:-+[A-Za-z0-9]+)*\.)+[A-Za-z]{2,}(?::\d{1,5})?(?:[/?#][^\s]*)?$/
Python
Machine-verifiedre.compile(r"^https?:\/\/[^\s/$.?#][^\s]*$", re.IGNORECASE)
re.compile(r"^https?:\/\/(?:[A-Za-z0-9]+(?:-+[A-Za-z0-9]+)*\.)+[A-Za-z]{2,}(?::\d{1,5})?(?:[/?#][^\s]*)?$")
Java
Syntax-reviewed — not machine-verifiedPattern.compile("^https?:\\/\\/[^\\s/$.?#][^\\s]*$", Pattern.CASE_INSENSITIVE)
Pattern.compile("^https?:\\/\\/(?:[A-Za-z0-9]+(?:-+[A-Za-z0-9]+)*\\.)+[A-Za-z]{2,}(?::\\d{1,5})?(?:[/?#][^\\s]*)?$")
Go
Syntax-reviewed — not machine-verifiedregexp.MustCompile(`(?i)^https?:\/\/[^\s/$.?#][^\s]*$`)
regexp.MustCompile(`^https?:\/\/(?:[A-Za-z0-9]+(?:-+[A-Za-z0-9]+)*\.)+[A-Za-z]{2,}(?::\d{1,5})?(?:[/?#][^\s]*)?$`)
C#
Syntax-reviewed — not machine-verifiednew Regex(@"^https?:\/\/[^\s/$.?#][^\s]*$", RegexOptions.IgnoreCase)
new Regex(@"^https?:\/\/(?:[A-Za-z0-9]+(?:-+[A-Za-z0-9]+)*\.)+[A-Za-z]{2,}(?::\d{1,5})?(?:[/?#][^\s]*)?$")
PHP
Syntax-reviewed — not machine-verified'/^https?:\\/\\/[^\\s\\/$.?#][^\\s]*$/i'
'/^https?:\\/\\/(?:[A-Za-z0-9]+(?:-+[A-Za-z0-9]+)*\\.)+[A-Za-z]{2,}(?::\\d{1,5})?(?:[\\/?#][^\\s]*)?$/'
Caveats
- A URL regex checks shape, not reachability or safety — for parsing, the URL constructor is the right tool.
- The pragmatic gate accepts credentials, IPs and unusual hosts by design; switch to strict when you need a public web link.
Related patterns
Do it offline:$ grep -E -i '^https?://[^[:space:]/$.?#][^[:space:]]*$' urls.txt
How to use it
- Gate pasted links with the pragmatic variant when the goal is catching garbage — a missing scheme, an inner space — before storage.
- Choose strict for outbound public links: it refuses localhost, bare IP hosts and credential-carrying forms that rarely belong in shared content.
- Dissect matched strings with the URL constructor afterward; extraction and normalization are parser work, not this pattern's.
A URL regex earns its keep as a shape gate: an http or https scheme, then anything that is not whitespace. The pragmatic form reads schemes in either case and rejects embedded spaces; the strict variant further demands a dotted, letter-TLD host in canonical lowercase. Thirteen vectors chart where the two variants split.
Examples
^https?:\/\/[^\s/$.?#][^\s]*$pattern /^https?:\/\/[^\s/$.?#][^\s]*$/i
https://sub.example.co.uk/a/b?q=1#frag ✓ matches
https://example.com:8443/health ✓ matches
example.com ✗ no match
ftp://example.com ✗ no matchPorts, deep paths, queries and fragments all ride inside the not-whitespace tail; both refusals are scheme problems — none at all, and the wrong one.
^https?:\/\/(?:[A-Za-z0-9]+(?:-+[A-Za-z0-9]+)*\.)+[A-Za-z]{2,}(?::\d{1,5})?(?:[/?#][^\s]*)?$pragmatic /^https?:\/\/[^\s/$.?#][^\s]*$/i
strict /^https?:\/\/(?:[A-Za-z0-9]+(?:-+[A-Za-z0-9]+)*\.)+[A-Za-z]{2,}(?::\d{1,5})?(?:[/?#][^\s]*)?$/
http://localhost:3000 pragmatic ✓ matches strict ✗ no match
HTTPS://EXAMPLE.COM/PATH pragmatic ✓ matches strict ✗ no match
http://example.com pragmatic ✓ matches strict ✓ matchesDevelopment habits split the variants twice: localhost has no dotted TLD, and an uppercase scheme is not canonical — strict refuses both strings the shape gate waves through.
Every example on this page is executed against the same engine that powers the tool, on every build.
Gotchas worth knowing
Matching is not parsing
This gate answers one question — does the string look like a web link — and never checks that the host resolves or the path exists. The URL constructor in every modern runtime decomposes and normalizes far better than capture groups would.
Common errors
Invalid regular expression: /^https?:\/\/[a-Z0-9.-]+$/: Range out of order in character class- Ranges run by code point, and lowercase a (97) sits above uppercase Z (90), so [a-Z] is backwards. Write [a-zA-Z] as two ranges — or keep the i flag this page's pragmatic variant already carries.
FAQ
Why does ftp://example.com fail this URL regex?
The alternation names http and https only, on purpose — most link fields should not accept arbitrary protocols. Widen it deliberately — (https?|ftp) — when your intake genuinely speaks more schemes.
Which variant belongs on a comment form?
Usually strict: submitted links aimed at localhost, raw IPs or user:pass@ hosts are at best mistakes, at worst probes. Pragmatic fits internal tooling where dev URLs are legitimate.
Can this pattern extract every link from a page of text?
Anchored, no — it validates one field. Dropping ^ and $ and adding g in the tester makes a crude extractor, though trailing punctuation clings to matches; trim what it finds.