Hybrid App integration
A Hybrid App uses both native URL requests and in-app WebViews to talk to your servers. HUMAN must treat those as one visitor: the native SDK VID and the WebView VID must be the same.
How do I know if my app is a hybrid app
Your app is a hybrid app if both of the following are true:
- It loads your website (or a HUMAN-protected page on it) in an in-app WebView.
- That site is protected by HUMAN (Bot Defender or Account Defender).
You do not need to call the app “hybrid” in product language. These topologies all count:
- Login, signup, account, billing, or help pages shown in a WebView.
- OAuth or SSO pages on a login sub-domain of the same registrable root (for example
login.example.comwhen the root isexample.com). - A Cordova (or similar) app whose UI is a whole-app WebView of a protected site.
- Cross-platform WebViews:
react-native-webview, Expo WebView, Flutterwebview_flutter, Ionic/Capacitor.
A WebView that only loads an unprotected static page (for example, a privacy policy that is not behind HUMAN) does not need hybrid configuration. However, if that same WebView later loads a protected login or account flow, treat it as hybrid.
Which WebViews are supported
Use WKWebView or Android WebView with setupWebView (or iOS automaticSetup). Hybrid is also supported for wrapper-hosted WebViews when you configure them as described in the WebView container support matrix.
SFSafariViewController and Chrome Custom Tabs need a special integration. Contact HUMAN; do not assume default hybrid setup will sync identity. Those containers own a cookie jar the app cannot write with the default Hybrid App path.
Domain configuration
Set hybridAppPolicy.webRootDomains to the bare registrable root of the site in the WebView.
Canonical form: example.com
- No leading dot is preferred and should be used whenever possible. However, a leading dot is accepted and equivalent, which means
.example.comis stored and matched the same asexample.com. If your older samples use.example.com, there’s no need to correct them. - No scheme (
https://) - No
www - No path or port
This value is used to decide which WebView hosts get hybrid support and as the Domain when the SDK sets _pxmd and _pxwvm. webRootDomains = ["example.com"] is sufficient for the apex and every sub-domain of that root.
How matching works
A WebView host matches a configured domain when the host equals that value, or when the host ends with a dot plus that value. That is not a free-text suffix test: notexample.com does not match example.com.
Most common misconfiguration
Listing a sub-domain (api.example.com, login.example.com) instead of the registrable root is the most common failure.
It fails silently. Instrumentation still matches that host, so the integration looks healthy, but cookies are scoped to the sub-domain. Other hosts on the same site never share VID with native. Configured sub-domains are kept as-is; they are not reduced to the root.
A leading www. is not this failure: www.example.com is stripped to example.com (the same as a leading dot). Prefer the bare root example.com in new code.
Do not copy the dotted cookie name from browser DevTools into webRootDomains as a requirement. DevTools often shows .example.com; configure example.com.
Remote domain configuration
HUMAN can push hybrid root domains for your App ID from the Portal. The SDK unions those domains with the list in policy. A domain correction does not require an app release.
Still set webRootDomains in the app when you can. Contact HUMAN if you need a server-side domain update.
Cookie Domain versus test harnesses
A real Set-Cookie: Domain=example.com header is not host-only: it is readable on the apex and on sub-domains.
Some test-harness APIs that take a bare domain string — for example Playwright addCookies({ domain: 'example.com' }) — can create a host-only cookie instead. That can make a lab test pass (or fail) in a way production never would. Treat the HTTP Set-Cookie header as the source of truth, not the harness helper.
Multiple App IDs and registrable domains
If the WebView loads a different registrable domain (not a sub-domain of the first root), list that root under the App ID that protects it.
OAuth on login.example.com does not need a second entry if example.com is already configured.
Enable Hybrid App support
Integrate the native SDK first:
Pass the WebView instance to the SDK:
- iOS:
HumanSecurity.setupWebView(webView:navigationDelegate:) - Android:
HumanSecurity.setupWebView(webView:webViewClient:)
Important: Do not set the navigationDelegate (iOS) or webViewClient (Android) after setupWebView.
On iOS, set HSHybridAppPolicy.automaticSetup to true to detect and set up WKWebView instances without calling setupWebView for each one.
Example implementation
Android
Kotlin:
Java:
Kotlin (Activity):
Java (Activity):
iOS
Swift:
Objective-C:
Explanation of the code
- Web root domains: Set the registrable root (
example.com) inwebRootDomains. Prefer the bare form; a leadingwww.is stripped to that root. Do not usehttps://…. - Start the SDK as early as possible on the main thread so URL requests include HUMAN headers.
- Set up WebViews:
- Android:
HumanSecurity.setupWebView(webView:webViewClient:). Do not set theWebViewClientdirectly after this. - iOS:
HumanSecurity.setupWebView(webView:navigationDelegate:). Do not set thenavigationDelegatedirectly after this.
- Android:
Notes:
- iOS automatic setup:
HSHybridAppPolicy.automaticSetup = truesets upWKWebViewinstances without callingsetupWebViewfor each one. - Multiple App IDs: Use
HumanSecurity.start(appIds:policy:)and pass the App ID that owns each root, as in the example above.
What a working integration looks like
The native VID and the WebView VID must be identical.
Confirm the following:
If your sync is successful, you should see the following cookies in an instrumented WebView:
If those VIDs differ after the first navigation, hybrid is not working. If that’s the case, refer to Troubleshooting.
Verification
Run the Doctor App Web view test before production. Treat a passing hybrid/WebView test as a release gate.
- Enable the Doctor App (
HSDoctorAppPolicy.enabled = true). See How to verify the SDK integration in your app for more information. - Choose the Web view framework test and exercise the WebView that loads your protected site.
- On the summary screen, confirm the test passed.
- Export the result JSON and keep it. That export is the standard attachment for HUMAN Support.
Disable the Doctor App (enabled = false) before you ship.
_pxhd in hybrid apps
_pxhd is an HTTP cookie that carries an encrypted VID and socket IP. On Enforcer traffic, it is consulted before _pxvid.
Inside an instrumented WebView, the SDK and Sensor manage _pxhd so the SDK’s VID wins. You may see _pxhd disappear in that WebView; that is expected.
If native and WebView VIDs diverge on a domain that is also a desktop property, _pxhd is the first thing support checks. Contact HUMAN; this is not fixed by changing webRootDomains alone.
Cookies and storage you must not delete
If the app clears cookies or WebView storage on logout, use Mobile SDK 5.3.0 or later. That release restores missing _pxmd / _pxwvm after a wipe (cookies channel; other channels restore _pxwvm as appropriate). Older SDKs stay broken until the next full handshake.
Prevention still matters, especially on Android native and wrapper-hosted WebViews where hybrid often depends on cookies. If you write deletion logic, skip HUMAN keys. Do not delete every cookie in the WebView store.
Cookies (skip these): _pxmd, _pxwvm, _pxvid, _pxmvid, _pxhd, _pxda, _px_mobile_data, _px / _px2 / _px3, _pxde, _pxff_*, _pxttld, pxcts, __pxvid
Local storage (skip these): _advanced_features, fsch, pxcts, px-ff, px_hvd, and Code Defender keys px_22j9f8hlau2f5, px_33df3rmnerrf5 if you use that product.
Session storage (skip these): _pr_c, px_c_p_, px_fp, px_nfsp, pxsid, pxtiming, and Code Defender px_11a381f6 if applicable.
Full descriptions: Use of cookies and web storage.
Troubleshooting
Native VID and WebView VID differ
First check: webRootDomains is the bare root (example.com), not a sub-domain. Confirm matching with the table above.
Then check the following:
- The WebView is a supported container
setupWebView/automaticSetup/supportExternalWebViewsmatches how the WebView is created- Contact HUMAN if
_pxhdis on a domain shared with desktop.
Challenge loop inside a WebView
Check the VID split (native vs. _pxvid). A challenge loop usually means two identities. Confirm the container is supported. SFSafariViewController and Chrome Custom Tabs are not default hybrid.
Apple Pay on the Web
If your website uses Apple Pay on the Web, disable JavaScript evaluation by the SDK. Apple Pay cannot be used alongside script injection APIs on iOS 13–15.
If your app targets only iOS 16 or above, you do not need to disable JavaScript evaluation.
Swift:
Objective-C:
External Web Views (available from v4.0.1)
If WebViews are created outside native code but still use WKWebView or Android WebView (for example react-native-webview), enable supportExternalWebViews.
The HUMAN React Native wrapper and Expo module already set supportExternalWebViews = true. Flutter, Ionic, Capacitor, and Cordova do not. Instead, set the flag in native start together with webRootDomains. See WebView container support for more information.
Android
Kotlin:
Java:
iOS
Swift:
Objective-C: