Use strings.Contains when you need a simple yes or no: does the UTF-8 string s hold the literal substring substr? The standard library does not ship a case-insensitive variant or regex mode on this helper; you combine Contains with other strings functions or with regexp when the job grows. For splitting and fields, see string split in Go.
I used
go teston Go 1.22+ (Linux) while writing this so I could sanity-check case sensitivity, the empty-substring rule, the match-any and match-all helpers, and theContainsversusContainsAnycontrast on the same haystack.
Check If a String Contains a Substring in Go
strings.Contains(s, substr string) bool reports whether substr appears anywhere in s as a contiguous UTF-8 substring. The name reads naturally as “string contains substring”; the arguments are always haystack first, then needle.
Basic strings.Contains example
package main
import (
"fmt"
"strings"
)
func main() {
fmt.Println(strings.Contains("Hello, World!", "World")) // true
fmt.Println(strings.Contains("Hello, World!", "Mars")) // false
}strings.Contains is case-sensitive
Matching respects Unicode code points and exact casing: go and Go are different substrings.
package main
import (
"fmt"
"strings"
)
func main() {
fmt.Println(strings.Contains("Golang Tutorial", "go")) // false
fmt.Println(strings.Contains("Golang Tutorial", "Go")) // true
}Case-Insensitive String Contains in Go
There is no strings.ContainsIgnoreCase. The usual pattern is to normalize both sides the same way (often lowercasing) and then call Contains.
Use strings.ToLower before strings.Contains
package main
import (
"fmt"
"strings"
)
func main() {
text := "Golang Tutorial"
substr := "go"
found := strings.Contains(
strings.ToLower(text),
strings.ToLower(substr),
)
fmt.Println(found) // true
}Unicode note for case-insensitive matching
strings.ToLower and strings.ToUpper apply simple Unicode case mapping. That is enough for many ASCII-heavy logs and configs, but it is not a full locale-aware collation. For comparing two whole strings for case-insensitive equality, strings.EqualFold is often better than allocating two lowered strings. For “does this long text contain this substring ignoring case?” there is still no single standard helper; libraries or manual normalization are used when simple lowering is not enough.
Check for Multiple Substrings
Match any substring from a list
Stop as soon as one keyword matches (for example log triage).
package main
import (
"fmt"
"strings"
)
func main() {
line := "request failed: timeout waiting for peer"
keywords := []string{"error", "failed", "timeout"}
anyMatch := false
for _, k := range keywords {
if strings.Contains(line, k) {
anyMatch = true
break
}
}
fmt.Println(anyMatch) // true
}Match all required substrings
Every needle must appear (for example a URL sanity check).
package main
import (
"fmt"
"strings"
)
func main() {
url := "https://golinuxcloud.com/docs"
required := []string{"https", "golinuxcloud.com"}
all := true
for _, r := range required {
if !strings.Contains(url, r) {
all = false
break
}
}
fmt.Println(all) // true
}Related String Matching Functions
These functions sit next to Contains in the strings package; pick the smallest tool that answers your question.
| Function | Use when |
|---|---|
strings.Contains |
You only need true or false for a literal substring |
strings.Index |
You need the byte offset of the first occurrence (-1 if missing) |
strings.ContainsAny |
You need to know whether any rune from a set appears in s |
strings.ContainsRune |
You need to detect one specific Unicode code point |
strings.HasPrefix |
You care only about how s starts |
strings.HasSuffix |
You care only about how s ends |
strings.ContainsAny reports whether any Unicode code point from the chars argument appears anywhere in s; it does not search for a multi-character phrase unless that phrase literally appears as a substring. strings.ContainsRune is the specialized single-rune form.
strings.Contains vs strings.ContainsAny
Contains("Golang", "go") is false because the contiguous substring go does not occur. ContainsAny("Golang", "go") is true because both g and o occur somewhere in the string.
strings.Contains vs strings.ContainsRune
Use ContainsRune when the needle is a single rune literal (for example strings.ContainsRune(s, '✓')). Contains(s, "✓") works for one-character UTF-8 strings but ContainsRune avoids building a substring value.
strings.Contains vs strings.Index
Contains is a thin boolean wrapper: it is equivalent to strings.Index(s, substr) >= 0. Use Index when you need the position for slicing or error messages.
strings.Contains vs strings.HasPrefix and strings.HasSuffix
HasPrefix and HasSuffix test only the start or end boundary. Contains scans the whole string. For file extensions, HasSuffix(s, ".go") is clearer than Contains on a bare ".go" (which could match the middle of a path accidentally depending on your data).
package main
import (
"fmt"
"strings"
)
func main() {
s := "https://example.com/path"
fmt.Println(strings.HasPrefix(s, "https://"))
fmt.Println(strings.HasSuffix("main.go", ".go"))
}Common String Contains Use Cases
Short patterns you can copy into CLIs, HTTP handlers, and scripts.
Validate user input
email := "user@example.com"
if !strings.Contains(email, "@") {
// reject: missing at-sign
}Check file paths, URLs, and extensions
u := "https://golinuxcloud.com/page"
path := "/var/log/app/server.log"
file := "handler.go"
_ = strings.Contains(u, "https://")
_ = strings.Contains(path, "/log/")
_ = strings.HasSuffix(file, ".go") // often clearer than Contains for extensionsFilter logs or command output
line := "2026/01/02 15:04:05 ERROR connection reset"
if strings.Contains(line, "ERROR") {
// escalate or count
}
out := "... install failed: disk full ..."
if strings.Contains(out, "failed") {
// surface failure to user
}Blocked-word style checks often lower once, then loop needles:
msg := strings.ToLower("You won a prize - spam guaranteed")
for _, w := range []string{"spam", "scam"} {
if strings.Contains(msg, w) {
// reject
break
}
}Other one-liners in the same style: checking a path segment with Contains on a cleaned path (mind separator rules for production path logic—often filepath is better than raw substring checks).
Common Mistakes with strings.Contains
Empty substring returns true
By definition the empty string is a substring of every string. The package doc uses strings.Contains("seafood", "") and strings.Contains("", "") as examples; both are true. Treat an empty substr as “always matches” when designing validation.
Contains does not mean exact match
Contains only tells you that the needle appears somewhere inside the haystack. For equality, use s == t or strings.EqualFold when appropriate.
strings.Contains is not regex
Metacharacters like .* are literal characters, not a pattern. For regex, use regexp.MatchString or compile a regexp.Regexp once and reuse it.
Case sensitivity (again)
If a check “should” ignore case, lowering both sides is common; if you forget, Contains silently returns false for otherwise matching text.
ContainsAny checks characters, not words
Passing ContainsAny(s, "go") asks whether g or o appears anywhere, not whether the word go appears as a substring.
Prefer HasPrefix or HasSuffix for starts or ends
Using Contains for “starts with https” can match unintended middle positions on odd inputs; HasPrefix states the intent directly.
Go String Contains Cheat Sheet
Which function should you use?
| Goal | Use | Example |
|---|---|---|
| Check substring | strings.Contains() |
strings.Contains(s, "go") |
| Check any character from a set | strings.ContainsAny() |
strings.ContainsAny(s, "aeiou") |
| Check one Unicode character | strings.ContainsRune() |
strings.ContainsRune(s, '✓') |
| Find substring position | strings.Index() |
strings.Index(s, "go") |
| Check prefix | strings.HasPrefix() |
strings.HasPrefix(s, "https://") |
| Check suffix | strings.HasSuffix() |
strings.HasSuffix(s, ".go") |
| Case-insensitive contains | strings.Contains(strings.ToLower(s), strings.ToLower(substr)) |
Matches Go, go, GO for ASCII-like text |
| Regex match | regexp.MatchString() |
Pattern-based search |

