Creating a client-side diagnostics site

Namito YokotaNamito Yokota
5 min read

Debugging issues with clients of an enterprise software solution often feels like chasing the wind. Your team release a new feature and receive great feedback from many customers except one: reports missing icons, broken layouts, or the entire page not loading. The culprit for my scenario was an overzealous firewall or content filter silently blocking third-party resources. After spending a week going back and forth with IT (with the client as the middle man), I decided to build a simple, client-side diagnostics page to check which URLs are blocked and help IT teams know exactly what needs to be whitelisted.

Acceptance Criteria

Before brainstorming solutions, let’s first set some ground rules:

  • Client-Side: The page must be fully static and run entirely in the browser — no backend, no server-side logic. This ensures it’s easy to host anywhere and works in restrictive environments.

  • Simple Deployment: There should be no build process or bundling step required. Just pure HTML, CSS, and vanilla JavaScript — drop the files into a server and it should just work.

  • Maintainable: While the ideal setup would involve fetching the list of resources from an external API, the initial implementation can use a hardcoded array of URLs. This keeps the MVP simple and testable.

  • Reporting: For each URL, the tool should clearly and accurate indicate whether it is accessible. The status must be visually obvious (e.g., ✅, ❌, ⏳).

  • Secure: Any method used to check URLs must not interfere with the styling, behavior, or security of the page itself. This includes isolating loaded scripts or styles to avoid accidental side effects.

3 Possible Approaches

1. fetch()

The fetch() approach is a straightforward way to check if a URL is reachable, but its behavior depends on CORS settings. With standard fetch(), the browser requires the server to allow cross-origin requests via CORS headers; if not, the request will fail—even if the URL is actually reachable. To work around this, using fetch() with mode: 'no-cors' lets the request go through silently without needing CORS approval. While the response is opaque and doesn’t give detailed status codes, it still indicates whether the resource was blocked entirely or not. This makes the no-cors method a practical choice for checking basic reachability across different types of assets.

2. DOM Injection

The DOM injection approach works by dynamically adding <script>, <link>, or <img> elements to the page and watching for their onload or onerror events. If the resource loads successfully, it’s likely not blocked; if it fails, the browser will fire an error event, signaling that it might be blocked by a firewall or content filter. This method gives a more accurate picture of whether the resource can actually be fetched and used by the browser. It's especially useful for fonts, stylesheets, and JavaScript files, though care must be taken to avoid interfering with the existing page layout or behavior.

3. Using <iframe>

The <iframe> approach involves embedding an external URL inside a hidden or sandboxed iframe and observing whether it loads successfully. If the iframe triggers a load event, the resource is likely accessible; if not, it may be blocked. While this method can detect general reachability, it’s less precise for specific assets like fonts or scripts. It’s best used for checking full websites or dashboards rather than individual files. Additionally, many sites block iframe embedding using headers like X-Frame-Options, which can limit its effectiveness.

Implementation

To keep things lightweight and avoid CORS headaches, I chose to implement the diagnostics page using the fetch() API with mode: 'no-cors'. This approach doesn't give you access to the actual response content or status code, but it does let you know whether the browser was able to reach the resource at all—which is perfect for detecting firewall blocks. For each URL I want to check, I make a HEAD request in no-cors mode, and if the request completes without throwing an error, I consider that a success. While the response object is opaque and always returns ok: false, the real signal is whether the .catch() block is triggered. If it throws, the resource is most likely blocked; if it doesn't, the URL is accessible.

This approach lets the diagnostics tool focus purely on reachability, which is usually what matters when dealing with IT departments and URL whitelisting. I wrapped each check in a small promise and updated the UI with a status icon: a green check for success, a red “x” for failure, and a spinner while the check is in progress. Since the diagnostics page is entirely client-side, there are no privacy concerns, no server logs, and no CORS configuration required. It works from any browser and doesn’t require installation — just load the page and instantly see which URLs are blocked. This approach gave me the right balance of simplicity, reliability, and ease of deployment.

This is the heart of the logic, using fetch() in no-cors mode:

function checkUrlAccessibility(url) {
  return new Promise((resolve) => {
    fetch(url, {
      method: 'HEAD',
      mode: 'no-cors',
    })
      .then(() => resolve(true))
      .catch(() => resolve(false));
  });
}

Looping through an array of resources to check each one and update the UI accordingly:

const resources = [
  { name: 'Google Fonts', url: '...' },
  { name: 'Font Awesome', url: '...' },
  { name: 'Bootstrap', url: '...' }
];

resources.forEach(resource => {
  showLoadingIcon(resource.name);
  checkUrlAccessibility(resource.url).then(isAccessible => {
    updateStatusIcon(resource.name, isAccessible);
  });
});

Testing

To test whether the diagnostics page correctly detected blocked resources, there are a few simple options — such as using intentionally breaking URLs, using a browser extension, or modifying the hosts file to simulate a DNS failure. All of these approaches are easy ways to trigger errors without requiring a significant time to set up.

However, the best approach I found was to use the browser’s DevTools. By manually blocking specific domains in the Network tab, I could mimic a client's firewall filtering out resources. This allowed me to confirm that the diagnostics page correctly showed failures without needing to set up a real firewall or proxy. With everything working as expected, the tool was ready to hand off to client IT teams for real-world testing.

0
Subscribe to my newsletter

Read articles from Namito Yokota directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Namito Yokota
Namito Yokota