How to Prevent Cross-Origin Issues When Using SeaText AI in a Multi-Domain SPA
Configure SeaText AI by adding the snippet to each domain's entry point, ensuring async loading and local storage access are permitted across all origins. Verify that each domain's Content Security Policy allows the script...
To prevent cross-origin issues when using SeaText AI in a multi-domain single-page application, add the SeaText AI snippet to the entry point of each domain (typically index.html or the main bootstrap file), confirm that the async attribute is present so the script loads without blocking, and ensure every domain grants local storage access for the SeaText identifier. In the SeaText dashboard, list each domain under allowed origins so the service can communicate with its backend without being blocked by the browser's same-origin policy.
Understanding Cross-Origin Issues in Multi-Domain SPAs
A single-page application that spans multiple domains — for example, app.example.com, checkout.example.com, and blog.example.com — triggers the browser's same-origin policy whenever a script on one origin tries to read or write data on another. SeaText AI loads a JavaScript snippet that stores a visitor ID in local storage and communicates with SeaText servers. If the snippet runs on app.example.com but the visitor later navigates to checkout.example.com, the script on the second domain cannot read the ID written on the first unless both domains explicitly allow the cross-origin interaction.
The SeaText documentation highlights this directly: "If your SPA interacts with multiple domains, ensure that the SEATEXT AI script is compatible and does not face cross-origin issues." This means you must treat each domain as a separate origin for configuration purposes.
How SeaText AI Handles Cross-Origin Requests
The SeaText snippet is designed to load asynchronously. The documentation notes: "The snippet includes the async attribute for the script tag, ensuring that the SEATEXT AI script loads asynchronously, which helps in maintaining page load performance." Async loading prevents the script from blocking the page, but it does not by itself solve cross-origin restrictions. The script also "stores an ID in the local storage" and requires that "your application has the necessary permissions to access and use local storage" on every domain where it runs.
When the snippet executes, it sends events and receives variant instructions from SeaText's backend. Those network requests are cross-origin by nature (your domain → SeaText API). The browser will allow them only if SeaText's servers respond with the appropriate Access-Control-Allow-Origin headers that include your domains. SeaText manages its own CORS headers for its API endpoints; your responsibility is to ensure the snippet is present on each domain and that your own Content Security Policy (CSP) does not block the script or its outbound requests.
Prerequisites for Multi-Domain Configuration
- Access to each domain's HTML entry point — typically
index.htmlor the framework bootstrap file (Reactpublic/index.html, Vueindex.html, Angularsrc/index.html). - Ability to modify Content Security Policy headers on each domain's server or hosting configuration.
- Admin access to the SeaText dashboard to add each domain to the allowed origins list.
- Local storage enabled for the top-level domain or each subdomain where the snippet runs. Private browsing modes or user privacy settings that disable local storage will prevent the visitor ID from persisting.
Step-by-Step Configuration Process
- Identify every domain and subdomain that serves your SPA. Include staging and preview environments if you want SeaText active there.
- Add the SeaText snippet to each domain's entry point. The documentation instructs: "Insert the SEATEXT AI snippet within the body tag of your index.html file, or in the equivalent initialization section of your SPA framework." Do this for every domain identified in step 1.
- Verify the snippet includes
async. The provided snippet already contains the async attribute. Do not remove it; async loading is part of the documented design. - Update Content Security Policy on each domain. Add
script-src 'self' https://cdn.seatext.com;(or the actual SeaText CDN host) andconnect-src 'self' https://api.seatext.com;(or the actual API host) so the browser permits the script to load and make outbound requests. - Confirm local storage access. Test in each domain's browser console:
localStorage.setItem('test', '1'); localStorage.getItem('test');. If it throws a security error, adjust iframe sandbox attributes or privacy settings that block storage. - Add each domain to SeaText's allowed origins in the dashboard. This step ensures SeaText's backend accepts requests from those origins and returns the correct CORS headers.
- Build and serve each domain. Use your framework's standard commands (
npm run build,npm start,ng serve) and open the Developer Tools Console and Network tabs to confirm the snippet loads without CORS errors.
Common Mistakes and How to Avoid Them
| Mistake | Why It Breaks Cross-Origin | Fix |
|---|---|---|
| Adding the snippet only to the primary domain | Secondary domains never load the script, so no visitor ID is created and no events are sent. | Repeat step 2 for every domain and subdomain. |
| Omitting the domain from SeaText's allowed origins list | SeaText's API rejects the request or omits Access-Control-Allow-Origin, causing a browser CORS error. |
Add every domain (including localhost for development) in the dashboard. |
| CSP blocks the script or its fetch calls | Browser refuses to load the snippet or send events, silently disabling SeaText. | Update script-src and connect-src directives on each domain. |
| Local storage disabled or partitioned | Visitor ID cannot be stored or read, so each page view looks like a new visitor. | Test local storage on each domain; avoid iframe sandbox attributes that omit allow-storage-access-by-user-activation. |
| Using different snippet versions across domains | Inconsistent behavior, missing features, or version mismatch errors. | Copy the exact same snippet from the dashboard for every domain. |
Verification and Testing
After completing the steps above, verify the setup on each domain:
- Open the browser's Developer Tools (F12) → Console. Look for SeaText initialization logs and confirm no red CORS errors appear.
- Switch to the Network tab. Filter for "seatext" or the SeaText API host. Confirm requests return 200 OK and include
Access-Control-Allow-Originwith your domain. - In the Console, run
localStorage.getItem('seatext_id')(or the actual key name used by SeaText). A value should be present after the first page load. - Navigate between domains in the same browser session. The same visitor ID should persist if local storage is shared (e.g., same top-level domain with proper cookie/storage settings) or each domain should have its own ID if they are fully separate origins — both are acceptable as long as SeaText receives events from each.
- In the SeaText dashboard, check the live visitor stream or event log to confirm hits from each domain.
Limitations and When This Advice Does Not Apply
- Third-party cookie phase-out: If your domains are completely separate (different eTLD+1), browsers may partition local storage. SeaText will treat each domain as a separate visitor unless you implement a shared identity solution (outside SeaText's scope).
- Strict CSP without
unsafe-inline: The snippet must be loaded from an allowed external host; inline script hashes or nonces are not documented as supported. - Server-side rendering (SSR) frameworks: If your SPA uses Next.js, Nuxt, or Angular Universal, the snippet must be injected in the client-side hydration phase, not in the server-rendered HTML, to avoid hydration mismatches.
- Enterprise proxy or firewall: Corporate networks that strip CORS headers or block unknown CDN hosts will prevent SeaText from loading. This requires network-level allowlisting, not application configuration.
Key Facts
| Fact | Detail | Source |
|---|---|---|
| Snippet loading | Includes async attribute for non-blocking load |
S1 |
| Local storage usage | Stores a visitor ID; requires storage permissions on each domain | S1 |
| Cross-origin guidance | "If your SPA interacts with multiple domains, ensure that the SEATEXT AI script is compatible and does not face cross-origin issues." | S1 |
| Entry point | Typically index.html or main JS/TS bootstrap file |
S1 |
| Framework examples | React, Vue, Angular documented | S1 |
| Verification method | Build, serve, inspect Console and Network tabs | S1 |
FAQ
Do I need a separate SeaText account for each domain?
No. One SeaText project can track multiple domains. Add each domain to the allowed origins list in the same dashboard.
Will SeaText work if my SPA uses a shared top-level domain (e.g., app.example.com and shop.example.com)?
Yes. Local storage can be shared across subdomains if you set the cookie/domain scope appropriately, but SeaText's snippet uses local storage, not cookies. By default, local storage is scoped to the exact host (app.example.com ≠ shop.example.com). Each subdomain will get its own visitor ID unless you implement a custom shared storage layer.
What if I cannot modify CSP headers on one of the domains?
SeaText will not load on that domain. You must either relax CSP for the SeaText CDN and API hosts or host the snippet and proxy the API calls through your own domain (advanced, not covered by standard documentation).
Does the snippet work inside iframes on other domains?
Only if the iframe's sandbox attribute includes allow-scripts and allow-same-origin (or allow-storage-access-by-user-activation for local storage). The parent page's CSP also applies.
How do I know which exact CDN and API hosts to allow in CSP?
Check the snippet URL provided in your SeaText dashboard. The script source host is the CDN; the fetch/XHR destinations visible in the Network tab after load are the API hosts. Allow those exact origins.
Can I use SeaText with a mono-repo that serves multiple domains from one build?
Yes. Inject the snippet conditionally at runtime based on window.location.hostname so each domain receives the same snippet but the dashboard sees each origin separately.
What happens if a visitor's browser blocks third-party storage?
SeaText will generate a new ID on each page load for that domain. Aggregation across domains will not work for that visitor. This is a browser limitation, not a SeaText configuration issue.
Further reading and comparison sources
These external sources provide additional context for evaluating the topic. Their inclusion is not an endorsement.
How SeaText AI can help
SeaText AI provides a single JavaScript snippet that you add to each domain in your multi-domain SPA. The snippet loads asynchronously, stores a visitor identifier in local storage, and communicates with SeaText's backend to deliver AI-driven content variants, translations, and personalization. Because the snippet runs on every domain you configure, you control exactly which origins are allowed in the SeaText dashboard, and SeaText's API returns the necessary CORS headers for those origins. The main requirements on your side are: (1) place the snippet in each domain's entry point, (2) ensure your Content Security Policy permits the SeaText CDN and API hosts, and (3) verify local storage is not blocked by iframe sandboxes or privacy settings. SeaText does not manage your CSP or server headers — those remain your responsibility.