|
| 1 | +--- |
| 2 | +title: CORS and Security Attributes |
| 3 | +description: Understanding how Nuxt Scripts handles cross-origin security. |
| 4 | +--- |
| 5 | + |
| 6 | +## Background |
| 7 | + |
| 8 | +When loading scripts from external domains, browsers enforce Cross-Origin Resource Sharing (CORS) policies. CORS controls how resources on one domain can be requested by scripts running on another domain. For third-party scripts, this affects: |
| 9 | + |
| 10 | +- Whether cookies are sent with requests |
| 11 | +- Access to error details for debugging |
| 12 | +- Subresource Integrity (SRI) validation |
| 13 | + |
| 14 | +## Default Behavior |
| 15 | + |
| 16 | +Nuxt Scripts applies privacy-focused defaults to all scripts: |
| 17 | + |
| 18 | +```html |
| 19 | +<script |
| 20 | + src="https://example.com/script.js" |
| 21 | + crossorigin="anonymous" |
| 22 | + referrerpolicy="no-referrer" |
| 23 | +></script> |
| 24 | +``` |
| 25 | + |
| 26 | +These defaults: |
| 27 | +- **`crossorigin="anonymous"`** - Prevents the script from sending cookies to third-party servers |
| 28 | +- **`referrerpolicy="no-referrer"`** - Prevents sharing the page URL with third-party servers |
| 29 | + |
| 30 | +This improves user privacy but may break scripts that require cookies or referrer information. |
| 31 | + |
| 32 | +## Common CORS Errors |
| 33 | + |
| 34 | +### Script Fails to Load |
| 35 | + |
| 36 | +```text |
| 37 | +Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource |
| 38 | +``` |
| 39 | + |
| 40 | +This occurs when a server doesn't return proper CORS headers but `crossorigin="anonymous"` is set. Some third-party scripts don't support CORS. |
| 41 | + |
| 42 | +### Script Loads but Functions Fail |
| 43 | + |
| 44 | +The script loads but functionality is broken because it expected cookies or session data. |
| 45 | + |
| 46 | +### Error Details Hidden |
| 47 | + |
| 48 | +```js |
| 49 | +window.onerror = (msg) => console.log(msg) |
| 50 | +// Shows: "Script error." instead of actual error |
| 51 | +``` |
| 52 | + |
| 53 | +Without `crossorigin`, browsers hide error details from external scripts for security. |
| 54 | + |
| 55 | +## Configuring CORS Attributes |
| 56 | + |
| 57 | +### Per-Script Configuration |
| 58 | + |
| 59 | +Disable CORS attributes for scripts that don't support them: |
| 60 | + |
| 61 | +```ts |
| 62 | +useScript({ |
| 63 | + src: 'https://example.com/script.js', |
| 64 | + crossorigin: false, // Remove crossorigin attribute |
| 65 | + referrerpolicy: false, // Remove referrerpolicy attribute |
| 66 | +}) |
| 67 | +``` |
| 68 | + |
| 69 | +Or use a different `crossorigin` value: |
| 70 | + |
| 71 | +```ts |
| 72 | +useScript({ |
| 73 | + src: 'https://example.com/script.js', |
| 74 | + crossorigin: 'use-credentials', // Send cookies with request |
| 75 | +}) |
| 76 | +``` |
| 77 | + |
| 78 | +### Global Configuration |
| 79 | + |
| 80 | +Change defaults for all scripts: |
| 81 | + |
| 82 | +```ts [nuxt.config.ts] |
| 83 | +export default defineNuxtConfig({ |
| 84 | + scripts: { |
| 85 | + defaultScriptOptions: { |
| 86 | + crossorigin: false, |
| 87 | + referrerpolicy: false, |
| 88 | + } |
| 89 | + } |
| 90 | +}) |
| 91 | +``` |
| 92 | + |
| 93 | +## Crossorigin Values |
| 94 | + |
| 95 | +| Value | Cookies Sent | Error Details | Use Case | |
| 96 | +|-------|-------------|---------------|----------| |
| 97 | +| `anonymous` | No | Yes (if server supports) | Privacy-focused default | |
| 98 | +| `use-credentials` | Yes | Yes | Scripts requiring auth | |
| 99 | +| `false` | Yes | No | Scripts without CORS support | |
| 100 | + |
| 101 | +## Registry Scripts |
| 102 | + |
| 103 | +Many registry scripts already disable CORS attributes because the third-party doesn't support them: |
| 104 | + |
| 105 | +```ts |
| 106 | +// From useScriptStripe |
| 107 | +scriptInput: { |
| 108 | + src: 'https://js.stripe.com/basil/stripe.js', |
| 109 | + crossorigin: false, |
| 110 | + referrerpolicy: false, |
| 111 | +} |
| 112 | +``` |
| 113 | + |
| 114 | +Scripts with `crossorigin: false` include: |
| 115 | +- Stripe |
| 116 | +- YouTube Player |
| 117 | +- Google Sign-In |
| 118 | +- Google reCAPTCHA |
| 119 | +- Meta Pixel |
| 120 | +- TikTok Pixel |
| 121 | +- X (Twitter) Pixel |
| 122 | +- Snapchat Pixel |
| 123 | +- Cloudflare Web Analytics |
| 124 | +- Lemon Squeezy |
| 125 | +- Matomo Analytics |
| 126 | + |
| 127 | +If a registry script fails, check if CORS configuration needs adjustment. |
| 128 | + |
| 129 | +## Subresource Integrity |
| 130 | + |
| 131 | +When using [bundled scripts with SRI](/docs/guides/bundling#subresource-integrity-sri), `crossorigin="anonymous"` is required and automatically added: |
| 132 | + |
| 133 | +```ts [nuxt.config.ts] |
| 134 | +export default defineNuxtConfig({ |
| 135 | + scripts: { |
| 136 | + assets: { |
| 137 | + integrity: true, // Automatically sets crossorigin="anonymous" |
| 138 | + } |
| 139 | + } |
| 140 | +}) |
| 141 | +``` |
| 142 | + |
| 143 | +## Troubleshooting |
| 144 | + |
| 145 | +### Script Won't Load |
| 146 | + |
| 147 | +1. Check browser console for CORS errors |
| 148 | +2. Set `crossorigin: false` to disable CORS mode |
| 149 | +3. Verify the third-party server supports CORS headers |
| 150 | + |
| 151 | +### Script Loads but Broken |
| 152 | + |
| 153 | +1. The script may require cookies - try `crossorigin: 'use-credentials'` |
| 154 | +2. The script may need the referrer - set `referrerpolicy: false` |
| 155 | +3. Check if the script expects to be loaded without CORS attributes |
| 156 | + |
| 157 | +### Debugging External Script Errors |
| 158 | + |
| 159 | +To see full error messages from external scripts: |
| 160 | + |
| 161 | +1. Ensure the script has `crossorigin="anonymous"` |
| 162 | +2. Verify the server returns `Access-Control-Allow-Origin` header |
| 163 | +3. If the server doesn't support CORS, you won't get detailed errors |
| 164 | + |
| 165 | +### Bundling as Alternative |
| 166 | + |
| 167 | +If CORS issues persist, consider [bundling the script](/docs/guides/bundling) to serve it from your own domain, eliminating CORS entirely. |
0 commit comments