SecurityConfig = object
Defined in: packages/modelence/src/app/securityConfig.ts:34
Security configuration for the application
By default, the app is protected against clickjacking by setting
Content-Security-Policy: frame-ancestors 'self' and X-Frame-Options: SAMEORIGIN
on all responses, preventing the app from being embedded in iframes on other domains.
Examples
import { startApp } from 'modelence/server';
// Allow embedding in iframes on specific domains
startApp({
security: {
frameAncestors: ['https://modelence.com', 'https://app.example.com'],
trustedProxies: ['loopback', 'linklocal', 'uniquelocal'],
clientIpHeader: 'cf-connecting-ip',
},
});
// Allow a browser client on another origin to call this app's API (e.g. Expo
// Web, which Metro serves on :8081 while the API runs on :3000). Applies to
// module routes and framework API routes, not to SSR pages or static assets.
startApp({
security: {
allowedOrigins: ['http://localhost:8081'],
},
});
Properties
| Property | Type | Description | Defined in |
|---|---|---|---|
allowedOrigins? | string[] | Origins allowed to read this app’s responses from a browser (CORS). Browsers block a cross-origin fetch unless the response carries a matching Access-Control-Allow-Origin. The common case is Expo Web, which Metro serves on a different port from the API — a different port is a different origin, so every method call is blocked without this. Scope: this covers your module routes and the framework’s own API routes (method calls and the OAuth endpoints). SSR pages and static assets are excluded, so a listed origin can call your API but cannot read your rendered pages with credentials. The scope is derived from the routes actually registered, not matched by path prefix: module routes carry no framework-imposed prefix (the docs’ example mounts /todos at the root), so a prefix rule would silently drop CORS from the user-defined routes that most need it. Each entry must be an exact origin (scheme://host[:port]); patterns and wildcards are not supported, since the response header carries one concrete origin. Entries are normalized (trimmed, lowercased, default port and trailing slash dropped) to match what browsers send, and anything that is not a valid origin throws at startup rather than silently never matching. The matched origin is echoed back rather than *, and Access-Control-Allow-Credentials is sent, so the browser will expose a credentialed response to JS. Note this only covers same-site requests: the auth cookie is SameSite=Lax, so a genuinely cross-site caller (app.example.com → api.other.com) never has the cookie attached in the first place, regardless of this setting. The Expo Web case works because localhost:8081 and localhost:3000 differ only by port, and port is not part of a site. Opt-in by design: when unset, no CORS headers are sent at all. Deployments that already add CORS at a proxy or router therefore stay untouched — a duplicated Access-Control-Allow-Origin is invalid and would break them. Native iOS/Android do not enforce CORS and never need this. | packages/modelence/src/app/securityConfig.ts:82 |
clientIpHeader? | string | Name of a single-value header the trusted proxy sets to the originating client IP, used instead of walking the X-Forwarded-For chain. Cloudflare recommends reading CF-Connecting-IP (or True-Client-IP on Enterprise plans) rather than X-Forwarded-For, because Cloudflare appends to an inbound X-Forwarded-For instead of overwriting it, while these headers always carry exactly one address. This header is only read when the immediate peer is a trusted proxy, so trustedProxies (or MODELENCE_TRUSTED_PROXIES) must also be configured with the proxy’s addresses. Without that, a direct caller could set the header themselves and choose their own rate-limit identity. When the peer is untrusted or the header is absent, the IP falls back to the normal trust proxy resolution. Example // Behind Cloudflare, with Cloudflare's published ranges trusted: clientIpHeader: 'cf-connecting-ip' | packages/modelence/src/app/securityConfig.ts:128 |
frameAncestors? | string[] | Additional origins allowed to embed this app in an iframe. The app’s own origin ('self') is always included automatically. When not set, only same-origin framing is allowed. When set, X-Frame-Options is omitted since it cannot express multiple origins. | packages/modelence/src/app/securityConfig.ts:42 |
trustedProxies? | string | string[] | IP addresses or CIDR ranges of reverse proxies that are allowed to supply the client IP through X-Forwarded-For. This uses Express’s trust proxy address syntax, which also supports the named ranges loopback, linklocal, and uniquelocal. For backward compatibility, all proxy addresses are trusted when neither this option nor MODELENCE_TRUSTED_PROXIES is set. Configure one of them in production so only addresses that cannot be reached directly by untrusted clients are trusted. Once configured, connectionInfo.ip is resolved by walking the proxy chain from the app toward the client and stopping at the first untrusted address. This keeps a caller from choosing its rate-limit identity by prepending a forged X-Forwarded-For value. Example trustedProxies: ['loopback', '10.0.0.0/8'] | packages/modelence/src/app/securityConfig.ts:104 |