go 56 lines · 7 steps

Parsing user-agent strings in Go

A small package extracts browser, version, and OS from a raw user-agent string using ordered regex and substring checks.

Explained by highlit
1package useragent
2 
3import (
4 "regexp"
5 "strings"
6)
7 
8type Parsed struct {
9 Browser string
10 BrowserVersion string
11 OS string
12 Mobile bool
13}
14 
15var (
16 edgeRe = regexp.MustCompile(`Edg(?:e|A|iOS)?/([\d.]+)`)
17 operaRe = regexp.MustCompile(`OPR/([\d.]+)`)
18 chromeRe = regexp.MustCompile(`Chrome/([\d.]+)`)
19 firefoxRe = regexp.MustCompile(`Firefox/([\d.]+)`)
20 safariRe = regexp.MustCompile(`Version/([\d.]+).*Safari`)
21)
22 
23func Parse(ua string) Parsed {
24 p := Parsed{Browser: "Unknown", OS: "Unknown"}
25 p.Mobile = strings.Contains(ua, "Mobi") || strings.Contains(ua, "Android")
26 
27 switch {
28 case edgeRe.MatchString(ua):
29 p.Browser, p.BrowserVersion = "Edge", edgeRe.FindStringSubmatch(ua)[1]
30 case operaRe.MatchString(ua):
31 p.Browser, p.BrowserVersion = "Opera", operaRe.FindStringSubmatch(ua)[1]
32 case chromeRe.MatchString(ua):
33 p.Browser, p.BrowserVersion = "Chrome", chromeRe.FindStringSubmatch(ua)[1]
34 case firefoxRe.MatchString(ua):
35 p.Browser, p.BrowserVersion = "Firefox", firefoxRe.FindStringSubmatch(ua)[1]
36 case safariRe.MatchString(ua):
37 p.Browser, p.BrowserVersion = "Safari", safariRe.FindStringSubmatch(ua)[1]
38 }
39 
40 switch {
41 case strings.Contains(ua, "Windows NT 10.0"):
42 p.OS = "Windows 10"
43 case strings.Contains(ua, "Windows NT"):
44 p.OS = "Windows"
45 case strings.Contains(ua, "iPhone"), strings.Contains(ua, "iPad"):
46 p.OS = "iOS"
47 case strings.Contains(ua, "Mac OS X"):
48 p.OS = "macOS"
49 case strings.Contains(ua, "Android"):
50 p.OS = "Android"
51 case strings.Contains(ua, "Linux"):
52 p.OS = "Linux"
53 }
54 
55 return p
56}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Ordering matters when patterns overlap — check the most specific signature before the generic one it contains.
  2. 2Precompiling regexes once at package scope avoids recompiling on every call.
  3. 3A safe default plus a fall-through switch keeps unknown inputs from producing empty or invalid results.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Parsing user-agent strings in Go — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code