Open the console first - it usually names the cause
Before touching any setting, open DevTools with F12 (or Cmd+Option+I on Mac) and read the Console tab. A widget that fails to appear almost always leaves an error there, and the error normally names the exact cause.
Last verified: August 2026. The diagnostic sequence below applies to any embedded chat widget, not one product in particular.
Match what you see against this table and jump straight to the relevant section.
| Console output | Cause | Section |
|---|---|---|
| "Refused to load the script ... Content Security Policy" | CSP is blocking it | 1 |
404 or net::ERR_ on the widget script | Wrong URL, or a blocker | 2, 3 |
| Nothing at all, no widget request in Network | Script never ran - placement or caching | 4, 5 |
| Script loads 200, still nothing visible | CSS - z-index, overflow, or a container | 6 |
| "Uncaught TypeError" from another script | An earlier JS error halted execution | 7 |
Then check the Network tab, filter for the widget domain, and note whether the request appears at all. "Request never made" and "request made and blocked" are entirely different problems, and this distinction saves the most time of any single check.
1. Content Security Policy is blocking the script
The console message is unambiguous:
Refused to load the script 'https://widget.example.com/loader.js'
because it violates the following Content Security Policy directive:
"script-src 'self'"CSP restricts which domains may serve scripts, styles, images and connections. It is a genuinely valuable security control, and it blocks third-party widgets by default unless you allow them explicitly.
The four directives a chat widget usually needs
| Directive | Why the widget needs it |
|---|---|
script-src | Load the loader and the widget bundle |
connect-src | API calls and the WebSocket that carries live messages |
img-src | Avatars, uploaded images and icons |
style-src | Widget styling, often injected inline at runtime |
A partial allowance produces the most confusing symptom of all: the launcher appears but the conversation never connects, because script-src was allowed and connect-src was not. If your widget opens and then sits loading forever, check connect-src first - and remember WebSocket URLs use the wss: scheme, which must be permitted explicitly.
The 'unsafe-inline' problem
Many widgets inject inline <style> tags at runtime. Under a strict policy without 'unsafe-inline' in style-src, the browser blocks those styles and you get an unstyled or invisible widget - the script loaded successfully, so the console shows no script error and the failure looks like a rendering bug.
Where a nonce-based policy is in place, most vendors document a way to pass the nonce to the widget. Check the integration docs before weakening the policy globally.
Where the CSP actually comes from
Look in three places, because teams often edit one and miss the others: a Content-Security-Policy HTTP response header from your server or framework config, a <meta http-equiv="Content-Security-Policy"> tag in the document head, and your CDN or WAF - Cloudflare, Vercel and similar platforms can inject or modify headers independently of your application.
2. Ad blockers, privacy extensions and tracking protection
Many blocklists include chat and support widgets under "tracking", so a widget that is entirely functional simply never loads for a meaningful share of visitors.
Test in a private window with all extensions disabled. If the widget appears there and not in your normal browser, an extension is the cause. Also try a second browser: Brave blocks aggressively by default, and Safari's Intelligent Tracking Prevention affects some third-party storage patterns even without an extension installed.
You cannot fix this for your visitors, and that is worth accepting rather than fighting. What you can do is make sure the widget is not your only contact route - a visible email address, phone number or contact page means a blocked widget costs you a convenience rather than a conversation.
Note also that corporate networks frequently block unknown third-party domains at the firewall. If the widget works everywhere except at one client's office, that is why, and it is resolved by their IT team allowlisting the domain rather than by anything on your side.
3. The script is in the wrong place, or never runs
If the Network tab shows no request for the widget at all, the script never executed. Common reasons, in order:
Pasted outside the document
The snippet must sit inside <head> or <body>. Pasted after </html> - which happens easily in a CMS footer field - browsers may ignore it entirely.
An earlier JavaScript error stopped execution
Scripts run in order, and an uncaught error in an earlier script halts the rest of that block. If the console shows a TypeError from an unrelated script above your widget snippet, fix that first - the widget is a victim, not the cause. This is why the widget sometimes "stops working" after an unrelated site change.
Installed in a template that does not render on every page
Placing the snippet in a specific page template rather than the global layout means it appears on some routes only. Check whether it is missing on all pages or just some - the answer localises the problem immediately.
Single-page apps and route changes
In React, Vue, Next.js and similar frameworks, a widget initialised once on mount may not survive client-side navigation, or may double-initialise. The symptom is a widget present on first load that disappears after navigating. Initialise it at the application root rather than inside a page component, and guard against double-initialisation. Our guides on embedding a chatbot in React, Next.js, Vue and Angular cover the framework-specific patterns.
A tag manager that is not firing
If you installed via Google Tag Manager, the tag may be unpublished, restricted by a trigger condition, or blocked by consent mode. Check in GTM Preview whether the tag actually fires on the page you are testing - an unpublished container is the single most common GTM cause.
4. Caching - you are looking at an old page
You installed the widget, the code is correct, and the live site still shows the previous version. Between you and your HTML there may be several caches, and clearing one is rarely enough.
- Browser cache. Hard reload with Ctrl+Shift+R (Cmd+Shift+R on Mac), or test in a private window.
- CDN cache. Cloudflare, Fastly and similar hold HTML at the edge. Purge the cache from the provider dashboard.
- Platform or plugin cache. WordPress caching plugins, Shopify's theme cache, and static site builds all serve pre-rendered HTML. Clear the plugin cache and, for static sites, trigger a fresh build - editing the template is not enough if nothing rebuilt.
- Service worker. A PWA service worker can serve a cached shell indefinitely. Check Application -> Service Workers in DevTools and unregister it to test.
The fastest way to confirm caching is the culprit: view page source (Ctrl+U) and search for your widget's domain. If the snippet is not in the source, the browser never had a chance - the problem is upstream in your build or cache, not in the widget.
5. The cookie consent manager is blocking it
This one is easy to miss because everything about the installation is correct.
Consent platforms such as OneTrust, Cookiebot, Osano and Usercentrics work by holding back third-party scripts until the visitor consents. If your widget is categorised as "marketing" or "functional" and the visitor has not accepted that category, the script is deliberately never executed.
Two things to check:
- The category assigned to the widget. A support chat widget is usually genuinely necessary or functional rather than marketing, but consent scanners often auto-classify unknown third-party domains as marketing by default. Reclassifying it - where your legal position supports doing so - restores it for most visitors.
- Script blocking attributes. Consent managers frequently rewrite
<script>tags totype="text/plain"with adata-cookieconsentattribute until consent is given. Check the rendered HTML: if your widget tag hastype="text/plain", the consent manager is holding it and no browser will execute it.
Test by accepting all cookies in a fresh private window. If the widget appears only after acceptance, this is your cause. Treat the categorisation as a decision for whoever owns your privacy compliance rather than something to override quietly.
6. It loaded and it is invisible: the CSS causes
The script returns 200, no console errors, and still nothing. The widget is on the page and something in your CSS is hiding it. Confirm by searching the Elements panel for the widget's container element - if it exists in the DOM, this section is your answer.
z-index
The most common cause. A sticky header, a promotional banner, a cookie bar or a full-page overlay with a higher z-index renders on top. Widgets typically use a high value such as 999999, so a competing element needs an absurd one - and site-wide overlays often have exactly that.
Inspect the element sitting where the widget should be. If it is not your widget, that element is covering it.
A parent with overflow: hidden
A widget injected inside a container with overflow: hidden gets clipped out of view. Widgets are normally appended to <body> to avoid this, but a script that runs inside a component tree can end up nested. Check where the widget's element actually sits in the DOM.
Aggressive global CSS resets
Rules such as div { position: static !important; } or blanket * { } selectors carrying !important can override the widget's positioning. Third-party widgets normally scope their styles, but a sufficiently aggressive global rule wins anyway.
Mobile-specific hiding
If the widget shows on desktop and not on mobile, check for a media query hiding it - some themes hide floating elements below a breakpoint deliberately to protect the viewport. Also check the widget's own settings, since most offer a mobile visibility toggle that is easy to leave off.
Positioned off-screen
Custom offset settings that place the launcher 200px from the bottom work on a tall desktop viewport and push it out of a short mobile one. If the element exists with sensible dimensions but is not visible, check its computed bottom and right values against the viewport size.
7. Configuration: it is working exactly as configured
Finally, a category that is not a bug at all. Check these in the widget's own dashboard before spending more time in DevTools.
- Domain allowlist. Most widgets restrict which domains may load them. A widget configured for
example.comwill not load onstaging.example.com, onwww.example.comif only the bare domain was added, or on a preview deployment URL. This is by far the most common configuration cause. - Business hours. A widget set to appear only during business hours is invisible outside them, exactly as intended - and this is a favourite cause of "it works for me but not for our overseas team".
- Page targeting rules. Show/hide rules based on URL patterns can exclude more than intended, particularly when a rule was written for a path that has since changed.
- Bot paused or unpublished. A flow in draft, or paused during editing, may stop the widget rendering at all rather than showing an empty one.
- Time or scroll delay. A launcher configured to appear after 30 seconds or 50% scroll is not missing during the first few seconds of testing.
The domain allowlist deserves a specific test: compare the domain in the browser address bar, character for character, against the allowlist entry. www versus bare domain accounts for a surprising share of these.
The diagnostic sequence
Worked in this order, the cause is normally identified within a few minutes.
| # | Check | Tells you |
|---|---|---|
| 1 | View source, search for the widget domain | Absent = build or cache problem, not the widget |
| 2 | Console tab | CSP violations and JS errors name themselves |
| 3 | Network tab, filtered to the widget domain | Request absent vs blocked vs 200 |
| 4 | Private window, extensions off | Isolates ad blockers and stale cache together |
| 5 | Accept all cookies, then reload | Isolates the consent manager |
| 6 | Search Elements for the widget container | Present = CSS problem; absent = loading problem |
| 7 | Widget dashboard: domain, hours, targeting | Working as configured rather than broken |
Step 1 is the highest-value check in the list. If the snippet is not in the page source, nothing about the widget itself can be the cause, and you have eliminated six of the seven sections instantly.
8. It is loading, just not when you are looking
Some widgets are present and functional but appear late enough that a quick check concludes they are missing. Worth ruling out before deeper debugging.
Deferred loading
Many widgets deliberately delay initialisation until after the page's main content, to protect Core Web Vitals. On a slow connection that can be several seconds. Watch the Network tab rather than the viewport - if the request fires eventually, the widget is working and the question is whether the delay is acceptable.
Lazy-loading tied to interaction
Some installations only load the widget after first scroll, mouse movement or a timer. If you load the page and immediately screenshot it, you will conclude it is broken. Scroll, wait, and move the mouse before judging.
Slow third-party responses
If the widget request is pending for many seconds, the vendor's edge may be slow or degraded from your region. Check the vendor status page. This is also worth measuring: a widget that consistently takes 4 seconds to appear is costing you conversations even though it technically works.
The performance trade to be aware of
Widgets are third-party JavaScript and they have a cost - typically some main-thread work and a WebSocket connection. If Lighthouse flags your chat widget, the answer is usually to defer it rather than remove it, but check that your deferral is not so aggressive that visitors leave before it appears. Measure the actual time-to-launcher on a throttled connection and decide deliberately.
9. Platform-specific traps: Shopify, WordPress, Wix, Webflow
Hosted platforms each have a characteristic way of losing your script.
Shopify
Code added to theme.liquid is lost when the theme is updated or replaced, which is the classic "the widget disappeared and nobody changed anything" report. Prefer a theme app extension or the script tag API where available. Also note that the checkout pages are separate and generally do not run theme code - a widget present on product pages and absent at checkout is expected behaviour, not a bug.
WordPress
Three things interfere routinely. Caching plugins serve stale HTML, so purge after installing. JavaScript minification and combination features in performance plugins can mangle third-party snippets - exclude the widget script from optimisation. And theme updates overwrite edits made directly to header.php or footer.php; use a child theme or a code-insertion plugin so the snippet survives.
Wix
Custom code is added through Settings rather than by editing pages, and each snippet has an explicit page-scope setting - "all pages" is not always the default. Wix also loads custom code inside a sandboxed context in some configurations, which can affect widgets expecting direct DOM access.
Squarespace and Webflow
Both distinguish site-wide code injection from per-page code, and both require a republish for changes to reach the live site. Editing the injection field and not publishing is the single most common cause of a widget that is "installed" and absent. On Webflow, also check that you added the code to the correct project and to the published domain rather than only the staging subdomain.
A general rule
On every one of these platforms, the definitive test is the same: load the live URL, view source, and search for the widget domain. If it is not there, the platform did not publish it, and no amount of widget-side configuration will change that. Platform-specific installation steps are covered in our guides for Shopify, WordPress, Wix, Squarespace and Webflow.
Installing a widget that stays installed
Most of the failures above are environmental rather than product-specific - CSP policies, consent managers, caching layers and z-index wars affect every embedded widget equally. What differs between products is how much they help you diagnose them.
Conferbot's website widget installs with a single script tag on Shopify, WordPress, Wix, Squarespace, Webflow and plain HTML, with framework guides for React and Next.js and native SDKs for iOS, Android, React Native and Flutter when a web widget is not the right surface. The same conversation flow also runs on WhatsApp and Telegram, which matters more than it sounds: a visitor whose browser blocks third-party widgets can still reach the same bot on a channel their browser has no opinion about.
Start free with Conferbot - 600 conversations a month, no credit card required.
Debugging right now? Start with step 1. Whether the snippet appears in view-source decides which half of this guide you need, and it takes ten seconds.
Was this article helpful?
Build and deploy in 10 minutes. No coding needed.
Chat Widget Not Showing on Your Website? Work These 11 Causes in Order FAQ
Everything you need to know about chatbots for chat widget not showing on your website? work these 11 causes in order.
About the Author
The Conferbot team writes about building, deploying, and improving AI chatbots.
View all articles