rubyguides

String#end_with?

str.end_with?(*suffixes) -> true or false

The end_with? method tests whether a string ends with a specified suffix. It takes one or more suffix arguments and returns true if the string terminates with any of them, otherwise false. This method is useful for validating file extensions, checking URL domains, and routing logic.

The match is exact and case-sensitive. There is no built-in option for case-insensitive matching — you must convert the string and suffix to the same case yourself if needed.

That makes the method easy to reason about because the match rule stays simple. When the suffix matters, end_with? gives you a direct yes-or-no answer without extra parsing or regular expressions.

Syntax

str.end_with?(*suffixes)

The splat operator allows multiple suffixes. The method returns true if the string ends with any of the provided suffixes.

That is handy when a value can end in more than one acceptable extension. The call stays short even when the whitelist is a little longer, which keeps the matching rule visible at the call site.

Parameters

ParameterTypeDefaultDescription
*suffixesStringOne or more suffix strings to check. Pass each as a separate argument.

Examples

Basic usage

"ruby".end_with?("y")
# => true

"ruby".end_with?("r")
# => false

"ruby".end_with?("by")
# => true

These tiny checks are the easiest way to show what the method does. The suffix is either present or it is not, so the return value stays obvious and there is no need to inspect a larger pattern.

That simplicity is what makes the method practical for file names and URLs. When you only care about the ending, a suffix check is clearer than a regular expression and easier to explain to the next person reading the code.

It also keeps the logic local to the comparison. The code says what ending it expects, and that is usually enough for filenames, route guards, or a quick validation step.

Multiple suffixes

"report.pdf".end_with?("pdf", "doc", "txt")
# => true

"image.png".end_with?("jpg", "gif", "svg")
# => false

"data.csv".end_with?("csv", "xlsx")
# => true

Passing several suffixes is useful when the code can accept more than one extension or file type. The method stops as soon as one match succeeds, which keeps the call compact.

The method is especially useful when the acceptable endings share the same meaning, such as several image extensions or a few document formats. In those cases, the suffix list reads like a small policy instead of a nested conditional.

That pattern keeps the accepted endings in one visible list. It is easier to scan than a chain of separate comparisons, and it is simpler to update when a new file type becomes valid.

Case sensitivity

"Ruby".end_with?("ruby")
# => false

"Ruby".end_with?("Ruby")
# => true

# Case-insensitive version:
"Ruby".downcase.end_with?("ruby")
# => true

Case-sensitive matching is often what you want for file names and extension checks, because it matches the actual characters on disk or in the input string. If the source data is messy, normalize the case before calling end_with?.

That small normalization step keeps the rule in one place. Instead of repeating case conversion across the codebase, you can convert once and then reuse the same suffix logic everywhere else.

Empty suffix

"hello".end_with?("")
# => true

"".end_with?("")
# => true

An empty suffix always matches because every string ends with an empty string.

That edge case matters mostly when suffix values are generated dynamically. If the list can ever be empty, it is worth checking that condition before you rely on the result.

No arguments

"hello".end_with?()
# => true

Called with no arguments, end_with? returns true (vacuous truth — every string ends with nothing).

That behavior can be surprising the first time you see it, but it fits the method contract. An empty check does not rule anything out, so the answer stays true.

If a no-argument call would be a mistake in your code, add a guard before the method call. That way the intent stays obvious and the surprising truthy result never escapes into the rest of the program.

Common patterns

File extension checking

def pdf_file?(filename)
  filename.end_with?(".pdf")
end

def image_file?(filename)
  filename.end_with?(".png", ".jpg", ".gif", ".webp")
end

pdf_file?("document.pdf")    # => true
pdf_file?("document.PDF")    # => false (case-sensitive)
image_file?("photo.jpg")      # => true

This pattern is easy to reuse because the suffix list reads like a whitelist. When the accepted endings are fixed, the method keeps the policy in one short line instead of hiding it inside a more complex check.

That pattern also fits upload filters and route guards, where the accepted endings are already known. The suffix check stays obvious, and the surrounding code does not need extra branching.

URL and domain validation

def secure_url?(url)
  url.end_with?("https://")
end

def asset_path?(path)
  path.end_with?(".js", ".css", ".svg", ".png")
end

def markdown_file?(filename)
  filename.end_with?(".md", ".markdown")
end

Path and extension checks often live next to routing or file dispatch code. Keeping them as suffix checks makes the rule easy to read and easy to update when a new file type needs to be supported.

Once the extension list is visible, the handler can focus on the matching value instead of checking the ending again.

Conditional routing

case filename
when ->(f) { f.end_with?(".rb") }
  run_ruby_file(filename)
when ->(f) { f.end_with?(".py") }
  run_python_file(filename)
when ->(f) { f.end_with?(".js") }
  run_javascript_file(filename)
else
  unknown_file_handler(filename)
end

The lambda form is useful when the dispatch logic needs to stay compact. Each branch still says exactly what ending it accepts, so the rule remains readable even without a separate helper method.

That is a good fit when a file dispatcher or router already knows the valid endings up front. The suffix test stays local to the branch, and the code stays easy to skim.

checking against an array of suffixes

If you have an array of suffixes, you must splat it when passing to end_with?:

extensions = [".pdf", ".doc", ".txt"]

"report.pdf".end_with?(extensions)
# => false (passes the array as a single argument, which won't match)

"report.pdf".end_with?(*extensions)
# => true (splats the array into separate arguments)

Without the splat, Ruby compares the string against the array object itself, not its elements.

This is a small syntax detail, but it matters whenever suffixes are stored in a variable. The splat keeps the call easy to read while still letting the method inspect each suffix on its own.

When the suffix list comes from configuration or another helper, the splat is usually the cleanest way to pass it along. It preserves the meaning of each suffix instead of turning the array into one unmatched object.

That is the main reason the example is worth remembering. A stored list of endings should still behave like a list of endings, not as one opaque value that never matches.

If the suffix list is assembled in one place and checked in another, the splat keeps the handoff natural. The caller still passes individual suffixes, and the method can keep comparing one ending at a time.

gotchas

Regexp is not supported

Unlike start_with?, end_with? does not accept a Regexp argument. Passing a Regexp will raise an ArgumentError or match against the object’s string representation:

"ruby123".end_with?(/\d+/)
# => false (attempts to match against the Regexp object's to_s)

# Use gsub or match instead for regex suffix matching:
"ruby123".match?(/\d+\z/)
# => true

If you need a pattern at the end of the value, a regular expression is still available, just through match? or another regex helper instead of end_with?. That keeps the suffix check and the pattern check in their own lanes.

Order of suffixes does not matter

"ruby".end_with?("y", "by") behaves the same as "ruby".end_with?("by", "y") — either match returns true.

Case sensitivity is strict

There is no end_with?(:ignore_case) option. Always normalize case if you need case-insensitive matching.

If you are dealing with user input, that normalization step is usually the best place to handle it. The method then stays focused on the suffix check itself instead of trying to guess how the string should be interpreted.

That separation keeps the API predictable. Once the case is normalized, the suffix check is doing one simple job instead of carrying extra assumptions about the input source.

errors

Passing nil as a suffix raises a NoMethodError:

"hello".end_with?(nil)
# => NoMethodError: undefined method `end_with?' for nil:NilClass

Always ensure suffix values are strings before calling end_with?.

See Also