> ## Documentation Index
> Fetch the complete documentation index at: https://docs.glood.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Content Security Policy Setup

> Configure CSP to enable Glood pixel tracking and event transmission

# Content Security Policy Setup

To enable Glood pixel tracking, you need to configure your Content Security Policy (CSP) to whitelist Glood's domains. This allows the SDK to send analytics events to Glood endpoints.

## Required CSP Configuration

Add the following Glood domains to your `connectSrc` directive in your CSP configuration:

```javascript theme={null}
const {nonce, header, NonceProvider} = createContentSecurityPolicy({
  shop: {
    checkoutDomain: context.env.PUBLIC_CHECKOUT_DOMAIN,
    storeDomain: context.env.PUBLIC_STORE_DOMAIN,
  },
  connectSrc: [
    'https://cdn.shopify.com',
    'https://*.shopifycloud.com',
    'https://events.glood.ai',
    'https://s-pixel.glood.ai',
    'https://w-pixel.glood.ai'
  ],
});
```

## Glood Domains Explained

### Required Pixel Endpoints

| Domain                     | Purpose                        | Used By             |
| -------------------------- | ------------------------------ | ------------------- |
| `https://events.glood.ai`  | Recommendations pixel tracking | Recommendations app |
| `https://s-pixel.glood.ai` | Search analytics tracking      | Search app          |
| `https://w-pixel.glood.ai` | Wishlist analytics tracking    | Wishlist app        |

### Why These Domains Are Needed

* **Event Transmission**: The SDK sends analytics events to these endpoints via `fetch()` API calls
* **Privacy Compliance**: Events are only sent when customer consent is granted
* **Performance Tracking**: Enables monitoring of user interactions and e-commerce metrics
* **Personalization**: Allows Glood to provide personalized recommendations and search results

## Implementation in Hydrogen

### 1. Root Layout Configuration

Add the CSP configuration to your root layout file:

```typescript theme={null}
// app/root.tsx
import {
  json,
  type LoaderFunctionArgs,
} from '@shopify/remix-oxygen';
import {
  Links,
  Meta,
  Outlet,
  Scripts,
  ScrollRestoration,
  useLoaderData,
} from '@remix-run/react';
import {
  Analytics,
  getShopAnalytics,
  useNonce,
} from '@shopify/hydrogen';
import { createContentSecurityPolicy } from '@shopify/hydrogen';

export async function loader({context}: LoaderFunctionArgs) {
  const {nonce, header, NonceProvider} = createContentSecurityPolicy({
    shop: {
      checkoutDomain: context.env.PUBLIC_CHECKOUT_DOMAIN,
      storeDomain: context.env.PUBLIC_STORE_DOMAIN,
    },
    connectSrc: [
      'https://cdn.shopify.com',
      'https://*.shopifycloud.com',
      'https://events.glood.ai',         // Recommendations pixel
      'https://s-pixel.glood.ai',        // Search pixel
      'https://w-pixel.glood.ai'         // Wishlist pixel
    ],
  });

  return json({
    nonce,
    header,
    // ... other loader data
  });
}

export default function App() {
  const nonce = useNonce();
  const data = useLoaderData<typeof loader>();

  return (
    <html lang="en">
      <head>
        <meta charSet="utf-8" />
        <meta name="viewport" content="width=device-width,initial-scale=1" />
        <Meta />
        <Links />
      </head>
      <body>
        <Analytics>
          <GloodProvider client={glood} loaderData={data}>
            <Layout>
              <Outlet />
            </Layout>
          </GloodProvider>
        </Analytics>
        <ScrollRestoration nonce={nonce} />
        <Scripts nonce={nonce} />
      </body>
    </html>
  );
}

export function headers({loaderHeaders}: {loaderHeaders: Headers}) {
  return {
    'Content-Security-Policy': loaderHeaders.get('Content-Security-Policy'),
  };
}
```

### 2. Environment-Specific Configuration

Configure different endpoints for different environments:

```typescript theme={null}
export async function loader({context}: LoaderFunctionArgs) {
  const isDevelopment = context.env.NODE_ENV === 'development';
  const isStaging = context.env.NODE_ENV === 'staging';

  const gloodDomains = isDevelopment
    ? [
        'https://dev-events.glood.ai',
        'https://dev-s-pixel.glood.ai',
        'https://dev-w-pixel.glood.ai'
      ]
    : isStaging
    ? [
        'https://staging-events.glood.ai',
        'https://staging-s-pixel.glood.ai',
        'https://staging-w-pixel.glood.ai'
      ]
    : [
        'https://events.glood.ai',
        'https://s-pixel.glood.ai',
        'https://w-pixel.glood.ai'
      ];

  const {nonce, header, NonceProvider} = createContentSecurityPolicy({
    shop: {
      checkoutDomain: context.env.PUBLIC_CHECKOUT_DOMAIN,
      storeDomain: context.env.PUBLIC_STORE_DOMAIN,
    },
    connectSrc: [
      'https://cdn.shopify.com',
      'https://*.shopifycloud.com',
      ...gloodDomains
    ],
  });

  return json({ nonce, header });
}
```

### 3. Custom Domain Configuration

If you're using custom Glood endpoints:

```typescript theme={null}
export async function loader({context}: LoaderFunctionArgs) {
  const customEndpoints = [
    context.env.GLOOD_RECOMMENDATIONS_PIXEL_ENDPOINT,
    context.env.GLOOD_SEARCH_PIXEL_ENDPOINT,
    context.env.GLOOD_WISHLIST_PIXEL_ENDPOINT,
  ].filter(Boolean); // Remove undefined values

  const {nonce, header, NonceProvider} = createContentSecurityPolicy({
    shop: {
      checkoutDomain: context.env.PUBLIC_CHECKOUT_DOMAIN,
      storeDomain: context.env.PUBLIC_STORE_DOMAIN,
    },
    connectSrc: [
      'https://cdn.shopify.com',
      'https://*.shopifycloud.com',
      ...customEndpoints
    ],
  });

  return json({ nonce, header });
}
```

## CSP Directives Explained

### connectSrc

The `connectSrc` directive controls which URLs can be loaded using script interfaces:

* **fetch()** - Used by the SDK to send pixel events
* **XMLHttpRequest** - Alternative HTTP request method
* **WebSocket** - Real-time connections (not used by Glood)
* **EventSource** - Server-sent events (not used by Glood)

### Why Not Other Directives?

| Directive   | Not Required | Reason                                       |
| ----------- | ------------ | -------------------------------------------- |
| `scriptSrc` | ❌            | Glood doesn't load external scripts          |
| `imgSrc`    | ❌            | Glood doesn't load tracking pixels as images |
| `frameSrc`  | ❌            | Glood doesn't embed iframes                  |
| `mediaSrc`  | ❌            | Glood doesn't load media files               |

## Troubleshooting CSP Issues

### 1. Check Console Errors

CSP violations appear in the browser console:

```
Content Security Policy: The page's settings blocked the loading of a resource at https://events.glood.ai/
```

### 2. Verify Domain Configuration

Ensure all Glood domains are included:

```typescript theme={null}
// ❌ Missing domains will cause CSP violations
connectSrc: [
  'https://cdn.shopify.com',
  'https://events.glood.ai', // Only recommendations pixel
  // Missing search and wishlist pixels!
],

// ✅ All required domains included
connectSrc: [
  'https://cdn.shopify.com',
  'https://*.shopifycloud.com',
  'https://events.glood.ai',      // Recommendations
  'https://s-pixel.glood.ai',     // Search
  'https://w-pixel.glood.ai'      // Wishlist
],
```

### 3. Test with Debug Mode

Enable debug mode to see pixel transmission attempts:

```typescript theme={null}
const glood = createGlood({
  apiKey: process.env.GLOOD_API_KEY!,
  myShopifyDomain: 'your-store.myshopify.com',
  debug: true, // Enable debug logging
});
```

Debug logs will show:

```
[Glood Debug] Sending event to https://events.glood.ai (attempt 1)
[Glood Debug] Successfully sent event to https://events.glood.ai
```

If CSP is blocking requests, you'll see network errors instead.

### 4. Validate CSP Headers

Check that CSP headers are being sent:

```bash theme={null}
# Check CSP header in response
curl -I https://your-store.com | grep -i content-security-policy

# Expected output:
Content-Security-Policy: default-src 'self'; connect-src 'self' https://cdn.shopify.com https://*.shopifycloud.com https://events.glood.ai https://s-pixel.glood.ai https://w-pixel.glood.ai;
```

## Advanced CSP Configuration

### 1. Strict CSP with Nonces

Use nonces for enhanced security:

```typescript theme={null}
const {nonce, header, NonceProvider} = createContentSecurityPolicy({
  shop: {
    checkoutDomain: context.env.PUBLIC_CHECKOUT_DOMAIN,
    storeDomain: context.env.PUBLIC_STORE_DOMAIN,
  },
  defaultSrc: ["'none'"],
  scriptSrc: [`'nonce-${nonce}'`, "'strict-dynamic'"],
  connectSrc: [
    "'self'",
    'https://cdn.shopify.com',
    'https://*.shopifycloud.com',
    'https://events.glood.ai',
    'https://s-pixel.glood.ai',
    'https://w-pixel.glood.ai'
  ],
  styleSrc: [`'nonce-${nonce}'`, "'unsafe-inline'"],
  imgSrc: ["'self'", "data:", "https:"],
});
```

### 2. Report-Only Mode

Test CSP changes without blocking requests:

```typescript theme={null}
const {nonce, header, NonceProvider} = createContentSecurityPolicy({
  shop: {
    checkoutDomain: context.env.PUBLIC_CHECKOUT_DOMAIN,
    storeDomain: context.env.PUBLIC_STORE_DOMAIN,
  },
  connectSrc: [
    'https://cdn.shopify.com',
    'https://*.shopifycloud.com',
    'https://events.glood.ai',
    'https://s-pixel.glood.ai',
    'https://w-pixel.glood.ai'
  ],
  reportOnly: true, // Report violations without blocking
});
```

### 3. Domain Wildcards

Use wildcards for subdomain flexibility:

```typescript theme={null}
connectSrc: [
  'https://cdn.shopify.com',
  'https://*.shopifycloud.com',
  'https://*.glood.ai', // Wildcard for all Glood subdomains
],
```

⚠️ **Note**: Wildcards are less secure than explicit domain lists.

## Security Considerations

### 1. Principle of Least Privilege

Only include domains that are actually needed:

```typescript theme={null}
// ✅ Good - Only required domains
connectSrc: [
  'https://events.glood.ai',
  'https://s-pixel.glood.ai',
  'https://w-pixel.glood.ai'
],

// ❌ Avoid - Overly permissive
connectSrc: [
  'https://*.glood.ai',    // Too broad
  'https://*',             // Far too broad
  '*'                      // Completely insecure
],
```

### 2. Environment Separation

Use different domains for different environments:

```typescript theme={null}
// Development: Use dev domains
'https://dev-events.glood.ai'

// Staging: Use staging domains
'https://staging-events.glood.ai'

// Production: Use production domains
'https://events.glood.ai'
```

### 3. Regular Review

* **Review CSP regularly** to ensure it's still appropriate
* **Remove unused domains** when disabling Glood apps
* **Monitor CSP violations** in production
* **Test changes** in staging environments first

## Integration Checklist

* [ ] Add Glood domains to `connectSrc` directive
* [ ] Include all required pixel endpoints:
  * [ ] `https://events.glood.ai` (recommendations)
  * [ ] `https://s-pixel.glood.ai` (search)
  * [ ] `https://w-pixel.glood.ai` (wishlist)
* [ ] Configure environment-specific domains if needed
* [ ] Test CSP with debug mode enabled
* [ ] Verify no CSP violations in browser console
* [ ] Monitor pixel transmission success in debug logs
* [ ] Deploy CSP headers correctly
* [ ] Test in production environment

## Common Issues

### Issue: CSP Blocking Pixel Requests

**Symptoms**: Network errors, no pixel events sent
**Solution**: Add missing Glood domains to `connectSrc`

### Issue: CSP Header Not Applied

**Symptoms**: CSP violations not shown, headers missing
**Solution**: Ensure CSP headers are returned from loader and applied in headers function

### Issue: Wrong Environment Domains

**Symptoms**: 404 errors on pixel endpoints
**Solution**: Verify correct domains for current environment

### Issue: Wildcard Domain Issues

**Symptoms**: Inconsistent blocking behavior
**Solution**: Use explicit domain lists instead of wildcards

## Example Implementation

Complete working example:

```typescript theme={null}
// app/root.tsx
import {json, type LoaderFunctionArgs} from '@shopify/remix-oxygen';
import {Analytics, createContentSecurityPolicy} from '@shopify/hydrogen';
import {GloodProvider, createGlood} from '@glood/hydrogen';
import {recommendations, search, wishlist} from '@glood/hydrogen';

const glood = createGlood({
  apiKey: process.env.GLOOD_API_KEY!,
  myShopifyDomain: process.env.PUBLIC_STORE_DOMAIN!,
  debug: process.env.NODE_ENV === 'development',
})
  .use(recommendations())
  .use(search())
  .use(wishlist());

export async function loader({context}: LoaderFunctionArgs) {
  const {nonce, header} = createContentSecurityPolicy({
    shop: {
      checkoutDomain: context.env.PUBLIC_CHECKOUT_DOMAIN,
      storeDomain: context.env.PUBLIC_STORE_DOMAIN,
    },
    connectSrc: [
      'https://cdn.shopify.com',
      'https://*.shopifycloud.com',
      'https://events.glood.ai',
      'https://s-pixel.glood.ai',
      'https://w-pixel.glood.ai'
    ],
  });

  return json({
    nonce,
    header,
    analytics: getShopAnalytics({
      storefront: context.storefront,
      publicStorefrontId: context.env.PUBLIC_STOREFRONT_ID,
    }),
  });
}

export default function App() {
  const {analytics} = useLoaderData<typeof loader>();

  return (
    <html>
      <head>
        <Meta />
        <Links />
      </head>
      <body>
        <Analytics analytics={analytics}>
          <GloodProvider client={glood} loaderData={data}>
            <Outlet />
          </GloodProvider>
        </Analytics>
        <Scripts />
      </body>
    </html>
  );
}

export function headers({loaderHeaders}: {loaderHeaders: Headers}) {
  return {
    'Content-Security-Policy': loaderHeaders.get('Content-Security-Policy'),
  };
}
```

This configuration ensures that:

* ✅ All Glood pixel endpoints are whitelisted
* ✅ CSP headers are properly applied
* ✅ Pixel tracking works correctly
* ✅ Security is maintained with explicit domain lists

## See Also

* [Event System](/for-developers/glood-hydrogen-sdk/api-reference/event-system) - Understanding pixel transmission
* [App Modules](/for-developers/glood-hydrogen-sdk/api-reference/app-modules) - App-specific pixel endpoints
* [Installation Guide](/for-developers/glood-hydrogen-sdk/installation) - Complete setup instructions
* [Hydrogen CSP Documentation](https://shopify.dev/docs/custom-storefronts/hydrogen/deployment/content-security-policy) - Official Shopify CSP guide
