# How to Secure Navigation and External Links in WebView-Enabled Native SDK Apps

> Learn how to secure navigation and external links in your WebView-enabled apps. Discover best practices for validating URLs, intercepting clicks, and protecting your users.

- Repository: [Vercel Labs/native](https://github.com/vercel-labs/native)
- Tags: how-to-guide
- Published: 2026-07-18

---

**Validate every URL before calling `navigate()`, intercept external link clicks inside the WebView, and never enable the `trusted` flag for remote content.**

The Vercel Native SDK lets you embed multiple WebViews inside a native window using handles that expose native-side controls. Because the SDK itself does not enforce URL restrictions, securing navigation and external links in WebView-enabled Native SDK apps falls entirely to the developer. This guide maps the actual type definitions and options in `vercel-labs/native` to concrete security patterns you can implement today.

## Understand the WebView Security Model in the Native SDK

The public API surface lives in [`packages/native-sdk/native-sdk.d.ts`](https://github.com/vercel-labs/native/blob/main/packages/native-sdk/native-sdk.d.ts). It defines `NativeSdkWebViewHandle`, which exposes a `navigate(url: string)` method (around line 128 of the declaration file) that triggers navigation without any built-in allow-list. The SDK also declares:

- **`NativeSdkCreateWebViewOptions`** – configuration passed to `NativeSdk.webviews.create()`, including a `trusted` flag that controls whether `window.zero` is injected.
- **`NativeSdkWebViewInfo`** – metadata payload that reflects the WebView’s current URL after navigation.
- **`NativeSdkSetWebViewLayerOptions`** – z-index controls used to stack WebViews relative to native UI layers.

According to [`docs/src/lib/site.ts`](https://github.com/vercel-labs/native/blob/main/docs/src/lib/site.ts), the project treats WebViews as an opt-in feature, while [`docs/src/lib/page-titles.ts`](https://github.com/vercel-labs/native/blob/main/docs/src/lib/page-titles.ts) maps the human-readable documentation titles and [`docs/src/lib/docs-navigation.ts`](https://github.com/vercel-labs/native/blob/main/docs/src/lib/docs-navigation.ts) organizes the navigation structure. None of these files implement runtime security, confirming that policy enforcement belongs in host application code.

## Validate URLs Before Calling navigate()

The first line of defense is a strict validation routine invoked before any call to `navigate()`. The SDK typings define the method signature as `navigate(url: string)`, accepting any string you provide.

Enforce an explicit allow-list that covers scheme, hostname, and path:

```typescript
const allowedHosts = new Set(['example.com', 'cdn.example.com']);

function isSafeUrl(raw: string): boolean {
  try {
    const url = new URL(raw);
    return url.protocol === 'https:' && allowedHosts.has(url.hostname);
  } catch {
    return false;
  }
}

```

Only proceed with navigation if `isSafeUrl` returns `true`:

```typescript
if (isSafeUrl(targetUrl)) {
  await webview.navigate(targetUrl);
} else {
  throw new Error('Blocked navigation to disallowed URL');
}

```

## Intercept External Links Inside the WebView

Remote pages can contain `<a>` tags that point to phishing sites or unauthorized domains. Since the SDK does not auto-intercept these, inject a click handler into the WebView context to inspect each link before it loads.

For untrusted destinations, prevent the default behavior and offload the URL to the system browser via `NativeSdk.shell.openExternal`:

```typescript
webview.addEventListener('dom-ready', () => {
  webview.executeJavaScript(`
    document.addEventListener('click', (e) => {
      const a = e.target.closest('a');
      if (!a) return;
      const href = a.href;
      if (!window.__isSafeUrl(href)) {
        e.preventDefault();
        window.__openExternal(href);
      }
    });
  `);
});

// Inject helpers into the WebView context
webview.executeJavaScript(`
  window.__isSafeUrl = ${isSafeUrl.toString()};
  window.__openExternal = (url) => {
    NativeSdk.shell.openExternal(url);
  };
`);

```

This pattern keeps the user inside the embedded view for approved content while routing external links out to the OS browser.

## Use the Trusted Flag Only for Internal UI

The `trusted` option in `NativeSdkCreateWebViewOptions` determines whether the SDK injects a privileged `window.zero` object into the page. As implemented in `vercel-labs/native`, this capability is intended for first-party app chrome only.

- Set **`trusted: false`** for any WebView that loads remote or user-generated content.
- Reserve **`trusted: true`** for local HTML assets that constitute your own trusted interface.

Enabling privileged injection on untrusted sites gives remote scripts unrestricted access to the native host, which defeats the sandbox.

## Layer Untrusted WebViews Behind Native UI Controls

The SDK exposes layer controls through `NativeSdk.webviews.setLayer()`, typed as `NativeSdkSetWebViewLayerOptions`. When you must render untrusted content, place the WebView on a lower layer and overlay native UI controls above it.

```typescript
await NativeSdk.webviews.setLayer({
  label: 'secure-child',
  layer: 10,
});

```

Place dismissal buttons, address bars, or confirmation dialogs on a higher layer. This ensures user actions can be intercepted by your code before reaching the underlying WebView.

## Monitor Navigation with WebView Metadata

After calling `navigate()`, verify the outcome by inspecting the `NativeSdkWebViewInfo` payload, which includes the WebView’s current URL. If the reported location deviates from the intended destination, close the view or reset the stack.

The documentation files [`docs/src/lib/docs-navigation.ts`](https://github.com/vercel-labs/native/blob/main/docs/src/lib/docs-navigation.ts), [`docs/src/lib/page-titles.ts`](https://github.com/vercel-labs/native/blob/main/docs/src/lib/page-titles.ts), and [`docs/src/lib/site.ts`](https://github.com/vercel-labs/native/blob/main/docs/src/lib/site.ts) frame WebViews as an opt-in capability. This design reinforces the expectation that developers implement custom policies around navigation rather than relying on a built-in browser sandbox.

## Summary

- **`navigate(url)`** does not filter destinations; always validate URLs with a scheme and hostname allow-list before invoking it.
- Inject click interceptors inside the WebView to catch `<a>` tags and route disallowed links to `NativeSdk.shell.openExternal`.
- Keep **`trusted: false`** for every WebView that renders remote content to prevent privileged `window.zero` injection.
- Use **`NativeSdk.webviews.setLayer()`** to stack untrusted WebViews beneath native UI overlays that can block or dismiss them.
- Inspect **`NativeSdkWebViewInfo`** metadata after navigation to confirm the final URL matches your expectations.

## Frequently Asked Questions

### How does the Native SDK enforce URL security for WebViews?

It does not. The `navigate()` method and WebView creation options defined in [`packages/native-sdk/native-sdk.d.ts`](https://github.com/vercel-labs/native/blob/main/packages/native-sdk/native-sdk.d.ts) accept arbitrary strings. The SDK delegates all URL policy decisions to the host application.

### What is the risk of setting trusted to true on a remote WebView?

Setting `trusted: true` injects a privileged `window.zero` object into the page context. According to the Native SDK type definitions, this gives the loaded page elevated access to the native host, which is dangerous for any content you do not control.

### How should external links inside a WebView be handled?

Intercept click events on `<a>` elements inside the WebView, validate the `href` against your allow-list, and call `e.preventDefault()` for disallowed URLs. Then invoke `NativeSdk.shell.openExternal` to send those links to the system browser instead of loading them inside the embedded view.

### Can I rely on layer ordering to prevent malicious WebView behavior?

Layer ordering is a defense-in-depth measure, not a primary security boundary. Use `NativeSdkSetWebViewLayerOptions` to place native UI controls above untrusted WebViews, but pair this with URL validation and the `trusted: false` flag to maintain a proper sandbox.