Skip to content

Content Security Policy

If your site uses Content Security Policy (CSP), you need to allow specific domains and rules for the Feedback Widget to work correctly.

A typical first symptom is an error like this in the browser console, with no launcher button on the page:

Loading the script 'https://widget.ybug.io/button/XXXXXXXXX.js' violates the following
Content Security Policy directive: "script-src 'self' 'unsafe-inline'".

Example CSP setup

This policy covers everything the widget does. Merge it with your existing directives, keeping your own sources in place:

script-src  'self' 'unsafe-inline' https://widget.ybug.io https://app.ybug.io;
style-src   'self' 'unsafe-inline' https://fonts.googleapis.com;
img-src     'self' data: blob: https://*.ybug.io;
media-src   'self' blob:;
connect-src 'self' https://app.ybug.io https://*.s3.eu-central-1.amazonaws.com;
font-src    'self' data: https://fonts.gstatic.com;

You can send the policy as a Content-Security-Policy HTTP response header, or apply it with a <meta> tag in your HTML:

html
<meta http-equiv="Content-Security-Policy" content="
  script-src 'self' 'unsafe-inline' https://widget.ybug.io https://app.ybug.io;
  style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
  img-src 'self' data: blob: https://*.ybug.io;
  media-src 'self' blob:;
  connect-src 'self' https://app.ybug.io https://*.s3.eu-central-1.amazonaws.com;
  font-src 'self' data: https://fonts.gstatic.com;
">

unsafe-eval is not needed

The widget contains no eval() or new Function() calls. If 'unsafe-eval' is in your policy because of Ybug, you can safely remove it.

Why two domains are needed

The widget uses two hostnames, and both have to be allowed in script-src:

  • widget.ybug.io serves the launcher script from your installation snippet.
  • app.ybug.io serves the widget application itself, its lazy-loaded chunks (screen recorder, language files) and our API.

The widget renders itself into blank iframes that it creates in your page. Such iframes inherit the CSP of the parent document, so everything the widget loads inside them, including the application bundle from app.ybug.io, is still governed by your policy. Allowing only widget.ybug.io gets you the launcher button, but the widget will fail to open.

Rules

  • script-src — both Ybug domains, plus permission for the installation snippet itself, which is an inline <script> tag. Allow it with 'unsafe-inline' or, more securely, with a nonce (see Using a nonce below).
  • style-src — the widget injects inline styles, so 'unsafe-inline' is required. Google Fonts also need to be whitelisted.
  • img-src — icons and images served from our domain, data: for the rendered screenshot, and blob: for attachment thumbnails.
  • media-srcblob: is used to preview a recorded video before the report is sent.
  • connect-src — API calls to our backend, and the S3 bucket that screenshots, videos and attachments are uploaded to.
  • font-src — Google Fonts, and data: for fonts that get embedded into the screenshot.

Screenshots and your own domains

To capture the page exactly as your visitor sees it, the widget re-downloads the fonts and images used on your page so it can embed them into the screenshot. If your connect-src does not include your own asset domains (your CDN, your font provider), the widget still works, but screenshots may render with fallback fonts or missing images.

Using a nonce

If your policy cannot include 'unsafe-inline' for scripts, generate a random nonce on every request and authorize the installation snippet with it:

html
<script type="text/javascript" nonce="THISISASECRETNONCE">
// ... ybug script goes here
</script>
script-src 'self' 'nonce-THISISASECRETNONCE' https://widget.ybug.io https://app.ybug.io;

Replace THISISASECRETNONCE with a secure, dynamic value generated by your server for each request.

The widget also loads its own scripts and styles dynamically, and those are covered by the widget.ybug.io and app.ybug.io sources above. If your policy does not allowlist hosts at all, or if it uses 'strict-dynamic' (which makes browsers ignore host sources), pass the same nonce to the widget as well:

html
<script type="text/javascript" nonce="THISISASECRETNONCE">
window.ybug_settings = {
    id: 'XXXXXXXXX',
    nonce: 'THISISASECRETNONCE',
};
// ... rest of the ybug script
</script>

The widget then stamps the nonce onto every script and stylesheet it creates.

WARNING

Even with a nonce, style-src still requires 'unsafe-inline'. Some of the widget's launcher styles are injected before the nonce is applied.