Overview

The Safari browser user agent (UA) is the identification string Safari sends with HTTP requests. It also appears via navigator.userAgent in JavaScript. In modern Safari, the string is intentionally reduced. OS details are masked, and many tokens are frozen to protect privacy and reduce fingerprinting.

As MDN notes, a User-Agent header is free-form and not guaranteed to be stable over time. Indiscriminate UA sniffing often breaks. See MDN’s User-Agent header for a primer.

Two facts matter today. First, Safari does not support User-Agent Client Hints. Google’s documentation confirms Client Hints are a Chromium feature and not available in Safari (see Chromium’s Client Hints overview).

Second, recent Safari releases reduce or mask OS details and keep some tokens static for compatibility. Apple documents changes in the Safari Release Notes. This guide covers UA patterns, safe server-side heuristics, analytics segmentation, and Applebot verification so you avoid common misidentification traps.

Safari user-agent fundamentals

Safari’s UA string is a product list with a few reliable anchors and several advisory or frozen tokens. You will always see AppleWebKit to indicate the engine. You will also see a Version/x.y token and a Safari/x token.

On iOS you’ll see a Mobile/… token. On iPadOS desktop mode the UA looks intentionally “Mac-like.” The Version/x correlates to Safari’s major version family. Safari/x and AppleWebKit/x are largely frozen and not consistent indicators of the true engine build.

For example, macOS Safari typically appears as: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Safari/605.1.15. Notice the OS token reports “Intel” and “10_15_7” even on Apple silicon and newer macOS releases. This is a deliberate compatibility reduction.

On iPhone, Safari looks like: Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Mobile/15E148 Safari/604.1. The Mobile/… build tag is not the iOS version. The Safari/604.1 tail is not a real build identifier.

navigator.userAgent generally mirrors the HTTP header. Extensions, in‑app browsers, and some proxies can add or alter tokens. Treat Version/ and the presence of Safari/ plus AppleWebKit as your minimal “Safari-class” signal. Avoid relying on OS tokens beyond coarse bucketing.

Private Browsing does not change Safari’s UA. Testing shows it remains the same. Don’t try to infer privacy mode from the UA. Many tokens (OS, CPU architecture) are spoofable or reduced and should not drive critical logic.

Latest Safari user-agent examples by platform

Safari’s UA strings differ by platform and mode, but they keep consistent anchor tokens. Use these examples as references. Validate on real devices when precision matters.

macOS examples

Typical macOS Safari presents “Macintosh; Intel Mac OS X 10_15_7” regardless of actual hardware or OS. It also includes a Version/x correlating with the Safari major series. A common pattern is: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Safari/605.1.15.

Even on Apple silicon, expect the “Intel” and “10_15_7” tokens. The AppleWebKit/605.1.15 and Safari/605.1.15 numbers are largely frozen. They don’t reveal the real WebKit build.

On newer releases the Version/ token changes (for example, Version/18.0). The rest often remains stable. You can’t derive the exact macOS version from the UA anymore.

At best, bucket traffic as “Safari on desktop-class WebKit.” Confirm your patterns with your own logs and a known-good machine after each update (see Safari Release Notes).

iPhone (iOS) examples

iPhone Safari includes explicit iPhone and Mobile tokens. It also shows a Version/x that matches the Safari family tied to the OS major version. A typical UA is: Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Mobile/15E148 Safari/604.1.

Three useful anchors are the presence of “iPhone,” the “Mobile/…” tag, and “Version/… Safari/…”. The OS portion (“iPhone OS 17_5”) may omit minor or patch information as Apple continues reduction.

The Safari/604.1 token remains largely static across iOS generations. Version/x is the better indicator for high-level grouping. Use these tokens for conservative logic, such as ensuring touch-friendly UI, rather than strict version gating.

iPadOS examples

In mobile mode, iPad Safari looks similar to iPhone but shows “iPad” and retains Mobile/…: Mozilla/5.0 (iPad; CPU OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Mobile/15E148 Safari/604.1. This is a good indicator you’re talking to the iPad browser in a mobile profile.

Many iPadOS browsers and in‑app views inherit this structure because WebKit is the platform engine. When you need to differentiate Safari from a third‑party shell, look for additional vendor tokens (e.g., “CriOS” for Chrome on iOS, “FxiOS” for Firefox on iOS, “EdgiOS” for Edge). The presence of Version/ is a Safari hallmark.

Desktop mode on iPadOS

When users choose Request Desktop Site on iPadOS, Safari shifts to a Mac-like UA. It drops the Mobile token and uses the “Macintosh; Intel Mac OS X 10_15_7” pattern: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Safari/605.1.15.

This makes an iPad indistinguishable from macOS Safari in server logs. The behavior is by design to improve desktop compatibility. It is described in Apple’s help for Request Desktop Site on iPad.

Server-side detection of iPadOS desktop mode is inherently lossy because the UA converges with macOS. If you must differentiate, combine UA with client-side hints (touch support, screen metrics). Treat the result as best-effort.

How Safari differs from Chrome and Firefox on macOS

Chrome and Firefox UAs on macOS also include the word “Safari.” Many naive regexes get tripped up by that. Chrome reports “Chrome/xx” and ends with “Safari/537.36.” Firefox is Gecko-based and ends with “Firefox/xx” without AppleWebKit.

Real Safari on macOS uniquely contains a Version/x token. It sits alongside AppleWebKit and a Safari/x token. It never includes Chrome/Chromium/Edg/OPR.

A conservative separation strategy is to require all three Safari anchors—AppleWebKit, Version/x, and Safari/x. Explicitly exclude Chromium and Gecko tokens. In practice, the presence of Version/x prevents matching Chromium’s Safari/… tail.

Always log and review mismatches. Enterprise proxies and automation can still spoof tokens.

As a quick checklist:

Safari versus third‑party browsers on iOS and iPadOS

All browsers on iOS and iPadOS use WebKit. Every UA looks “Safari-like” at first glance. Third-party browsers add their own tokens—Chrome on iOS uses “CriOS/xx,” Firefox uses “FxiOS/xx,” and Edge uses “EdgiOS/xx.”

They still include AppleWebKit and often end with a Safari/… tail for compatibility. This reality makes simple “contains Safari” checks nearly useless on iOS.

A practical heuristic for “Safari the app” versus “another WebKit shell” is to require Version/x and the absence of third‑party tokens. For example, treat iOS Safari as present if your UA contains Version/x, contains Mobile/ (in mobile mode), and does not contain CriOS|FxiOS|EdgiOS|DuckDuckGo|OPiOS|YaApp|GSA.

This will correctly classify most sessions. It is still spoofable and will misclassify some in‑app browsers that don’t add unique tokens.

When accuracy matters, prefer capability checks and UI fallbacks over branching logic based solely on the UA. For analytics, keep both a strict “Safari app” segment (Version/x, no third‑party tokens) and a broader “iOS WebKit” segment. This reflects the engine reality and minimizes false negatives.

Safari vs WKWebView vs SFSafariViewController

On iOS, you’ll encounter Safari proper, WKWebView embedded inside apps, and SFSafariViewController as a secure in‑app browser. WKWebView and SFSafariViewController are both WebKit-based and documented by Apple as the supported way to embed web content (WKWebView and SFSafariViewController).

Their user agents closely resemble Safari’s and often reuse the same Version/ and Mobile/ tokens. In practice, SFSafariViewController’s UA is nearly indistinguishable from Safari’s. It uses Safari’s rendering environment with added privacy boundaries.

WKWebView UAs can be the same as Safari unless the host app modifies them via setCustomUserAgent. Many apps also append app identifiers (“FBAN/…”, “FBAV/…”, “Instagram”, “Twitter”, “GSA”).

Servers can sometimes detect prominent in‑app browsers via those extra tokens. They cannot reliably separate Safari from a plain WKWebView.

Treat all three as “Safari-class WebKit” for rendering purposes. Use explicit app tokens only for analytics bucketing. Avoid feature gating based on perceived container.

If you must detect an in‑app browser, look for well-known tokens (e.g., FBAN|FBAV for Facebook, Instagram, Line, WeChat). Confirm behavior on devices.

Stable tokens versus spoofable tokens in Safari’s UA

Because Safari deliberately reduces and freezes parts of the UA, some tokens are relatively stable while others are unreliable or spoofable. Knowing which is which keeps your detection logic conservative.

Stable enough to trust for coarse detection:

Spoofable, reduced, or unstable:

Bottom line: build detection around presence/absence patterns and explicit third‑party tokens, not around numeric versions or OS hints. Use logs to tune exceptions and keep a fallback path for unknown-but-Safari-class traffic.

Feature detection versus UA sniffing

Prefer feature detection over UA sniffing whenever possible. UA strings are reduced and spoofable. They make a brittle foundation for behavior changes.

MDN emphasizes that User-Agent is an informational header and not a capability declaration. Safari’s lack of User-Agent Client Hints means you won’t get richer structured metadata as you might in Chromium (see Chromium’s Client Hints overview).

For example, if you’re working around a Safari scrollbar quirk, check for the CSS or JS behavior directly. Do that instead of testing “is Safari.” If you need to detect touch + hover support combinations on iPadOS desktop mode, query CSS media features or Pointer Events.

When dealing with IndexedDB or Service Worker differences, test for the API and a minimal behavior. Then degrade gracefully.

Reserve UA heuristics for analytics segmentation, conditional polyfill downloads, or server-side content negotiation where feature tests aren’t available. Even then, keep them conservative, log exceptions, and bias toward shipping a safe baseline when in doubt.

For automation, emulate UAs thoughtfully and verify results on real devices. Tools like Playwright user agent emulation are useful for test scaffolding. They are not substitutes for real Safari.

Server-side Safari detection patterns with regex

Server-side detection should minimize false positives. Require Version/… with Safari/… and exclude Chromium/Gecko identifiers. Treat these as guardrails, not guarantees. Instrument logs to capture unexpected matches.

Core regex heuristics

A conservative “Safari-class” matcher (desktop and iOS, but excluding Chromium shells) can be expressed as: (?i)(?=.*\bAppleWebKit\b)(?=.*\bVersion/\d)(?=.*\bSafari/\d)(?!.*\bChrome|Chromium|CriOS|Edg|OPR|Vivaldi|Brave\b).

This checks for the three Safari anchors and rejects common Chromium tokens.

For “Safari app on iOS” specifically, favor: (?i)(?=.*\b(iPhone|iPad|iPod)\b)(?=.*\bVersion/\d)(?=.*\bMobile/)(?=.*\bSafari/)(?!.*\bCriOS|FxiOS|EdgiOS|DuckDuckGo|OPiOS|GSA\b).

This pattern requires the Mobile token and excludes well-known third‑party iOS shells. It catches most native Safari sessions without sweeping in Chrome or Firefox on iOS.

Language-specific snippets

Node.js example: const isSafari = /(?i)(?=.*\bAppleWebKit\b)(?=.*\bVersion\/\d)(?=.*\bSafari\/\d)(?!.*\bChrome|Chromium|CriOS|Edg|OPR|Vivaldi|Brave\b)/i.test(ua);

Python example: is_safari = bool(re.search(r'(?i)(?=.*\bAppleWebKit\b)(?=.*\bVersion/\d)(?=.*\bSafari/\d)(?!.*\bChrome|Chromium|CriOS|Edg|OPR|Vivaldi|Brave\b)', ua))

Go example: isSafari := regexp.MustCompile((?i)(?=.\bAppleWebKit\b)(?=.\bVersion/\d)(?=.\bSafari/\d)(?!.\bChrome|Chromium|CriOS|Edg|OPR|Vivaldi|Brave\b)).MatchString(ua)

Java example: boolean isSafari = Pattern.compile("(?i)(?=.*\\bAppleWebKit\\b)(?=.*\\bVersion/\\d)(?=.*\\bSafari/\\d)(?!.*\\bChrome|Chromium|CriOS|Edg|OPR|Vivaldi|Brave\\b)").matcher(ua).find();

These snippets are intentionally strict to avoid false positives. They may undercount Safari in some enterprise or in‑app scenarios. Adjust only after reviewing misclassified samples from real traffic.

Validation steps

Before deploying any UA matcher to production, validate with a short, repeatable workflow:

Analytics segmentation and validation

For analytics, segment conservatively and label segments clearly. A BigQuery-style predicate to bucket “Safari-class (any platform)” can be: REGEXP_CONTAINS(user_agent, r'(?i)(?=.*\bAppleWebKit\b)(?=.*\bVersion/\d)(?=.*\bSafari/\d)(?!.*\bChrome|Chromium|CriOS|Edg|OPR|Vivaldi|Brave\b)').

To split mobile iOS Safari, use: REGEXP_CONTAINS(user_agent, r'(?i)(?=.*\b(iPhone|iPad|iPod)\b)(?=.*\bVersion/\d)(?=.*\bMobile/)(?=.*\bSafari/)(?!.*\bCriOS|FxiOS|EdgiOS|DuckDuckGo|OPiOS|GSA\b)').

You can derive a “probable iPadOS desktop mode” slice by looking for “Macintosh; Intel Mac OS X 10_15_7” with Version/… Safari/… and the absence of Mobile/. Treat it as heuristic rather than definitive.

Validate segments against controlled test runs from known devices and a client-side beacon that reports screen class (e.g., touch support and viewport). This helps you spot over- or under-counting.

Keep an “iOS WebKit (any shell)” segment as well—AppleWebKit plus iPhone|iPad|iPod and Safari/. This reveals the engine footprint independent of the app container. It gives you resilience if a third‑party shell changes branding tokens without notice.

Applebot user-agents and verification

Apple operates Applebot for crawling and uses UA strings that can resemble Safari with an extra Applebot token. You should not block Applebot inadvertently. You should verify that traffic claiming to be Applebot actually belongs to Apple.

Recognizing Applebot

You’ll see Applebot in forms like: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15 (Applebot/0.1; +http://www.apple.com/go/applebot). The key difference from human Safari is the explicit Applebot/x product token appended at the end.

There are also variants such as Applebot-Extended with similar structure. If a UA lacks Applebot but behaves like a crawler, do not assume it’s Apple. UA strings are trivial to spoof.

Use the DNS verification steps recommended by Apple in About Applebot.

Reverse DNS and IP checks

To verify Applebot:

Follow Apple’s guidance in About Applebot. When in doubt, throttle rather than block. Provide a crawl contact in robots.txt to reduce accidental impact on legitimate crawling.

Troubleshooting common false positives and negatives

Misclassifications happen when analytics uses naive regex. They also occur when CDNs append or rewrite headers, or when in‑app browsers adopt minimal branding.

The most common false positive is counting Chrome on macOS as Safari due to the “Safari/537.36” tail. The most common false negative is missing iPadOS desktop mode because it looks like macOS Safari in logs.

A secondary source of confusion is mismatches between navigator.userAgent and the HTTP header. Extensions or custom WKWebView user agents are often the cause.

Start by aligning on a single, conservative definition for “Safari-class” and “Safari app on iOS.” Instrument your pipeline to capture raw UA samples for anything that fails your regex but claims to be Safari from client-side signals.

If your CDN is terminating TLS and reissuing headers, compare edge logs against origin logs. Ensure the UA survives intact. Some security middleboxes normalize or truncate headers under certain conditions.

A practical validation checklist:

Finally, remember the policy and ethics angle: UA spoofing is appropriate in test labs but risky in production. If you emulate Safari in automation, annotate requests and avoid polluting analytics. Tools that let you configure a header, like Playwright user agent emulation, are helpful for controlled tests. They should never be used to masquerade traffic in the wild.