# NEL & Reporting API Auditor (`phoenix2810/nel-reporting-auditor`) Actor

Audit a public URL's Network Error Logging (NEL) and Reporting API configuration in one API call.

- **URL**: https://apify.com/phoenix2810/nel-reporting-auditor.md
- **Developed by:** [Sanskar Jaiswal](https://apify.com/phoenix2810) (community)
- **Categories:** Developer tools, SEO tools, Open source
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-usage

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## NEL & Reporting API Auditor

Audit a public URL's Network Error Logging (NEL) and Reporting API configuration in one API call. Deep-parses the `NEL`, `Report-To`, and `Reporting-Endpoints` response headers, validates endpoint-group wiring against the W3C NEL and Reporting API specs, detects dangling `report_to` references and report loops, and returns a readiness score, letter grade, and recommendations. Built for web platform engineers, reliability/observability teams, and security/QA consultants.

### Use cases

- **Web platform engineers** - verify NEL and Report-To wiring before enabling client-side network error telemetry in production
- **Reliability / observability (SRE) teams** - confirm report collectors (report-uri.com, self-hosted collectors) are actually referenced by well-formed headers from an external vantage point
- **CDN users** - detect `Report-To` headers auto-attached by CDNs pointing at endpoints you don't control
- **Security/QA consultants** - one-shot observability header check that slots into existing header-audit workflows
- **Pre-deploy QA** - catch the classic mistake of setting only `Reporting-Endpoints`, which does not support NEL (it works only with the legacy `Report-To` pipeline)

### What it checks

- `NEL` header: valid JSON, required `report_to` and `max_age`, boolean `include_subdomains`, `failure_fraction`/`success_fraction` in 0-1, unknown keys, too-short `max_age`
- `Report-To` header (legacy pipeline, required by NEL): group names, `max_age`, `endpoints` arrays, https endpoint URLs, duplicate group definitions
- `Reporting-Endpoints` header (new pipeline): `name="url"` entries, https URLs, duplicate names
- **Wiring**: NEL `report_to` must reference a defined Report-To group (dangling references drop reports silently)
- **Pipeline awareness**: warns when only `Reporting-Endpoints` is set — NEL does not work with the new pipeline
- **Report endpoints**: https-only URLs, parse validity, and spec-forbidden report loops (endpoints resolving back to the audited origin)

Report endpoints found in headers are **never fetched or POSTed to** — they are validated as URL strings only, so this actor cannot be used to flood report collectors.

### Input

| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| `startUrl` | string | yes | - | Public URL to audit |
| `timeoutSeconds` | integer | no | `10` | Per-request timeout (3-30 seconds) |

#### Example input

```json
{
  "startUrl": "https://example.com",
  "timeoutSeconds": 10
}
```

### Output

A single dataset item with the full audit:

| Field | Type | Description |
|---|---|---|
| `inputUrl` | string | The URL provided as input |
| `finalUrl` | string | Final URL after redirects |
| `https` | boolean | Whether the final response was served over HTTPS |
| `status` | integer | Final HTTP status code |
| `hasNel` | boolean | Whether the `NEL` header is present |
| `hasReportTo` | boolean | Whether the legacy `Report-To` header is present |
| `hasReportingEndpoints` | boolean | Whether the new `Reporting-Endpoints` header is present |
| `nel` | object | Parsed NEL policy (`reportTo`, `maxAge`, `includeSubdomains`, `failureFraction`, `successFraction`, `issues`) |
| `reportTo` | object | Parsed Report-To groups, endpoints, and issues |
| `reportingEndpoints` | array | Parsed Reporting-Endpoints named endpoint list |
| `danglingReportTo` | boolean | NEL references a `report_to` group not defined in Report-To |
| `reportLoop` | boolean | A report endpoint resolves back to the audited origin (spec violation) |
| `maxAge` | integer | null | Parsed NEL `max_age` (seconds) |
| `includeSubdomains` | boolean | NEL `include_subdomains` flag |
| `endpointCount` | integer | Total distinct report endpoints across headers |
| `httpsEndpointCount` | integer | Report endpoints using https |
| `checks` | array | Per-check analysis (name, status, note, weight, recommendation) |
| `issues` | array | Aggregated issue descriptions |
| `score` | integer | NEL/Reporting readiness score (0-100) |
| `grade` | string | Letter grade (A+, A, B, C, D, E, F) |
| `checkedAt` | string | ISO 8601 timestamp |
| `recommendations` | array | Actionable recommendations |

#### `checks` array

| Field | Description |
|---|---|
| `name` | Check title (NEL header, Report-To header, Reporting-Endpoints header, NEL-to-Report-To wiring, Legacy vs new pipeline, Report endpoints) |
| `check` | Check slug |
| `status` | `good`, `warn`, `missing`, or `info` |
| `note` | What was found |
| `weight` | Check weight in the score |
| `recommendation` | Fix recommendation, or null |

### Security

- Public HTTP/HTTPS only; rejects URL credentials, private IP literals, private DNS resolutions, and revalidates redirects before following.
- Fetches only the provided URL's response headers; report endpoints found in headers are never fetched or POSTed to.
- No login, no JavaScript execution, no cookies, no stored page content.

### Pricing

Pay-per-event: one scored audit per run (~$0.015/run).

### Local development

```bash
npm install
npm test        # node --test suite (parsers, SSRF, scoring, live smoke)
npm run lint    # node --check
python3 ../scripts/audit_actor.py .   # portfolio security audit (run from actors/ dir)
```

# Actor input Schema

## `startUrl` (type: `string`):

Public URL to audit. The actor fetches the page once and inspects the NEL, Report-To, and Reporting-Endpoints response headers. HTTP and HTTPS only. Private IP ranges are blocked.

## `timeoutSeconds` (type: `integer`):

Timeout for the HTTP request.

## Actor input object example

```json
{
  "startUrl": "https://example.com",
  "timeoutSeconds": 10
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "startUrl": "https://example.com"
};

// Run the Actor and wait for it to finish
const run = await client.actor("phoenix2810/nel-reporting-auditor").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "startUrl": "https://example.com" }

# Run the Actor and wait for it to finish
run = client.actor("phoenix2810/nel-reporting-auditor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "startUrl": "https://example.com"
}' |
apify call phoenix2810/nel-reporting-auditor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,phoenix2810/nel-reporting-auditor"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/VOqfv50Ag7nqIY8De/builds/fiYZ0Pa5fVedfubA5/openapi.json
