NEL observability response
Network Error Logging — instructs browsers to report network-level failures like DNS errors, TCP failures, and TLS issues to a reporting endpoint.
What it does
NEL (Network Error Logging) instructs browsers to collect and report network-level failures that happen when users try to connect to your site. DNS resolution failures, TCP connection timeouts, TLS handshake errors, HTTP protocol errors — the kinds of failures that happen before your server even knows a request was attempted, and that you'd otherwise never know about.
It's one of the few ways to get visibility into connectivity issues your users experience that never show up in your server logs because the request never reached you.
Syntax
NEL takes a JSON object as its value:
NEL: {"report_to":"default","max_age":86400}
NEL: {"report_to":"nel-endpoint","max_age":31536000,"include_subdomains":true,"failure_fraction":1.0,"success_fraction":0.01}
Configuration fields
| Field | Type | Default | Meaning |
|---|---|---|---|
report_to |
string | required | Name of the reporting endpoint group (defined in Report-To header) |
max_age |
integer | required | How long (seconds) the browser caches this NEL policy |
include_subdomains |
boolean | false |
Whether to apply to subdomains too |
failure_fraction |
float 0–1 | 1.0 |
Fraction of network errors to report (1.0 = all) |
success_fraction |
float 0–1 | 0.0 |
Fraction of successful requests to report (0.0 = none) |
Requires Report-To
NEL doesn't work alone — it needs a Report-To header (or Reporting-Endpoints) that defines where to send the reports:
Report-To: {"group":"nel-endpoint","max_age":31536000,"endpoints":[{"url":"https://reports.example.com/nel"}]}
NEL: {"report_to":"nel-endpoint","max_age":31536000,"failure_fraction":1.0,"success_fraction":0.0}
The report_to value in NEL must match a group name in Report-To.
What gets reported
NEL captures failures at every layer of the network stack:
DNS errors:
dns.unreachable— DNS server unreachabledns.name_not_resolved— domain doesn't existdns.failed— DNS lookup failed for other reasons
TCP errors:
tcp.timed_out— connection timed outtcp.refused— connection refusedtcp.reset— connection reset
TLS errors:
tls.version_or_cipher_mismatch— incompatible TLS version/ciphertls.bad_client_auth_cert— client cert issuestls.cert.name_invalid— certificate name mismatchtls.cert.date_invalid— expired certificatetls.cert.authority_invalid— untrusted CA
HTTP errors:
http.protocol_error— HTTP protocol violationhttp.response.invalid— malformed responsehttp.response.redirect_loop— redirect loop detected
Application errors (if success_fraction > 0):
ok— successful request (for sampling successful traffic)
What a NEL report looks like
The browser POSTs JSON to your reporting endpoint:
{
"age": 0,
"type": "network-error",
"url": "https://example.com/api/data",
"body": {
"sampling_fraction": 1.0,
"referrer": "https://example.com/",
"server_ip": "203.0.113.42",
"protocol": "h2",
"method": "GET",
"status_code": 0,
"elapsed_time": 1234,
"phase": "connection",
"type": "tcp.timed_out"
}
}
Key fields: type (what went wrong), phase (where in the connection lifecycle), status_code (0 for pre-response failures), elapsed_time (ms before failure).
Setting up a reporting endpoint
The simplest possible receiver:
// Laravel route
Route::post('/nel-report', function (Request $request) {
$reports = $request->json()->all();
foreach ($reports as $report) {
Log::channel('nel')->info('NEL report', $report);
// Or push to your monitoring system
}
return response()->noContent();
})->withoutMiddleware([VerifyCsrfToken::class]);
For production: pipe to a logging service (Datadog, Elasticsearch, Splunk) or use a managed reporting service like Cloudflare's built-in NEL reporting.
Cloudflare's automatic NEL
If your site is behind Cloudflare, it sets NEL and Report-To automatically, collecting network errors and feeding them into Cloudflare's analytics. You'll see these reports in the Cloudflare dashboard without any setup — one of the rare cases where a third party handles this for you.
Success fraction for performance sampling
Setting success_fraction > 0 lets you sample successful requests for real-user performance data:
NEL: {"report_to":"nel","max_age":86400,"success_fraction":0.01,"failure_fraction":1.0}
1% of successful requests get reported with timing data — a lightweight RUM (Real User Monitoring) feed. The elapsed_time field tells you actual user-experienced latency from their browser.
Common mistakes and gotchas
Setting NEL without Report-To. If the report_to group name doesn't match an existing Report-To group, reports are silently discarded. Test with browser DevTools → Application → Reporting API to see if the policy was received.
HTTPS only. NEL only works on secure origins. Setting it on HTTP responses is ignored.
Not handling batched reports. Browsers batch NEL reports and send them together. Your endpoint receives an array, not individual objects. Parse accordingly.
Reporting endpoint on the same domain. If your domain is having DNS issues, your reporting endpoint on the same domain won't receive the report (DNS failed = can't reach anything on that domain). Use a different domain or a third-party service for the reporting endpoint.
Real-world examples
Full NEL setup:
HTTP/1.1 200 OK
Report-To: {"group":"nel","max_age":31536000,"endpoints":[{"url":"https://reports.example.com/nel"}],"include_subdomains":true}
NEL: {"report_to":"nel","max_age":31536000,"include_subdomains":true,"failure_fraction":1.0,"success_fraction":0.005}
Minimal setup (failures only, short policy lifetime for testing):
Report-To: {"group":"nel","max_age":300,"endpoints":[{"url":"/nel-report"}]}
NEL: {"report_to":"nel","max_age":300}
FAQ
Does NEL impact performance?
No — reports are sent in the background after the page loads, in a separate low-priority request. No impact on page performance.
How long does the browser cache the NEL policy?
max_age seconds, same as HSTS. Once a user visits your site and receives the NEL header, their browser will report errors for that domain for max_age seconds even if they never successfully connect again — which is exactly the point for capturing connectivity failures.
Is NEL widely supported?
Chrome and Edge support it. Firefox has limited support. Safari doesn't support it. So you'll capture errors for Chromium-based browser users, which is typically 65–70% of web traffic — still very useful.
Fun fact
NEL solves a blind spot that has existed since the web began: you can't log what your server never sees. Before NEL, if 2% of your users in a specific ISP couldn't reach your site due to a BGP routing issue, you'd never know unless they complained. NEL makes the browser itself your distributed monitoring agent, reporting failures from every corner of the internet back to you. The concept was proposed by Google's network reliability team after they discovered they were systematically unaware of connectivity issues affecting real users — problems that only became visible after building a browser-side reporting mechanism.