Troubleshooting Client Access

Lesson 9 — Troubleshooting Client Access | Firefox DevTools
Lesson 9 of 11 · Scenario

Troubleshooting Client Access

Firefox Developer Tools · CORS · Mixed Content · TLS · DNS · Firewall · Proxy
A user can't reach your web application. This lesson applies the tools from lessons 1–8 to diagnose exactly why: CORS enforcement, mixed content blocks, certificate errors, DNS failures, firewall timeouts, and proxy interference. Each failure type has a distinct fingerprint in DevTools.

The Diagnostic Mindset

Before reaching for DevTools, answer two questions: what exactly is failing (a whole page? one API call? just images?) and is this affecting all users or just one? A problem isolated to one user on a corporate network is almost always a firewall or proxy issue — not your code.

Open DevTools with F12, go to the Network panel, and press Escape to open the Console drawer. Most access failures produce a red Console error, a red Network entry, or both.

Step 1 — Read the Console Error

The Console almost always names the problem category before you click a single request. Here is a representative set of errors:

Console Network Debugger
🔴 Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://api.other.com/data. (Reason: CORS header 'Access-Control-Allow-Origin' missing).
🟡 Mixed Content: The page at 'https://shop.example.com' was loaded over a secure connection, but is attempting to access 'http://cdn.example.com/logo.png'. This content should be served over HTTPS.
🔴 NS_ERROR_UNKNOWN_HOST — The address wasn't understood. Firefox doesn't know how to connect to the server at api.bad-domain.local.
🔴 NS_ERROR_NET_TIMEOUT — The connection to the server at 192.168.10.5 was reset while the page was loading.
🔴 SEC_ERROR_UNKNOWN_ISSUER — The certificate is not trusted because the issuer certificate is unknown. Error code: MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT
Console errorCategory
CORS header 'Access-Control-Allow-Origin' missingCORS — missing server header
Mixed Content: … attempted to access 'http://…'Mixed content — HTTP on HTTPS page
NS_ERROR_UNKNOWN_HOSTDNS — domain cannot be resolved
NS_ERROR_CONNECTION_REFUSEDNetwork — port is closed or nothing is listening
NS_ERROR_NET_TIMEOUTNetwork — firewall dropping packets silently
SEC_ERROR_UNKNOWN_ISSUER / SSL_ERROR_BAD_CERT_DOMAINTLS — invalid or mismatched certificate
ERR_TUNNEL_CONNECTION_FAILEDProxy — CONNECT tunnel failed

Step 2 — CORS Errors

CORS (Cross-Origin Resource Sharing) is a browser security policy. When JavaScript at https://app.example.com tries to read a response from https://api.other.com, the browser first checks whether the server sent permission headers. If not, the browser suppresses the response — even though it was received.

🔑 Key point CORS is enforced by the browser, not the server. The request reaches the server and the server responds — the browser just refuses to hand the response to your JavaScript if the permission headers are missing.

How a CORS request flows

1
Browser sends the request
OPTIONS https://api.other.com/data (preflight)
Origin: https://app.example.com
Access-Control-Request-Headers: Authorization
2a
Server responds — but without CORS headers
HTTP/1.1 200 OK
Content-Type: application/json
⚠ Access-Control-Allow-Origin: (missing)
✖ Browser blocks the response. Console shows CORS error.
2b
Server responds — with correct CORS headers
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true
✔ Browser passes the response to JavaScript.

Investigating CORS in the Network panel

Click the blocked request → Response Headers tab. Look for:

Response Headers — api.other.com/data
Content-Type: application/json
Access-Control-Allow-Origin: — (not present) ← this is the problem
What it should look like
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true

CORS reason codes in the Console

Reason codeMeaning
CORS header 'Access-Control-Allow-Origin' missingNo header at all — add it on the server
CORS header 'Access-Control-Allow-Origin' does not matchWrong origin value — update the allowed-origins list
Credential is not supported if the CORS header … is '*'Wildcard + credentials: 'include' conflict — use a specific origin
CORS preflight response did not succeedOPTIONS returned non-2xx — check your OPTIONS handler
CORS header 'Access-Control-Allow-Headers' is missingAuthorization or a custom header not listed in Allow-Headers

Step 3 — Mixed Content

An HTTPS page that loads any sub-resource over HTTP is called mixed content. The browser blocks active mixed content (scripts, fetch requests, iframes) entirely. Passive mixed content (images, audio, video) may load but breaks the padlock icon.

Active Mixed Content (Blocked)

fetch('http://…') on HTTPS page

Scripts, XHR/Fetch calls, iframes. The browser stops the request before it leaves the machine. Status shows -- in the Network panel.

Passive Mixed Content (Warning)

<img src="http://…"> on HTTPS page

Images, audio, video. May load but breaks the padlock icon and generates a Console warning. Will be blocked in future browser versions.

Finding mixed content fast

In the Network panel, press Ctrl + F to open the search bar. Type http:// — this searches request URLs and response bodies across all requests. Any hit that is an HTTP URL on an HTTPS page is a candidate for the block.

Network 🔍 http://
Status
Method
URL
Type
Time
--
GET
http://cdn.example.com/analytics.js
script
blocked
200
GET
http://old-cdn.com/logo.png
img
42ms

Step 4 — TLS / Certificate Errors

TLS errors prevent the browser from establishing a secure connection. They are visible on the error page Firefox shows instead of the content, and in the Security tab of Network request details.

Error codeMeaning
SEC_ERROR_EXPIRED_CERTIFICATECertificate is past its Not After date — renew it
SEC_ERROR_UNKNOWN_ISSUERSelf-signed cert or private CA — browser doesn't trust the issuer
SSL_ERROR_BAD_CERT_DOMAINCertificate CN/SAN doesn't match the domain in the URL
SSL_ERROR_RX_RECORD_TOO_LONGHTTP traffic sent to an HTTPS port — server misconfiguration
MOZILLA_PKIX_ERROR_SELF_SIGNED_CERTSelf-signed certificate — not issued by a trusted CA

Checking certificate details

Click the lock icon in the address bar → Connection SecureMore Information. The Security panel shows the issuer, validity period, and SANs. For sub-resource TLS errors: click the failing request in the Network panel → Security tab.

Certificate Details — example.com
Issued by: Let's Encrypt Authority X3
Valid from: 2025-01-01 to 2025-04-01
Valid now: ⚠ EXPIRED — today is 2026-06-08
Subject Alt Names: example.com, www.example.com
🔧 Local development tip Self-signed certs are common in dev. To avoid clicking through warnings constantly, use mkcert to create a local CA. Import the CA root into Firefox via Settings → Privacy & Security → Certificates → View Certificates → Import. Firefox will then trust all certs issued by that CA.

Step 5 — DNS Failures

DNS failure means the browser couldn't resolve the domain name to an IP address. Firefox shows its "Server not found" page. In DevTools, the failing request shows -- status, and the Timings tab shows only a failed DNS Lookup phase.

Timings — DNS Failure scenario
DNS Lookup
5200ms ✖
Connecting
TLS Setup
Waiting (TTFB)
Receiving

DNS failures are almost always environmental — DNS filtering on a corporate network, a domain that hasn't propagated yet, or an expired domain. They look similar to firewall blocks in DevTools. To tell them apart, run nslookup api.example.com in a terminal — DNS failure returns Non-existent domain; a firewall block resolves successfully but the TCP connection never completes.

Step 6 — Network / Firewall Blocks

A firewall block means DNS succeeded but the TCP connection was refused or dropped. The Timings tab distinguishes the two failure modes:

Connection Refused — fast fail
DNS Lookup
12ms
Connecting
0ms ✖
Port closed; host sent TCP RST immediately. NS_ERROR_CONNECTION_REFUSED
Firewall Timeout — slow fail
DNS Lookup
12ms
Connecting
30s ✖
Firewall drops packets without RST; browser waits until timeout. NS_ERROR_NET_TIMEOUT
ℹ Also check: Is the Network panel's throttling dropdown set to something slow (GPRS, Regular 3G)? Slow throttling produces long Connecting times that look like firewall issues but aren't. Check the toolbar before assuming a block.

Step 7 — Proxy Issues

A proxy sits between the browser and the internet. Corporate networks commonly use proxies for compliance and security. Proxy involvement is identified by response headers and specific HTTP status codes.

Identifying proxy headers

Response Headers — suspect a proxy?
Via: 1.1 corporate-proxy-01 (squid/5.3) ← proxy confirmed
X-Cache: HIT from corporate-proxy-01 ← served from cache
X-Forwarded-For: 10.0.0.45
SymptomWhat the proxy is doing
407 Proxy Authentication RequiredProxy requires credentials — browser didn't send them
502 Bad Gateway from internal IPProxy got an invalid response from upstream
504 Gateway Timeout from internal IPProxy couldn't reach upstream in time
ERR_TUNNEL_CONNECTION_FAILEDProxy's CONNECT tunnel for HTTPS failed
Certificate issuer is "Zscaler" / "BlueCoat" / "Forcepoint"SSL inspection active — proxy is intercepting HTTPS
⚠ SSL Inspection Some corporate proxies terminate TLS, inspect the traffic, and re-encrypt it with their own certificate. Click the lock icon → More Information. If Issued By says something like "Zscaler Root CA" instead of "Let's Encrypt", your connection is being inspected by the proxy. This is legal and common in corporate environments but may cause errors if the proxy's CA certificate isn't in the browser's trust store.

Putting It All Together — Diagnostic Flow

1. Open Console → Is there a red error?
├─ CORS header missing → Step 2 (check CORS headers in Network → Response Headers)
├─ Mixed Content → Step 3 (Ctrl+F → search 'http://' in Network panel)
├─ SEC_ERROR / SSL_ERROR → Step 4 (lock icon → More Information)
├─ NS_ERROR_UNKNOWN_HOST → Step 5 (DNS failure — environmental)
├─ NS_ERROR_CONNECTION_REFUSED → Step 6 (port closed)
├─ NS_ERROR_NET_TIMEOUT → Step 6 (firewall dropping packets)
└─ 407 / ERR_TUNNEL → Step 7 (proxy auth or CONNECT failure)
2. Click failing request in Network panel
├─ Status 4xx/5xx → server responded (CORS, auth, proxy gateway)
├─ Status -- → browser blocked it (CORS, mixed content, firewall, DNS)
└─ Timings tab
├─ Long DNS → DNS failure
├─ Long Connecting → firewall/proxy timeout
└─ Instant Connecting fail → connection refused
3. Check response headers for Via / X-Cache → proxy in chain → Step 7

Exercises

Open the DevTools Console on any HTTPS page and run:
fetch('http://example.com/test')
  .catch(e => console.error(e.message));
Is the error a CORS error, a mixed content block, or both? Why?
In the Network panel, press Ctrl + F and type http://. Does any response on the current page contain an HTTP URL embedded in it? If so, what kind of resource is it?
Visit any HTTPS site and click the lock icon → Connection SecureMore Information. Who is the certificate issuer? When does it expire? Is a proxy (Zscaler, BlueCoat, Squid) listed under "Issued By"?
In the Network panel toolbar, set throttling to GPRS. Reload a resource-heavy page. Open the Timings tab for a request. How does each phase compare to a no-throttling result? Change it back to "No throttling" when done.
Right-click any request in the Network panel → Block URL. Reload the page. Does the page fail silently, show an error, or degrade gracefully? Unblock the URL afterwards (right-click → Remove from block list).
Filter the Network panel to XHR/Fetch. Click any API request → Response Headers. Is Access-Control-Allow-Origin present? What is its value? Is it a wildcard (*) or a specific origin?
Simulate a DNS failure: in the Console, run:
fetch('https://this-domain-definitely-does-not-exist-xyz.com')
  .catch(e => console.error('Error:', e.message));
Does the Timings tab show a long DNS phase, a quick DNS then failed Connecting, or something else?

Key Takeaways

  • Read the Console first — the error message names the problem category. Don't navigate the Network panel blindly when the Console has already told you what's wrong.
  • CORS is enforced by the browser; the request already reached the server. Fix it by adding Access-Control-Allow-Origin (and Access-Control-Allow-Headers for auth) on the server.
  • Wildcard + credentials conflict: Access-Control-Allow-Origin: * cannot be used with credentials: 'include' — use a specific origin instead.
  • Mixed content: any http:// resource on an HTTPS page. Use Ctrl + F in the Network panel and search http:// to find them quickly.
  • TLS errors: check the lock icon → More Information for certificate details; check Issued By to spot proxy SSL inspection.
  • DNS failures show only a failed DNS Lookup phase in Timings. They are environmental — corporate filter, unpropagated domain, expired domain.
  • Firewall blocks: connection refused = instant fail; firewall drop = slow timeout. The Timings tab Connecting phase distinguishes them.
  • Proxy involvement is revealed by Via, X-Cache, or X-Forwarded-For headers in the response, or a 407 status code.
  • Block URL (right-click any request) lets you test what happens when a resource is unavailable — ideal for resilience testing.
Next: Lesson 10 — Debugging a Slow or Broken Page. We tie together the Inspector, Console, Debugger, Network, and Performance panels to diagnose a page that renders incorrectly, behaves unexpectedly, or loads far too slowly.