Mobile implementation
Turnstile is designed to run in standard browser environments, which includes mobile devices accessing your website through mobile browsers.
For native mobile applications, Turnstile can be integrated using WebViews that render your web content containing Turnstile widgets.
WebViews are components that allow native mobile applications to display web content. They embed a browser engine within your native application, enabling you to show web pages, forms, and JavaScript-powered content like Turnstile widgets.
For Turnstile to function properly in WebView, the following requirements must be met.
- JavaScript execution must be enabled.
- DOM storage API must be available.
- Standard web APIs must be accessible.
- Access to
challenges.cloudflare.com - Support for both HTTP and HTTPS connections.
- Allow connections to
about:blankandabout:srcdoc
- Consistent User Agent throughout the session
- Stable device and browser characteristics
- No modification to core browser behavior
WebView webView = findViewById(R.id.webview);WebSettings webSettings = webView.getSettings();
// Required: Enable JavaScriptwebSettings.setJavaScriptEnabled(true);
// Required: Enable DOM storagewebSettings.setDomStorageEnabled(true);
// Recommended: Enable other web featureswebSettings.setLoadWithOverviewMode(true);webSettings.setUseWideViewPort(true);webSettings.setAllowFileAccess(true);webSettings.setAllowContentAccess(true);
// Load your web content with TurnstilewebView.loadUrl("https://yoursite.com/protected-form");import WebKit
class ViewController: UIViewController { @IBOutlet weak var webView: WKWebView!
override func viewDidLoad() { super.viewDidLoad()
// Configure WebView let configuration = WKWebViewConfiguration() configuration.preferences.javaScriptEnabled = true
// Load your web content with Turnstile if let url = URL(string: "https://yoursite.com/protected-form") { webView.load(URLRequest(url: url)) } }}import { WebView } from 'react-native-webview';
export default function App() { return ( <WebView source={{ uri: 'https://yoursite.com/protected-form' }} javaScriptEnabled={true} domStorageEnabled={true} allowsInlineMediaPlayback={true} mediaPlaybackRequiresUserAction={false} /> );}import 'package:flutter_inappwebview/flutter_inappwebview.dart';
class WebViewScreen extends StatelessWidget { @override Widget build(BuildContext context) { return InAppWebView( initialUrlRequest: URLRequest( url: Uri.parse('https://yoursite.com/protected-form') ), initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( javaScriptEnabled: true, useShouldOverrideUrlLoading: false, ), android: AndroidInAppWebViewOptions( domStorageEnabled: true, ), ios: IOSInAppWebViewOptions( allowsInlineMediaPlayback: true, ), ), ); }}Changing the User Agent during a session causes Turnstile challenges to fail because the system relies on consistent browser characteristics to validate the visitor's authenticity. When the User Agent changes mid-session, Turnstile treats this as a potential security risk and rejects the challenge.
// Android - Set consistent User AgentwebSettings.setUserAgentString(webSettings.getUserAgentString());// iOS - Maintain default User AgentwebView.customUserAgent = webView.value(forKey: "userAgent") as? StringStrict Content Security Policy settings can prevent Turnstile from loading the necessary scripts and making required network connections. This happens when CSP headers or meta tags block access to the domains and resources that Turnstile needs to function properly.
<meta http-equiv="Content-Security-Policy" content=" default-src 'self'; script-src 'self' challenges.cloudflare.com 'unsafe-inline'; connect-src 'self' challenges.cloudflare.com; frame-src 'self' challenges.cloudflare.com;">WebView security restrictions can prevent access to the domains that Turnstile requires for proper operation. Some WebViews are configured to only allow specific domains or block certain types of connections, which can interfere with Turnstile's ability to load challenges and communicate with Cloudflare's servers.
To resolve this, configure your WebView's allowed origins to include all domains that Turnstile needs:
challenges.cloudflare.comabout:blankabout:srcdoc- Your own domain(s)
The exact configuration method varies by platform, but the principle is to explicitly allow network access for these domains.
Cookies and local storage not persisting between sessions can cause Turnstile to fail because it relies on these mechanisms to maintain state and track visitor behavior. This commonly occurs when WebView storage settings are too restrictive or when the app clears storage between sessions. Ensure that your WebView is configured to properly handle cookies and local storage.
// Android - Enable cookiesCookieManager.getInstance().setAcceptCookie(true);CookieManager.getInstance().setAcceptThirdPartyCookies(webView, true);// iOS - Configure cookie storagewebView.configuration.websiteDataStore = WKWebsiteDataStore.default()Was this helpful?
- Resources
- API
- New to Cloudflare?
- Directory
- Sponsorships
- Open Source
- Support
- Help Center
- System Status
- Compliance
- GDPR
- Company
- cloudflare.com
- Our team
- Careers
- © 2025 Cloudflare, Inc.
- Privacy Policy
- Terms of Use
- Report Security Issues
- Trademark
-