How to check if a URL is valid

Solution 1:

Notice:

As pointed by @CGuess, there's a bug with this issue and it's been documented for over 9 years now that validation is not the purpose of this regular expression (see https://bugs.ruby-lang.org/issues/6520).




Use the URI module distributed with Ruby:

require 'uri'

if url =~ URI::regexp
    # Correct URL
end

Like Alexander Günther said in the comments, it checks if a string contains a URL.

To check if the string is a URL, use:

url =~ /\A#{URI::regexp}\z/

If you only want to check for web URLs (http or https), use this:

url =~ /\A#{URI::regexp(['http', 'https'])}\z/

Solution 2:

Similar to the answers above, I find using this regex to be slightly more accurate:

URI::DEFAULT_PARSER.regexp[:ABS_URI]

That will invalidate URLs with spaces, as opposed to URI.regexp which allows spaces for some reason.

I have recently found a shortcut that is provided for the different URI rgexps. You can access any of URI::DEFAULT_PARSER.regexp.keys directly from URI::#{key}.

For example, the :ABS_URI regexp can be accessed from URI::ABS_URI.

Solution 3:

The problem with the current answers is that a URI is not an URL.

A URI can be further classified as a locator, a name, or both. The term "Uniform Resource Locator" (URL) refers to the subset of URIs that, in addition to identifying a resource, provide a means of locating the resource by describing its primary access mechanism (e.g., its network "location").

Since URLs are a subset of URIs, it is clear that matching specifically for URIs will successfully match undesired values. For example, URNs:

 "urn:isbn:0451450523" =~ URI::regexp
 => 0 

That being said, as far as I know, Ruby doesn't have a default way to parse URLs , so you'll most likely need a gem to do so. If you need to match URLs specifically in HTTP or HTTPS format, you could do something like this:

uri = URI.parse(my_possible_url)
if uri.kind_of?(URI::HTTP) or uri.kind_of?(URI::HTTPS)
  # do your stuff
end

Solution 4:

I prefer the Addressable gem. I have found that it handles URLs more intelligently.

require 'addressable/uri'

SCHEMES = %w(http https)

def valid_url?(url)
  parsed = Addressable::URI.parse(url) or return false
  SCHEMES.include?(parsed.scheme)
rescue Addressable::URI::InvalidURIError
  false
end