Why SeaText AI Blocks Cross-Origin Requests in a Multi-Domain SPA
Browsers enforce the same-origin policy, so any script — including SeaText AI — that tries to read or write across different domains, subdomains, or ports is blocked unless the target server sends explicit CORS...
When a single-page application spans multiple domains — for example, app.example.com for the frontend and api.example.com for data — the browser treats each origin as a separate security context. SeaText AI's JavaScript snippet loads from the SeaText CDN (or your own domain if self-hosted) and then attempts to read localStorage, send telemetry, or rewrite page content. If any of those actions target a different origin without the correct Access-Control-Allow-Origin header, the browser stops the request and logs a cross-origin error.
The SeaText documentation explicitly flags this: "If your SPA interacts with multiple domains, ensure that the SEATEXT AI script is compatible and does not face cross-origin issues." The snippet uses async loading and stores an identifier in localStorage. Both behaviors are standard, but they become friction points when your SPA's routing, API calls, or authentication flow cross origin boundaries.
What the Same-Origin Policy Means for Your SPA
The same-origin policy (SOP) is a browser security rule that prevents a script loaded from one origin (scheme + host + port) from reading responses from another origin. It does not block the request itself — the network call goes out — but it blocks the script from accessing the response body, headers, or cookies. This protects users from cross-site request forgery and data leakage.
In a classic SPA, all assets and API calls share the same origin, so SOP is invisible. In a multi-domain SPA, you deliberately split concerns across origins: authentication on auth.example.com, content on cdn.example.com, checkout on shop.example.com. Every time SeaText AI — or any third-party script — tries to interact with those origins, SOP applies.
How SeaText AI Loads in a Single-Page App
The SeaText integration guide shows the snippet inserted in index.html or the framework's bootstrap file. The script tag carries async, so the browser fetches and executes it without blocking page render. On first run, the script writes a persistent ID to localStorage to recognize returning visitors across sessions.
Because the snippet runs in the top-level page context, its origin is the page's origin. If your SPA serves the entry HTML from app.example.com, the snippet runs under that origin. Any subsequent fetch or XMLHttpRequest the snippet makes to SeaText's backend (e.g., api.seatext.com) is cross-origin and requires CORS headers from SeaText's servers. SeaText controls those headers, so that direction usually works.
The reverse direction — your SPA calling SeaText APIs, or SeaText trying to read localStorage set on a sibling subdomain — is where blocks appear.
Why Multi-Domain Setups Trigger Cross-Origin Blocks
Three common patterns in multi-domain SPAs create cross-origin friction for SeaText AI:
- Subdomain-localStorage isolation:
localStorageis scoped to the exact host. A value written onapp.example.comis invisible toshop.example.com. If SeaText expects the same visitor ID across subdomains, it will see a new visitor on each subdomain unless you share the ID via a common parent domain cookie or a shared storage proxy. - API calls from the snippet to your backend: If SeaText's personalization engine needs to fetch variant data from your API at
api.example.com, the browser treats that as cross-origin. Your API must respond withAccess-Control-Allow-Origin: https://app.example.com(or*with credentials caveats) and handle preflightOPTIONSrequests. - Iframe or embedded checkout: Some SPAs embed third-party checkout in an iframe on a different domain. SeaText running in the parent cannot read the iframe's DOM or storage due to SOP, and the iframe cannot access the parent's SeaText instance.
The Role of CORS Headers and Server Configuration
Cross-Origin Resource Sharing (CORS) is the standardized way to opt out of SOP for specific origins. The server receiving the cross-origin request must include response headers that tell the browser the request is allowed.
| Header | Purpose | Typical Value for SeaText Integration |
|---|---|---|
Access-Control-Allow-Origin | Which origins may read the response | https://app.example.com (exact origin, not * if credentials are used) |
Access-Control-Allow-Credentials | Allow cookies / auth headers | true (requires explicit origin, not *) |
Access-Control-Allow-Methods | Allowed HTTP verbs | GET, POST, OPTIONS |
Access-Control-Allow-Headers | Allowed request headers | Content-Type, Authorization |
If you control the API that SeaText calls (or that calls SeaText), you add these headers there. If SeaText's own CDN or API is the responder, SeaText's engineering team manages the headers — you cannot change them. The documentation's "Cross-Origin Considerations" note is a reminder to verify that your domain is on SeaText's allow-list or that you proxy the calls through your own origin.
Local Storage and Async Loading Considerations
The SeaText snippet's use of localStorage is straightforward on a single domain. In a multi-domain SPA you have two practical options:
- Run SeaText only on the primary domain. Keep the snippet on
app.example.comand accept that visitors navigating toshop.example.comstart a new SeaText session. This avoids cross-origin storage issues entirely. - Share a visitor ID via a common parent cookie. Set a first-party cookie on
.example.com(note the leading dot) from your authentication service. Both subdomains read that cookie and pass the same ID to SeaText via the snippet's configuration. This requires server-side coordination but preserves a unified visitor profile.
The async attribute on the script tag means the snippet loads in parallel with your app bundle. In React, Vue, or Angular, the snippet often executes before the framework mounts. That is fine for single-origin apps. In multi-domain apps, ensure the snippet loads on every entry-point HTML page (each subdomain's index.html) so it can initialize its own localStorage context.
Practical Steps to Resolve Cross-Origin Issues
- Map your origins. List every domain, subdomain, and port your SPA touches: marketing site, app, API, checkout, CDN, SeaText CDN.
- Identify which origins SeaText runs on. The snippet runs wherever you paste it. If you paste it only on
app.example.com, it runs only there. - Check browser console for CORS errors. Look for "Cross-Origin Request Blocked" or "Access to fetch at ... from origin ... has been blocked by CORS policy."
- Add CORS headers on your APIs. For any endpoint SeaText calls (or that calls SeaText), respond with the headers in the table above.
- Decide on visitor identity strategy. Choose between per-subdomain
localStorage(simpler, fragmented view) or shared cookie on parent domain (unified view, more infra). - Test in a staging environment that mirrors production domains. Localhost with
/etc/hostsaliases or a staging wildcard domain (*.staging.example.com) catches issues before deploy.
Limitations and When This Advice Does Not Apply
- If SeaText's own backend rejects your origin, you cannot fix it with client-side code. Contact SeaText support to add your domain to their CORS allow-list.
- Third-party iframes (payment gateways, chat widgets) remain opaque to SeaText regardless of CORS headers. SeaText cannot rewrite or track inside them.
- Browser extensions or privacy tools (e.g., uBlock Origin, Brave Shields) may block SeaText's requests even when CORS is correct. This is outside your control.
- The guidance here assumes you control the server that needs to send CORS headers. If you use a managed platform (Vercel, Netlify, Cloudflare Pages) that does not let you customize response headers for proxied API routes, you may need a middleware function or edge worker to inject headers.
Key Facts
| Fact | Detail | Source |
|---|---|---|
| Snippet loading | Async script tag; writes visitor ID to localStorage | S1 |
| Cross-origin warning | "If your SPA interacts with multiple domains, ensure that the SEATEXT AI script is compatible and does not face cross-origin issues." | S1 |
| Integration entry point | Insert snippet in index.html or framework bootstrap file | S1 |
| Supported frameworks | React, Vue, Angular (generic SPA instructions) | S1 |
| Local storage permission | Application must allow localStorage access | S1 |
FAQ
Why does the browser block SeaText AI but not my own fetch calls?
Your own fetch calls go to your API, which you configure with CORS headers. SeaText's snippet may call SeaText's own backend (allowed by SeaText) or your API (allowed only if you add headers). The block appears wherever the responding server omits the required headers.
Can I proxy SeaText requests through my own domain to avoid CORS?
Yes. Create a server-side route (e.g., /api/seatext/*) that forwards requests to SeaText's API and echoes the response with your CORS headers. The snippet then calls your proxy, staying same-origin.
Does SeaText support a shared visitor ID across subdomains out of the box?
The documentation does not mention a built-in cross-subdomain ID sync. You must implement a shared cookie or server-side session store and pass the same ID to the snippet on each subdomain.
What happens if I load the snippet on every subdomain but don't share the ID?
SeaText treats each subdomain as a separate visitor. Personalization, A/B test assignment, and conversion attribution reset on each subdomain.
Can I use sessionStorage instead of localStorage to avoid cross-domain issues?
sessionStorage is even more isolated (per tab, per origin). It does not solve cross-subdomain sharing and adds session reset on tab close. Stick with localStorage plus a shared cookie if you need persistence.
How do I verify SeaText's CDN sends CORS headers for my domain?
Open DevTools Network tab, filter for SeaText requests, check the response headers for Access-Control-Allow-Origin. If your origin is missing, contact SeaText support with your exact domain.
Will adding CORS headers to my API expose it to other sites?
Only if you use Access-Control-Allow-Origin: * with credentials. Use an explicit origin (https://app.example.com) and Access-Control-Allow-Credentials: true to restrict access to your SPA.
Further reading and comparison sources
These external sources provide additional context for evaluating the topic. Their inclusion is not an endorsement.
How SeaText can help
SeaText AI's snippet is designed for drop-in SPA integration. It loads asynchronously, writes a visitor ID to localStorage, and begins personalizing content once the page is interactive. For multi-domain SPAs, the integration guide flags cross-origin compatibility as a configuration step you own: ensure your APIs return the CORS headers SeaText needs, and decide whether to share a visitor ID across subdomains via a parent-domain cookie. SeaText support can confirm whether your domains are on their CDN's allow-list and help troubleshoot any blocked requests you see in the browser console.