| Tested on | RHEL 10.2 with Go 1.26.5 |
|---|---|
| Package | go 1.26.5 (strings standard library) |
| Applies to | Any host with Go installed |
| Privilege | Normal user |
| Scope | Check whether a Go string contains a substring with strings.Contains, case-insensitive patterns, multiple needles, ContainsAny, ContainsRune, ContainsFunc, and when to use Index, HasPrefix, HasSuffix, or regexp. Does not cover full regex or Unicode normalization guides. |
| Related guides | String split in Go String interpolation in Go Golang cast to string Getting started with Go Golang if else |
strings.Contains("golang tutorial", "golang")returns true. A missing substring returns false:
strings.Contains("golang tutorial", "Go") // false — case-sensitivestrings.Contains checks for a literal substring and returns a boolean. Import strings from the standard library; no third-party package is required.
Use strings.Contains to check for a substring
strings.Contains(s, substr string) bool reports whether substr appears anywhere in s as a contiguous substring. The haystack comes first, then the needle.
package main
import (
"fmt"
"strings"
)
func main() {
text := "request failed: connection reset"
fmt.Println(strings.Contains(text, "failed")) // true
fmt.Println(strings.Contains(text, "error")) // false
}In application code you usually branch on the result:
if strings.Contains(text, "error") {
// handle failure path
}Matching is case-sensitive, searches anywhere in the string, and is literal substring matching—not regex. Metacharacters such as . and * are ordinary characters inside Contains.
Case-insensitive substring checks
There is no strings.ContainsIgnoreCase. For straightforward ASCII-style text, normalize both sides the same way:
found := strings.Contains(
strings.ToLower(text),
strings.ToLower(substr),
)Lowercasing both strings works for many simple checks, but full Unicode or language-aware case matching can require more specialized handling.
strings.EqualFold compares two complete strings for case-insensitive equality. It does not search for a substring inside a longer string—use Contains with normalization when you need “does this text include this phrase, ignoring case?”
Check for multiple substrings
Any substring
Stop when one needle matches—useful when any error keyword in a log line should trigger alerting:
func containsAny(haystack string, needles []string) bool {
for _, n := range needles {
if strings.Contains(haystack, n) {
return true
}
}
return false
}
// containsAny("disk full on /var", []string{"error", "failed", "full"}) → trueAll substrings
Return false when any required piece is missing—useful when every token must appear:
func containsAll(haystack string, needles []string) bool {
for _, n := range needles {
if !strings.Contains(haystack, n) {
return false
}
}
return true
}
// containsAll("level=error service=api", []string{"level=error", "service="}) → trueContainsAny, ContainsRune, and ContainsFunc
| Function | Checks |
|---|---|
strings.Contains |
A literal substring |
strings.ContainsAny |
Any rune from another string |
strings.ContainsRune |
One Unicode code point |
strings.ContainsFunc |
Whether any rune satisfies a predicate |
ContainsAny does not mean “does the string contain any of these words?” It asks whether any single character from the second argument appears in the haystack:
strings.ContainsAny("cloud", "xyzdo") // true — 'o' is in "cloud"For a whole word, use Contains or loop explicit substrings.
ContainsRune is the specialized single-rune form—handy when the needle is a rune literal instead of a one-character string:
strings.ContainsRune("hello", 'l') // true
strings.Contains("hello", "l") // also true for ASCIIContainsFunc, added in Go 1.21, runs a predicate on each rune:
import "unicode"
strings.ContainsFunc("abc123", unicode.IsDigit) // trueUse it when you need “contains a digit,” “contains whitespace,” or another rune-level test without building a custom loop. For a fixed substring, Contains remains the clearest choice.
When to use Index, HasPrefix, HasSuffix, or regex
| Need | Use |
|---|---|
| Boolean substring check | strings.Contains |
| Position of substring | strings.Index |
| Starts with text | strings.HasPrefix |
| Ends with text | strings.HasSuffix |
| Pattern or regular expression | regexp |
| Any rune matches predicate | strings.ContainsFunc |
strings.Contains is equivalent to strings.Index(s, substr) >= 0. Use Index when you need the offset for slicing or error messages.
Index returns the byte index of the first match, not a rune position. That matters when multi-byte characters precede the match:
strings.Index("日本go", "go") // 6 (byte offset); rune position 2HasPrefix and HasSuffix test only the start or end boundary:
strings.HasPrefix("https://example.com", "https://") // true
strings.HasSuffix("main.go", ".go") // trueFor file extensions, prefer HasSuffix or filepath.Ext instead of Contains, which can match ".go" in the middle of a path. For URL structure, use net/url parsing rather than substring checks alone.
Common mistakes and edge cases
Empty substring. strings.Contains("hello", "") returns true. The empty string is a substring of every string. Do not use an empty needle as a guard unless you intend that behavior.
Case sensitivity. Contains is case-sensitive; see the case-insensitive section above when go and Go should match.
Not validation. strings.Contains(line, "ERROR") is a simple presence check in a log line—not proof of a well-formed email, URL, or file type:
line := "2026/08/23 12:00:00 ERROR connection reset by peer"
if strings.Contains(line, "ERROR") {
// count or escalate — not the same as parsing structured logs
}strings.Contains(email, "@") only shows that @ appears; it does not validate an email address.
ContainsAny is not word search. ContainsAny(s, "go") asks whether g or o appears anywhere, not whether the word go appears as a substring.
Not regex. Contains treats .* as literal characters. Use regexp for patterns.
Summary
Use strings.Contains(haystack, needle) when you need a fast boolean answer for a literal substring. It is the right default for “is this token present anywhere in this text?”—log filtering, simple guards, and CLI output checks.
Normalize with ToLower for simple case-insensitive checks, loop needles for any-or-all matching, and reach for ContainsAny, ContainsRune, or ContainsFunc only when the question is about individual runes—not whole words. Remember that ContainsAny("cloud", "xyzdo") is true because o appears, not because do is a substring.
When you need a position, boundary, or pattern, switch to Index, HasPrefix, HasSuffix, or regexp instead of stretching Contains beyond its job. For splitting fields after you locate text, see string split in Go.

