To clarify, I’m looking for a decent regular expression to validate URLs that were entered as user input with. I have no interest in parsing a list of URLs from a given string of text (even though some of the regexes on this page are capable of doing that). I also don’t want to allow every possible technically valid URL — quite the opposite. See the URL Standard if you’re looking to parse URLs in the same way that browsers do.
Assume that this regex will be used for a public URL shortener written in PHP, so URLs like http://localhost/
, //foo.bar/
, ://foo.bar/
, data:text/plain;charset=utf-8,OHAI
and tel:+1234567890
shouldn’t pass (even though they’re technically valid). Also, in this case I only want to allow the HTTP, HTTPS and FTP protocols.
Also, single weird leading and/or trailing characters aren’t tested for. Just imagine you’re doing this before testing $url
with the regex:
$url = trim($url, '!"#$%&\'()*+,-./@:;<=>[\\]^_`{|}~');
Note that I’ve added the S
modifier to all the regexes to speed up the tests. In real-world usage, this modifier can be omitted.
Here’s a plain text list of all the URLs used in the test.
Diego Perini posted his version as a gist.
URL | Spoon Library | @krijnhoetmer | @gruber | @gruber v2 | @cowboy | Jeffrey Friedl | @mattfarina | @stephenhay | @scottgonzales | @rodneyrehm | @imme_emosol | @diegoperini | Using filter_var() |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
These URLs should match (1 → correct) |
|||||||||||||
http://foo.com/blah_blah
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
|
http://foo.com/blah_blah/
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
|
http://foo.com/blah_blah_(wikipedia)
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 0
| 1
| 1
| 1
|
http://foo.com/blah_blah_(wikipedia)_(again)
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 0
| 1
| 1
| 1
|
http://www.example.com/wpstyle/?p=364
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
|
https://www.example.com/foo/?bar=baz&inga=42&quux
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
|
http://✪df.ws/123
| 0
| 0
| 1
| 1
| 1
| 1
| 0
| 1
| 1
| 1
| 1
| 1
| 0
|
http://userid:password@example.com:8080
| 0
| 1
| 1
| 1
| 1
| 0
| 1
| 1
| 1
| 1
| 1
| 1
| 1
|
http://userid:password@example.com:8080/
| 0
| 1
| 1
| 1
| 1
| 0
| 1
| 1
| 1
| 1
| 1
| 1
| 1
|
http://userid@example.com
| 0
| 1
| 1
| 1
| 1
| 0
| 1
| 1
| 1
| 1
| 1
| 1
| 1
|
http://userid@example.com/
| 0
| 1
| 1
| 1
| 1
| 0
| 1
| 1
| 1
| 1
| 1
| 1
| 1
|
http://userid@example.com:8080
| 0
| 1
| 1
| 1
| 1
| 0
| 1
| 1
| 1
| 1
| 1
| 1
| 1
|
http://userid@example.com:8080/
| 0
| 1
| 1
| 1
| 1
| 0
| 1
| 1
| 1
| 1
| 1
| 1
| 1
|
http://userid:password@example.com
| 0
| 1
| 1
| 1
| 1
| 0
| 1
| 1
| 1
| 1
| 1
| 1
| 1
|
http://userid:password@example.com/
| 0
| 1
| 1
| 1
| 1
| 0
| 1
| 1
| 1
| 1
| 1
| 1
| 1
|
http://142.42.1.1/
| 0
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
|
http://142.42.1.1:8080/
| 0
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
|
http://➡.ws/䨹
| 0
| 0
| 1
| 1
| 1
| 0
| 0
| 1
| 1
| 0
| 1
| 1
| 0
|
http://⌘.ws
| 0
| 0
| 1
| 1
| 1
| 0
| 0
| 1
| 1
| 1
| 1
| 1
| 0
|
http://⌘.ws/
| 0
| 0
| 1
| 1
| 1
| 0
| 0
| 1
| 1
| 1
| 1
| 1
| 0
|
http://foo.com/blah_(wikipedia)#cite-1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
|
http://foo.com/blah_(wikipedia)_blah#cite-1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
|
http://foo.com/unicode_(✪)_in_parens
| 1
| 1
| 1
| 1
| 1
| 1
| 0
| 1
| 1
| 1
| 1
| 1
| 0
|
http://foo.com/(something)?after=parens
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
|
http://☺.damowmow.com/
| 0
| 1
| 1
| 1
| 1
| 0
| 0
| 1
| 1
| 1
| 1
| 1
| 0
|
http://code.google.com/events/#&product=browser
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
|
http://j.mp
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
|
ftp://foo.bar/baz
| 0
| 0
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
|
http://foo.bar/?q=Test%20URL-encoded%20stuff
| 0
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
|
http://مثال.إختبار
| 0
| 0
| 1
| 1
| 1
| 0
| 0
| 1
| 1
| 0
| 1
| 1
| 0
|
http://例子.测试
| 0
| 0
| 1
| 1
| 1
| 0
| 0
| 1
| 1
| 0
| 1
| 1
| 0
|
http://उदाहरण.परीक्षा
| 0
| 0
| 1
| 1
| 1
| 0
| 0
| 1
| 1
| 0
| 1
| 1
| 0
|
http://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com
| 0
| 1
| 0
| 1
| 1
| 0
| 0
| 1
| 1
| 1
| 1
| 1
| 1
|
http://1337.net
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
|
http://a.b-c.de
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 0
| 1
| 1
|
http://223.255.255.254
| 0
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
|
https://foo_bar.example.com/
| 1
| 1
| 1
| 1
| 1
| 1
| 0
| 1
| 1
| 1
| 1
| 1
| 0
|
These URLs should fail (0 → correct) |
|||||||||||||
http://
| 0
| 0
| 0
| 0
| 0
| 0
| 0
| 0
| 1
| 0
| 0
| 0
| 0
|
http://.
| 0
| 0
| 0
| 0
| 1
| 0
| 1
| 0
| 1
| 0
| 0
| 0
| 0
|
http://..
| 0
| 0
| 0
| 0
| 1
| 0
| 1
| 0
| 1
| 0
| 0
| 0
| 0
|
http://../
| 0
| 1
| 1
| 1
| 1
| 0
| 1
| 0
| 1
| 1
| 0
| 0
| 0
|
http://?
| 0
| 0
| 0
| 0
| 1
| 0
| 0
| 0
| 1
| 0
| 0
| 0
| 0
|
http://??
| 0
| 0
| 0
| 0
| 1
| 0
| 0
| 0
| 1
| 0
| 0
| 0
| 0
|
http://??/
| 0
| 1
| 1
| 1
| 1
| 0
| 0
| 0
| 1
| 1
| 0
| 0
| 0
|
http://#
| 0
| 0
| 0
| 1
| 1
| 0
| 0
| 0
| 1
| 1
| 0
| 0
| 0
|
http://##
| 0
| 1
| 0
| 1
| 1
| 0
| 0
| 0
| 1
| 1
| 0
| 0
| 0
|
http://##/
| 0
| 1
| 1
| 1
| 1
| 0
| 0
| 0
| 1
| 1
| 0
| 0
| 0
|
http://foo.bar?q=Spaces should be encoded
| 0
| 1
| 1
| 1
| 1
| 1
| 0
| 0
| 1
| 1
| 0
| 0
| 0
|
//
| 0
| 0
| 0
| 0
| 0
| 0
| 0
| 0
| 0
| 0
| 0
| 0
| 0
|
//a
| 0
| 0
| 0
| 0
| 0
| 0
| 0
| 0
| 0
| 0
| 0
| 0
| 0
|
///a
| 0
| 0
| 0
| 0
| 0
| 0
| 0
| 0
| 0
| 0
| 0
| 0
| 0
|
///
| 0
| 0
| 0
| 0
| 0
| 0
| 0
| 0
| 0
| 0
| 0
| 0
| 0
|
http:///a
| 0
| 1
| 1
| 1
| 1
| 0
| 0
| 0
| 1
| 1
| 0
| 0
| 0
|
foo.com
| 0
| 0
| 0
| 0
| 1
| 0
| 0
| 0
| 0
| 0
| 0
| 0
| 0
|
rdar://1234
| 0
| 0
| 1
| 1
| 1
| 0
| 1
| 0
| 1
| 0
| 0
| 0
| 1
|
h://test
| 0
| 0
| 1
| 0
| 1
| 0
| 1
| 0
| 1
| 0
| 0
| 0
| 1
|
http:// shouldfail.com
| 0
| 0
| 0
| 0
| 1
| 0
| 0
| 0
| 1
| 1
| 0
| 0
| 0
|
:// should fail
| 0
| 0
| 0
| 0
| 0
| 0
| 0
| 0
| 0
| 0
| 0
| 0
| 0
|
http://foo.bar/foo(bar)baz quux
| 0
| 1
| 1
| 1
| 1
| 1
| 0
| 0
| 1
| 1
| 0
| 0
| 0
|
ftps://foo.bar/
| 0
| 0
| 1
| 1
| 1
| 0
| 1
| 0
| 1
| 0
| 0
| 0
| 1
|
http://-error-.invalid/
| 0
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 0
| 0
| 0
|
http://a.b--c.de/
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 0
| 1
| 1
|
http://-a.b.co
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 0
| 0
| 0
|
http://a.b-.co
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 0
| 0
| 0
|
http://0.0.0.0
| 0
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 0
| 1
|
http://10.1.1.0
| 0
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 0
| 1
|
http://10.1.1.255
| 0
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 0
| 1
|
http://224.1.1.1
| 0
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 0
| 1
|
http://1.1.1.1.1
| 0
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 0
| 1
|
http://123.123.123
| 0
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 0
| 1
|
http://3628126748
| 0
| 1
| 1
| 1
| 1
| 0
| 1
| 1
| 1
| 1
| 1
| 0
| 1
|
http://.www.foo.bar/
| 0
| 1
| 1
| 1
| 1
| 0
| 1
| 0
| 1
| 1
| 0
| 0
| 0
|
http://www.foo.bar./
| 0
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
|
http://.www.foo.bar./
| 0
| 1
| 1
| 1
| 1
| 0
| 1
| 0
| 1
| 1
| 0
| 0
| 0
|
http://10.1.1.1
| 0
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 0
| 1
|
http://10.1.1.254
| 0
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| 0
| 1
|
/(((http|ftp|https):\/{2})+(([0-9a-z_-]+\.)+(aero|asia|biz|cat|com|coop|edu|gov|info|int|jobs|mil|mobi|museum|name|net|org|pro|tel|travel|ac|ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|cr|cu|cv|cx|cy|cz|cz|de|dj|dk|dm|do|dz|ec|ee|eg|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|io|iq|ir|is|it|je|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mk|ml|mn|mn|mo|mp|mr|ms|mt|mu|mv|mw|mx|my|mz|na|nc|ne|nf|ng|ni|nl|no|np|nr|nu|nz|nom|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|ps|pt|pw|py|qa|re|ra|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sj|sk|sl|sm|sn|so|sr|st|su|sv|sy|sz|tc|td|tf|tg|th|tj|tk|tl|tm|tn|to|tp|tr|tt|tv|tw|tz|ua|ug|uk|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|yu|za|zm|zw|arpa)(:[0-9]+)?((\/([~0-9a-zA-Z\#\+\%@\.\/_-]+))?(\?[0-9a-zA-Z\+\%@\/&\[\];=_-]+)?)?))\b/imuS
_(^|[\s.:;?\-\]<\(])(https?://[-\w;/?:@&=+$\|\_.!~*\|'()\[\]%#,☺]+[\w/#](\(\))?)(?=$|[\s',\|\(\).:;?\-\[\]>\)])_i
#\b(([\w-]+://?|www[.])[^\s()<>]+(?:\([\w\d]+\)|([^[:punct:]\s]|/)))#iS
#(?i)\b((?:[a-z][\w-]+:(?:/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))#iS
~(?:\b[a-z\d.-]+://[^<>\s]+|\b(?:(?:(?:[^\s!@#$%^&*()_=+[\]{}\|;:'",.<>/?]+)\.)+(?:ac|ad|aero|ae|af|ag|ai|al|am|an|ao|aq|arpa|ar|asia|as|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|biz|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|cat|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|coop|com|co|cr|cu|cv|cx|cy|cz|de|dj|dk|dm|do|dz|ec|edu|ee|eg|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|gm|gn|gov|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|info|int|in|io|iq|ir|is|it|je|jm|jobs|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mil|mk|ml|mm|mn|mobi|mo|mp|mq|mr|ms|mt|museum|mu|mv|mw|mx|my|mz|name|na|nc|net|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|org|pa|pe|pf|pg|ph|pk|pl|pm|pn|pro|pr|ps|pt|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|st|su|sv|sy|sz|tc|td|tel|tf|tg|th|tj|tk|tl|tm|tn|to|tp|travel|tr|tt|tv|tw|tz|ua|ug|uk|um|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|xn--0zwm56d|xn--11b5bs3a9aj6g|xn--80akhbyknj4f|xn--9t4b11yi5a|xn--deba0ad|xn--g6w251d|xn--hgbk6aj7f53bba|xn--hlcj6aya9esc7a|xn--jxalpdlp|xn--kgbechtv|xn--zckzah|ye|yt|yu|za|zm|zw)|(?:(?:[0-9]|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])\.){3}(?:[0-9]|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]))(?:[;/][^#?<>\s]*)?(?:\?[^#<>\s]*)?(?:#[^<>\s]*)?(?!\w))~iS
@\b((ftp|https?)://[-\w]+(\.\w[-\w]*)+|(?:[a-z0-9](?:[-a-z0-9]*[a-z0-9])?\.)+(?: com\b|edu\b|biz\b|gov\b|in(?:t|fo)\b|mil\b|net\b|org\b|[a-z][a-z]\b))(\:\d+)?(/[^.!,?;"'<>()\[\]{}\s\x7F-\xFF]*(?:[.!,?]+[^.!,?;"'<>()\[\]{}\s\x7F-\xFF]+)*)?@iS
/^([a-z][a-z0-9\*\-\.]*):\/\/(?:(?:(?:[\w\.\-\+!$&'\(\)*\+,;=]|%[0-9a-f]{2})+:)*(?:[\w\.\-\+%!$&'\(\)*\+,;=]|%[0-9a-f]{2})+@)?(?:(?:[a-z0-9\-\.]|%[0-9a-f]{2})+|(?:\[(?:[0-9a-f]{0,4}:)*(?:[0-9a-f]{0,4})\]))(?::[0-9]+)?(?:[\/|\?](?:[\w#!:\.\?\+=&@!$'~*,;\/\(\)\[\]\-]|%[0-9a-f]{2})*)?$/xiS
@^(https?|ftp)://[^\s/$.?#].[^\s]*$@iS
#([a-z]([a-z]|\d|\+|-|\.)*):(\/\/(((([a-z]|\d|-|\.|_|~|[\x00A0-\xD7FF\xF900-\xFDCF\xFDF0-\xFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?((\[(|(v[\da-f]{1,}\.(([a-z]|\d|-|\.|_|~)|[!\$&'\(\)\*\+,;=]|:)+))\])|((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|(([a-z]|\d|-|\.|_|~|[\x00A0-\xD7FF\xF900-\xFDCF\xFDF0-\xFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=])*)(:\d*)?)(\/(([a-z]|\d|-|\.|_|~|[\x00A0-\xD7FF\xF900-\xFDCF\xFDF0-\xFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*|(\/((([a-z]|\d|-|\.|_|~|[\x00A0-\xD7FF\xF900-\xFDCF\xFDF0-\xFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\x00A0-\xD7FF\xF900-\xFDCF\xFDF0-\xFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)|((([a-z]|\d|-|\.|_|~|[\x00A0-\xD7FF\xF900-\xFDCF\xFDF0-\xFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\x00A0-\xD7FF\xF900-\xFDCF\xFDF0-\xFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)|((([a-z]|\d|-|\.|_|~|[\x00A0-\xD7FF\xF900-\xFDCF\xFDF0-\xFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)){0})(\?((([a-z]|\d|-|\.|_|~|[\x00A0-\xD7FF\xF900-\xFDCF\xFDF0-\xFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\xE000-\xF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\x00A0-\xD7FF\xF900-\xFDCF\xFDF0-\xFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?#iS
#((https?://|ftp://|www\.|[^\s:=]+@www\.).*?[a-z_\/0-9\-\#=&])(?=(\.|,|;|\?|\!)?("|'|«|»|\[|\s|\r|\n|$))#iS
@(https?|ftp)://(-\.)?([^\s/?\.#-]+\.?)+(/[^\s]*)?$@iS
%^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z0-9\x{00a1}-\x{ffff}][a-z0-9\x{00a1}-\x{ffff}_-]{0,62})?[a-z0-9\x{00a1}-\x{ffff}]\.)+(?:[a-z\x{00a1}-\x{ffff}]{2,}\.?))(?::\d{2,5})?(?:[/?#]\S*)?$%iuS
— Mathias