Skip to the tool
URLExtractor
Code and automation

How to Extract URLs with Regex

Patterns that work, measured failures from the ones that do not, and a clear account of why no regular expression can fully validate a URL.

By URL Extractor EditorialPublished

A regular expression is a good way to find URLs and a poor way to validate them. This guide shows both halves honestly: a pattern that works well enough, and measured evidence of where the popular ones break.

The pattern

regex
https?://[^\s<>"'`]+

That is it. It finds a scheme and then runs until it hits whitespace or a character a URL written in prose cannot contain.

It is intentionally simple because the interesting work happens afterwards. Trying to encode "where does this URL end" into the pattern itself is the mistake.

What the popular patterns actually do

These are real outputs, produced by running each pattern against the same inputs in Python 3.11.

Measured behaviour of four common patterns
Input`https?://\S+``https?://[\w.-]+`Desired
Read https://example.com/pricing.`…/pricing.` — keeps the full stop`https://example.com` — loses the path`…/pricing`
See https://en.example.org/wiki/Foo_(bar) now`…/Foo_(bar)` ✓`https://en.example.org` — loses the path`…/Foo_(bar)`
(https://example.com/terms)`…/terms)` — keeps the bracket`https://example.com``…/terms`
[docs](https://example.com/docs)`…/docs)` — keeps the bracket`https://example.com``…/docs`
https://example.com/s?q=a+b&sort=Desc#Topwhole address ✓`https://example.com` — loses query and fragmentwhole address
https://example.com/a,https://example.com/bboth merged into one matchtwo matches, both truncatedtwo complete addresses

Two clear lessons.

\S+ keeps too much. It is right about where the address starts and wrong about where it ends, every time punctuation follows.

[\w.-]+ keeps far too little. It stops at the first /, so you get bare hostnames and lose every path, query and fragment. This pattern is widespread in answers online, and in most of them it is quietly producing the wrong result.

The fix is not a bigger pattern

The natural next move is to bolt trailing-punctuation handling into the regex:

regex
https?://[^\s<>"'`]+?(?=[.,;:!?]*(?:\s|$))

That handles a trailing full stop. It still cannot handle brackets, because deciding whether a closing bracket belongs to the URL requires counting how many openers came before it — and counting is precisely what regular expressions cannot do. It is not a limitation of any particular engine; it is what "regular" means.

So: match generously, then post-process.

The post-processing step, in Python
python
TRAILING = '.,;:!?\'"'
PAIRS = {')': '(', ']': '[', '}': '{'}

def trim(candidate: str) -> str:
    while candidate:
        last = candidate[-1]
        if last in TRAILING:
            candidate = candidate[:-1]
            continue
        if last in PAIRS and candidate.count(last) > candidate.count(PAIRS[last]):
            candidate = candidate[:-1]
            continue
        break
    return candidate

Run against Python 3.11.15. Keeps `…/Foo_(bar)` intact while stripping the bracket from `(…/terms)` — the two cases no single pattern gets right.

The same idea in other languages

javascript
// JavaScript
const CANDIDATE = /https?:\/\/[^\s<>"'`]+/gi;
const found = text.match(CANDIDATE) ?? [];
plain
# Google Sheets (RE2 — no lookbehind, no backreferences)
=REGEXEXTRACT(A2, "https?://[^\s,;""'<>)\]]+")
shell
# grep
grep -Eo 'https?://[^[:space:]<>"'"'"']+' notes.txt

Validation is a different job

Finding something URL-shaped is not the same as knowing it is a usable address. After matching, hand the candidate to a real parser:

  • Pythonurllib.parse.urlparse
  • JavaScriptnew URL(value), which throws on invalid input
  • Gonet/url.Parse
  • Javajava.net.URI

The parser tells you the scheme, the host and whether the whole thing is coherent. It is also what keeps javascript: and data: out of a list you are about to render as links.

And no parser tells you the address works. That needs a request.

Frequently asked questions

Is there one correct URL regex?

No. There are patterns derived from RFC 3986 that match the grammar, and they are enormous, unreadable, and still cannot tell you where an address ends inside a sentence. For finding URLs in text, a simple pattern plus post-processing beats a complicated one.

Should I use regex on HTML?

No. Use a parser. An HTML parser handles attribute quoting, entities and malformed markup correctly; a regular expression handles the first two badly and the third not at all.

How do I match bare domains?

You cannot do it reliably with a pattern alone, because a pattern cannot know which suffixes are real. `main.py` and `shop.io` are indistinguishable by shape, and `.py` and `.io` are both real TLDs. Match the shape, then check it against the Public Suffix List.

Why does my pattern miss internationalised domains?

Because `\w` is ASCII-only in most engines by default. Either add a Unicode range, or match generously on "not whitespace" as above and let the parser handle the rest.

Sources

URL Extractor from TextThis exact approach, already implemented and tested.

Tools used in this guide

Related guides

Share this pageWhatsAppXLinkedInFacebook