Answer the exact question first
To keep a release build from loading remote content via server.url, you must block the setting at both the configuration level (the file used for npx cap sync) and the build level (the generated native config that ends up in the app bundle). server.allowNavigation alone does not protect fetch/XHR calls; you need to pair it with platform‑specific network policies (Android Network Security Config / cleartext, iOS App Transport Security). The most reliable way to confirm the actual origin is to log window.location.origin (or a test fetch target) from a release‑signed build and read that value from Logcat or the Xcode console, while also verifying that a change to a bundled asset is reflected in the running app.
Likely explanation
The server block is copied into the native projects during cap sync. The WebView reads the generated native config at launch, so if server.url is present in the config used for the last sync, a release build will honor it and load remote content. The block does not have a built‑in debug‑only guard; the restriction must be enforced by the developer.
Confirmed facts
server.allowNavigation only limits top‑level and iframe navigations; it does not restrict fetch, XHR, or other resource loads.
- Android defaults to allowing only the app’s own origin for navigation, while iOS has been more permissive; these defaults have changed across Capacitor major versions.
server.cleartext (Android) and androidScheme/iosScheme affect the WebView origin, which in turn changes localStorage, cookies, and CORS behavior.
- Generated native files (
android/app/src/main/assets/capacitor.config.json and ios/App/App/capacitor.config.json) can drift from capacitor.config if cap sync is skipped or run against a different config file.
Steps needed for this case
- Ensure the configuration file used for
npx cap sync (or cap copy) does not contain a server.url property for the release profile. Use environment‑based config or a separate capacitor.release.json that omits the property.
- After syncing, verify the generated native config files:
# Android
cat android/app/src/main/assets/capacitor.config.json
# iOS
cat ios/App/App/capacitor.config.json
- Add a CI/build check that fails if
server.url appears in either generated file.
- To protect fetch/XHR, configure platform network policies:
- Android: set
android:usesCleartextTraffic="false" in the manifest and add a Network Security Config that permits only HTTPS origins you trust.
iOS: enable App Transport Security (ATS) in Info.plist with appropriate exceptions if needed.
- Keep
server.allowNavigation as an additional navigation allowlist, but do not rely on it alone for network security.
Verifying the actual origin in a release build
- Add a startup script to your web code, e.g.:
console.log('CAPACITOR ORIGIN', window.location.origin);
// optional test fetch
fetch('/ping').then(r => r.text()).then(t => console.log('FETCH target', new URL(r.url).origin));
- Build a release‑signed APK/AAB or IPA.
- Run the app on a device or emulator.
- Capture the console output:
- Android:
adb logcat | grep "CAPACITOR ORIGIN"
- iOS: view the output in Xcode’s console or via
device log.
- Confirm that the logged origin matches the expected bundled origin (typically
capacitor://localhost or the custom scheme you set).
- As a secondary check, edit a bundled asset (e.g., change the text in
index.html), rebuild the release bundle, and verify the change appears in the running app; if it does not, the WebView is loading from a remote origin.
Missing diagnostic detail
To give a precise recommendation about default values for server.allowNavigation and platform network policies, I need to know: Which major version of Capacitor are you targeting (e.g., v3, v4, v5)? This changes the default navigation allowlist and the default scheme used for the WebView origin.