Regex para URLs: qué reconocer y qué dejar a un parser
Encontrar enlaces en un texto y validar una URL son trabajos distintos. Un patrón para cada uno, las trampas que dejan pasar URLs malas (puntuación final, puntos sin escapar, anclas que faltan) y cuándo pasarle el trabajo a new URL() o a urllib.

· 8 min de lectura · Actualizado el
/^https?://\S+$/
atrapa
- https://a.io
- http://x.com/p
- https://b.dev?q=1
deja libre
- ftp://x.com
- www.x.com
- http:/x.com
“Regex de URL” suele significar una de dos cosas:
- Encontrar URLs dentro de un bloque de texto, como una app de chat que vuelve clicables los enlaces.
- Validar una entrada concreta, como el campo “Sitio web” de un formulario de perfil.
Necesitan patrones distintos, y el segundo es sobre todo trabajo para un parser de URLs. Este artículo cubre los dos, además de las trampas en las que cae la gente en cada caso. Todos los ejemplos son interactivos, así que edita los patrones y mira qué cambia.
/^ (https?) :// ([^/:?#\s]+) (:\d+)? (/[^?#\s]*)? (\?[^#\s]*)? (#\S*)?$/iUna URL http o https con un host y, después, puerto, ruta, consulta y fragmento opcionales, sin espacios.
Acepta
- https://example.com
- http://localhost:3000/api?x=1
- https://example.com/blog#top
Rechaza
- ftp://example.com
- www.example.com
- https://exa mple.com
01Encontrar URLs en un texto
La versión rápida es un esquema seguido de cualquier cosa que no sea un espacio:
/https?://\S+/Debe coincidir3/3
- Docs: https://regexhunter.com/guide.
- (see https://example.com/a)
- <https://example.com>
No debe2/2
- see www.example.com
- ftp://files.example.com
\S+ se lo lleva todo hasta el siguiente espacio, y en un texto real una URL suele ir seguida de puntuación. Enlaza eso y quien lo lea se encontrará un 404.
02Quitar la puntuación final
La solución es decir cuál puede ser el último carácter. El cuerpo puede ser cualquier cosa salvo espacios, corchetes angulares y comillas. El carácter final, además, no puede ser un signo de puntuación habitual:
/https?://[^\s<>"]*[^\s<>".,;:!?)]/- Docs: https://regexhunter.com/guide.
- (see https://example.com/a)
- <https://example.com>
- https://example.com/search?q=regex&page=2
Funciona porque el codicioso [^\s<>"]* llega hasta el final de la URL y luego retrocede de carácter en carácter hasta que encaja la clase final. (Nuestro artículo sobre cuantificadores codiciosos y perezosos explica ese retroceso paso a paso.)
Aquí no hay una regla perfecta. Los enlaces de Wikipedia como https://en.wikipedia.org/wiki/Regex_(disambiguation) terminan de verdad en ), y este patrón lo recorta. Las bibliotecas que convierten texto en enlaces lo resuelven comprobando si los paréntesis están equilibrados, que es más lógica de la que debería cargar una sola regex.
Fíjate en que www.example.com no se encuentra, porque no tiene esquema. Si también quieres los enlaces que empiezan por www. a secas, empieza el patrón con (https?://|www\.).
03Validar una URL: las partes
Una URL web tiene hasta seis partes. Aquí están en https://regexhunter.com:443/blog/regex-urls?ref=home#top:
| Parte | Ejemplo | ¿Obligatoria? |
|---|---|---|
| esquema | https |
sí |
| host | regexhunter.com |
sí |
| puerto | :443 |
no |
| ruta | /blog/regex-urls |
no |
| consulta | ?ref=home |
no |
| fragmento | #top |
no |
Un patrón que comprueba la forma de cada parte queda así:
/^(https?)://([^/:?#\s]+)(:\d+)?(/[^?#\s]*)?(\?[^#\s]*)?(#\S*)?$/iDebe coincidir4/4
- https://example.com
- HTTPS://EXAMPLE.COM
- http://localhost:3000/api?x=1
- https://regexhunter.com/blog/regex-urls#top
No debe5/5
- ftp://example.com
- https://
- https://exa mple.com
- https://example.com:port
- example.com
Léelo parte por parte:
^(https?)://el esquema, http o https, y el://([^/:?#\s]+)el host: cualquier cosa hasta el siguiente/,:,?o#(:\d+)?un puerto opcional, solo dígitos(/[^?#\s]*)?una ruta opcional, que empieza por/(\?[^#\s]*)?una consulta opcional, que empieza por?(#\S*)?$un fragmento opcional y luego el final
Es una buena comprobación de forma. Pero no dice nada sobre si el host tiene sentido. https://999.999.999.999 pasa, y https://- también. Podrías seguir añadiendo reglas para nombres de host, direcciones IP, corchetes de IPv6, usuarios y contraseñas, y dominios internacionales. Hay quien lo ha hecho, y el resultado ocupa cientos de caracteres.
04Deja que un parser haga el parseo
Todos los lenguajes habituales traen un parser de URLs que ya conoce esas reglas. En JavaScript (navegadores y Node), el constructor URL lanza una excepción con cualquier cosa que no pueda analizar:
function isHttpUrl(s) {
try {
const u = new URL(s);
return u.protocol === 'http:' || u.protocol === 'https:';
} catch {
return false;
}
}
new URL("https://exa mple.com"), new URL("https://999.999.999.999") y new URL("example.com") lanzan una excepción. La comprobación del protocolo importa: new URL("javascript:alert(1)") se analiza sin problema, y eso no lo quieres en un enlace.
Además obtienes las piezas gratis: u.hostname, u.port, u.pathname, u.search, u.hash y u.searchParams.get("x") para un valor de la consulta. Y la URL vuelve normalizada: HTTPS://Example.COM:443/a/../b se convierte en https://example.com/b.
En Python, urllib.parse.urlsplit divide una URL en las mismas partes:
from urllib.parse import urlsplit
u = urlsplit("http://localhost:3000/api?x=1#top")
(u.scheme, u.hostname, u.port, u.path, u.query, u.fragment)
# ('http', 'localhost', 3000, '/api', 'x=1', 'top')
Pero urlsplit divide, no valida. Casi nunca lanza un error. urlsplit("example.com") devuelve un esquema y un host vacíos con example.com como ruta, y urlsplit("https://exa mple.com") te dice sin inmutarse que el host es exa mple.com. Así que comprueba u.scheme in ("http", "https") y que u.netloc no esté vacío, y añade tus propias reglas para el host si las necesitas.
05Trampa: el punto sin escapar
En un patrón, . significa “cualquier carácter”. Olvida la barra invertida en un nombre de dominio y habrás admitido dominios que se le parecen:
/^https://www.example.com/- https://www.example.com
- https://wwwxexample.com
Escribe www\.example\.com. Cada punto literal de un patrón necesita su barra invertida.
06Trampa: el final que falta
Esta importa en las comprobaciones de redirecciones y listas de permitidos, del tipo “solo devolver a la gente a URLs de nuestro propio sitio”:
/^https://example\.com/- https://example.com/login
- https://example.com.evil.io
El host tiene que acabar justo después de .com. Exige uno de los caracteres que pueden cerrarlo, o el final de la cadena:
/^https://example\.com([/?#:]|$)/Debe coincidir3/3
- https://example.com
- https://example.com/login
- https://example.com?ref=mail
No debe3/3
- https://example.com.evil.io
- https://example.company.io
- https://evil.io/?next=https://example.com
Mejor aún para comprobaciones de seguridad: analiza con new URL() o urlsplit y luego compara el nombre de host con === o ==. Una comparación exacta de cadenas no tiene casos límite que olvidar.
07Pruébalo
Todos los monstruos son direcciones web. Los inocentes usan el esquema equivocado, un :// roto, una errata o ningún esquema:
Atrapa las direcciones http y https y deja libre el resto.
Objetivos · 0/4 atrapados
- https://regexhunter.comobjetivo
- http://localhost:3000objetivo
- https://a.io/x?y=1objetivo
- http://example.org/objetivo
Inocentes · 0 errores
- ftp://example.orginocente
- https:/broken.cominocente
- example.cominocente
- mailto:ana@site.cominocente
- htps://typo.ioinocente
Escribe una regex: el tablero se actualiza mientras escribes.
08Lista de comprobación
- Para encontrar URLs en un texto:
https?://[^\s<>"]*[^\s<>".,;:!?)], así la puntuación final se queda fuera. - Para validar una URL: usa
new URL()en JavaScript y comprueba el protocolo, ourlspliten Python y comprueba el esquema y el host. - Escapa cada punto literal:
example\.com. - En las listas de permitidos, ancla el principio y el final del host o, mejor, compara nombres de host ya analizados.
- Añade el modificador
isi pueden aparecer mayúsculas.HTTPS://EXAMPLE.COMes una URL válida.
09Sigue practicando
- La ruta Validadores del mundo real de la Academia incluye slugs de URL, correos electrónicos, direcciones IP y más.
- La sección de escapado de la guía cubre las reglas de la barra invertida en las que se basa la trampa del punto.
- Prueba la caza de hoy o repasa tableros anteriores en el archivo.