Check if a string contains a substring in Go (strings.Contains)

strings.Contains for literal UTF-8 substrings, case sensitivity and ToLower, match any or all needles, ContainsAny and ContainsRune, Index and HasPrefix or HasSuffix, empty substring behavior, and regexp only when you need patterns.

Published

Updated

Read time 6 min read

Reviewed byDeepak Prasad

Check if a string contains a substring in Go (strings.Contains)

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 test on 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 the Contains versus ContainsAny contrast 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

go
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.

go
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

go
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).

go
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).

go
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
}

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).

go
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

go
email := "user@example.com"
if !strings.Contains(email, "@") {
	// reject: missing at-sign
}

Check file paths, URLs, and extensions

go
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 extensions

Filter logs or command output

go
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:

go
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

References


Frequently Asked Questions

1. Does strings.Contains use regex?

No. It searches for a literal substring. Use the regexp package for patterns.

2. Is strings.Contains case-sensitive?

Yes. Normalize with ToLower or ToUpper on both sides for a simple ASCII-heavy case-insensitive contains, or use EqualFold when comparing whole strings.

3. What does strings.Contains(s, "") return?

Always true. The empty string is a substring of every string.

4. How is Contains different from ContainsAny and ContainsRune?

Contains looks for a contiguous substr. ContainsAny checks if any rune from the chars string appears in s. ContainsRune checks for one specific rune.

5. How do I get the index instead of a bool?

Use strings.Index; it returns the byte offset of the first occurrence or -1 if not found.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive …