WebView Architecture

Understanding How Your App Works

When you convert your HTML/CSS/JavaScript game or app into a native mobile app, it runs in a special architecture with three distinct layers. Understanding this architecture is crucial for properly configuring security and connectivity.

Web Architecture Analogy

Think of your mobile app like a traditional website:

  • Flutter WebView = User's web browser (Chrome, Firefox, Safari)
  • Internal Asset Server = CDN for static files (like Cloudflare, CloudFront, or Netlify)
  • External Servers = Backend API, CDN, or asset servers (Express.js, Django, FastAPI, etc.)

Just like a website loads HTML/CSS/JS from a CDN and makes API calls to a separate backend, your mobile app loads your game/app assets from an internal server and can make API calls to your external servers.

Architecture Diagram

Your mobile app has three layers with different purposes:

┌─────────────────────────────────────────────────────────────────┐
│                    Your Native Mobile App                       │
│                                                                 │
│  ┌──────────────────────────────────────────────────────────┐   │
│  │  Flutter WebView (The "Browser")                         │   │
│  │  • Renders your HTML/JS/CSS                              │   │
│  │  • Executes your JavaScript code                         │   │
│  │  • Enforces browser security (CSP, CORS)                 │   │
│  └──────────────────────────────────────────────────────────┘   │
│        │                                    │                   │
│        │ (1) Loads your app:                │ (2) JS fetch():   │
│        │ HTML, JS, CSS, images              │ GET/POST data     │
│        ▼                                    │                   │
│  ┌──────────────────────────┐               │                   │
│  │  Internal Asset Server   │               │                   │
│  │  http://localhost:????   │               │                   │
│  │  (Random port)           │               │                   │
│  │                          │               │                   │
│  │  • Serves your files     │               │                   │
│  │  • Security headers      │               │                   │
│  │  • NO CORS needed here   │               │                   │
│  │  • Only localhost access │               │                   │
│  └──────────────────────────┘               │                   │
│                                             │                   │
└─────────────────────────────────────────────┼───────────────────┘

                                              │ Cross-Origin Request
                                              │ (CORS enforced by WebView)

                                ┌──────────────────────────────┐
                                │   External Servers           │
                                │   (API, CDN, Assets)         │
                                │   https://api.yourapp.com    │
                                │                              │
                                │   **CORS REQUIRED HERE**     │
                                │   Must allow localhost:????  │
                                └──────────────────────────────┘

The Three Layers Explained

Layer 1: Flutter WebView (The Browser)

What it is: A browser embedded in your mobile app that renders your HTML/CSS and runs your JavaScript.

What it does:

  • Displays your game/app interface
  • Executes your JavaScript code
  • Enforces security policies (just like Chrome or Safari)
  • Prevents unauthorized cross-origin requests

Important: The WebView acts exactly like a regular web browser. It enforces the same security restrictions (CORS, CSP) that Chrome or Firefox would enforce.

Layer 2: Internal Asset Server (The CDN)

What it is: A tiny web server running inside your mobile app that serves your files to the WebView.

What it does:

  • Serves your HTML, JavaScript, CSS, images, and other assets
  • Runs on a random port (e.g., http://localhost:49152 or http://localhost:51234)
  • Only accessible from the device itself (not from the internet or local network)
  • Configured via the SaaS dashboard with security headers

Important Facts:

  • Extremely fast - Files are loaded from the device, not the internet
  • 🔒 Secure by default - Only accessible via localhost (127.0.0.1)
  • 🎲 Random port - Port changes every time the app starts
  • 🚫 No CORS needed - It only serves files, doesn't handle API requests

You configure: Content Security Policy (CSP) and other security headers via the SaaS dashboard.

👉 See Content Security Policy Guide for configuration details.

Layer 3: External Servers (Optional)

What it is: Your own backend server or external service that provides data, authentication, business logic, or assets (API, CDN, etc.).

What it does:

  • Handles API requests from your game/app (GET, POST, PUT, DELETE)
  • Serves assets like images, fonts, or libraries from CDNs
  • Manages user data, game state, leaderboards, etc.
  • Authenticates users and manages sessions

Important: If your game/app makes requests to external servers (API calls, loading assets from CDN, etc.), you must configure CORS on that server to allow requests from localhost with random ports.

You configure: CORS headers on your external server to allow cross-origin requests.

👉 See CORS Configuration Guide for setup instructions.

Key Distinctions

AspectInternal Asset ServerExternal Servers
PurposeServes your app's filesProvides data, logic, assets
LocationInside the mobile appYour own server
URLhttp://localhost:???? (random port)https://api.yourapp.com
AccessLocalhost onlyInternet accessible
SpeedInstant (< 1ms)Network dependent
ConfigurationVia SaaS dashboardOn your server
CSP needed?✅ Yes (controls what can load)❌ No
CORS needed?❌ No (same origin)✅ Yes (if you have an API)
Caching needed?❌ No (already instant)✅ Yes (reduces latency)

Common Scenarios

Scenario 1: Self-Contained Game (No External Server)

Your game is completely self-contained with no backend server.

┌─────────────────────────┐
│  WebView                │
│  Loads from localhost   │
└──────────┬──────────────┘


┌──────────────────────────┐
│  Internal Asset Server   │
│  Serves all your files   │
└──────────────────────────┘

You need to configure:

  • ✅ CSP on the internal server (via SaaS dashboard)
  • ❌ CORS - Not needed

Scenario 2: Game with External API or CDN

Your game calls an external API for leaderboards, user accounts, or game data, or loads assets from a CDN.

┌─────────────────────────┐
│  WebView                │
│  Loads from localhost   │
│  Calls external API     │
│  Loads from CDN         │
└──────┬──────────┬───────┘
       │          │
       ▼          ▼
┌──────────┐  ┌──────────────────┐
│ Internal │  │ External Servers │
│ Server   │  │ (API, CDN, etc)  │
└──────────┘  └──────────────────┘

You need to configure:

  • ✅ CSP on the internal server (via SaaS dashboard)
  • ✅ CORS on your external servers (allow localhost:*)

Scenario 3: Loading External Resources (CDNs)

Your app loads libraries or assets from external CDNs.

┌─────────────────────────┐
│  WebView                │
│  <script src="cdn...">  │
└──────┬──────────┬───────┘
       │          │
       ▼          ▼
┌──────────┐  ┌──────────────────┐
│ Internal │  │ External CDN     │
│ Server   │  │ cdn.jsdelivr.net │
└──────────┘  └──────────────────┘

You need to configure:

  • ✅ CSP to allow external CDNs (via SaaS dashboard)
  • ❌ CORS - CDNs already configured correctly

Why the Random Port is Secure

You might wonder: "If the port is random and changes every time, how is this secure?"

The random port is a security feature, not a bug:

  1. Localhost-only access: The internal server binds to 127.0.0.1, which means it's only accessible from the device itself, never from the network or internet.

  2. Port randomization prevents conflicts: Multiple apps can run simultaneously without port conflicts.

  3. No port scanning attacks: Since the server isn't exposed to the network, attackers can't discover or connect to it.

  4. CORS handles external servers: Your external servers validate the origin (any localhost:*) to allow legitimate requests while blocking unauthorized origins.

Next Steps

Now that you understand the architecture, configure your app's security:

  1. Configure CSP for the internal serverContent Security Policy Guide
  2. Configure CORS for your API (if applicable)CORS Configuration Guide

Quick Reference

Internal Asset Server (Configure via SaaS Dashboard):

Your External Servers (Configure on your server):

  • Purpose: Handle API requests and serve assets
  • Headers: CORS headers (Access-Control-Allow-Origin)
  • See: CORS Configuration Guide