Ingest a HAR file and quantify the cost of every third-party origin on a
page, then produce a prioritized defer / remove / self-host / facade
recommendation report. Zero runtime dependencies, pure Node (>=18).
It is designed to be a shareable performance-audit asset: run it in CI, paste the Markdown into a pull request, or send the standalone HTML report to a stakeholder.
Given a HAR (HTTP Archive) export, the tool:
- Groups every request by registrable domain (eTLD+1) and separates first-party from third-party origins.
- Computes, per third-party origin: request count, transfer bytes, uncompressed content bytes, a by-resource-type breakdown, total wall time, an approximate blocking time, an estimated JS main-thread cost, and how early it loads.
- Ranks origins by a documented impact score (0–100).
- Emits a recommendation per origin (
remove,defer,self-host,facade,preconnect, orreview) with a short rationale and an estimated saving. - Renders the result as a colored terminal table, JSON, a Markdown audit report, or a self-contained HTML report with an inline SVG chart.
For background on the technique, see Third-party resource impact mapping.
- Open DevTools (
F12orCmd/Ctrl+Shift+I) and select the Network tab. - Tick Preserve log and (optionally) Disable cache for a cold-load view.
- Reload the page and let it finish loading.
- Right-click any request in the list and choose Save all as HAR with content (or click the Export HAR / download icon in the Network toolbar).
- Save the
.harfile and point the tool at it.
- Run a test at WebPageTest.
- Open the result, then use Export → Export HAR (available from the
result's detail view) to download the
.har.
This is a standalone repository. It is not published to npm.
# 1. Clone
git clone https://github.com/network-priority/third-party-impact-mapper.git
cd third-party-impact-mapper
# 2. Run directly (no dependencies to install)
node bin/third-party-impact-mapper examples/example.har
# 3. Optional: expose it on your PATH as a global command
npm link
third-party-impact-mapper examples/example.harthird-party-impact-mapper <file.har> [options]| Option | Description |
|---|---|
--first-party <domain> |
Treat this registrable domain (eTLD+1) as first party instead of inferring it from the document URL. |
--json |
Emit machine-readable JSON. |
--md |
Emit a Markdown audit report (great for PRs/issues). |
--html |
Emit a standalone, self-contained HTML report. |
--top <n> |
Limit the ranked output to the top N origins. |
--ms-per-kb <n> |
Main-thread estimate factor (ms of main-thread work per KB of executable JS). Default 1.5. |
--sort <key> |
Sort by impact (default), bytes, requests, or time. |
--min-bytes <n> |
Hide third parties transferring fewer than N bytes. |
--no-color |
Disable ANSI colors in terminal output. |
--version |
Print version and exit. |
--help |
Print help and exit. |
The command exits 0 on success and non-zero on bad input (missing file,
unreadable file, invalid JSON, or an unusable HAR).
third-party-impact-mapper page.har
third-party-impact-mapper page.har --first-party example.com --top 10
third-party-impact-mapper page.har --md > report.md
third-party-impact-mapper page.har --html > report.html
third-party-impact-mapper page.har --json --sort bytesA runnable sample HAR and a committed report live in the examples folder.
A colored table ranked by impact, with a summary block (total third-party bytes, share of page weight, origin count, worst offender) and the top recommendations.
# Origin Category Reqs Transfer MainThr* Early Impact Action
───────────────────────────────────────────────────────────────────────────────────
1 YouTube Images Video 2 125.4 KB 498 ms 46% 91.9 facade
2 Intercom Support 1 68.6 KB 308 ms 31% 52.9 facade
3 Google DoubleClick Ads 1 43.2 KB 190 ms 88% 46.2 remove
A structured object with page metadata, config (including the scoring weights),
totals, and a full origins array. Ideal for CI budgets or dashboards.
{
"tool": "third-party-impact-mapper",
"totals": { "thirdPartyOrigins": 10, "thirdPartyBytes": 425510, "thirdPartyPct": 67.5 },
"origins": [
{ "domain": "ytimg.com", "impact": 91.9, "recommendation": { "type": "facade" } }
]
}A shareable audit report with a summary table, a ranked table, per-origin recommendations, and the scoring methodology. See the example report.
A single self-contained .html file (all CSS inline, chart drawn as inline
SVG, no external requests) that renders in light or dark mode. Safe to email or
attach to a review.
Each third-party origin gets an impact score from 0 to 100:
impact = 100 * (
0.35 * (transferBytes / maxTransferBytes)
+ 0.35 * (mainThreadEst / maxMainThreadEst)
+ 0.15 * (requests / maxRequests)
+ 0.15 * earliness
)
Each term explained:
- Transfer bytes — weight
0.35. Bytes on the wire are the most portable proxy for network cost. Normalised against the heaviest third party on the page. - Main-thread estimate — weight
0.35. Estimated JS execution time (see the caveat below); usually the real interactivity bottleneck. Normalised against the worst third party. - Request count — weight
0.15. Each request adds connection and scheduling overhead. Normalised against the worst third party. - Earliness — weight
0.15. A0..1value where1.0means the origin's first request starts at page start and0.0means it starts at or after the reference time (pageonLoad, falling back toonContentLoador the latest observed start). Earlier resources compete with critical content, so earliness amplifies harm.
Because the first three terms are normalised against the worst third party on
the page being audited, the score is relative to that page: a score of 100
means "the single worst offender on some axis." This makes scores comparable
within a report, not across unrelated pages.
A HAR file contains no CPU data. It records network activity, not how long
the browser's main thread spent parsing, compiling, and executing scripts. The
mainThreadEst value is therefore a deliberately simple heuristic:
mainThreadEst(ms) = (uncompressed JS content bytes / 1024) * msPerKb
The default msPerKb is 1.5, a rough mid-tier figure; tune it with
--ms-per-kb. Treat this number as a relative ranking signal, not a
measurement. For real main-thread timings, profile with the DevTools
Performance panel or Lighthouse. For the specific case of tag managers, see
Measuring tag-manager blocking time.
remove— analytics or ads carrying real cost that the page does not depend on functionally. Removing (or replacing with a lighter, consent-gated, or server-side alternative) reclaims the whole cost.defer— a script that loads early and has a meaningful main-thread estimate. Addasync/deferor load it after first paint to keep it off the critical path.facade— a heavy social, video, or chat embed. Replace it with a lightweight click-to-load placeholder so its weight is paid only on interaction. See Replacing third-party embeds with facades.self-host— fonts or CDN assets that add an extra connection. Self-host the asset (or at least preconnect) to remove the DNS+TCP+TLS handshake and gain caching control.preconnect— an origin used early that would benefit from an early connection hint. A HAR cannot reveal existingpreconnect/dns-prefetchhints, so verify before adding. See Strategic preconnect / dns-prefetch usage.review— a lower-impact third party with no clear pattern from the HAR alone. Worth a manual look.
- No CPU/main-thread data. See the caveat above; script execution cost is estimated, not measured.
async/deferis invisible. A HAR does not record how a script was requested, so "render-blocking-ish" is approximated as scripts requested early — an upper bound.- Existing resource hints are invisible.
preconnect/dns-prefetchalready in the page cannot be seen, sopreconnectsuggestions may be redundant — verify first. - Transfer sizes vary by exporter. The tool prefers Chrome's
_transferSize, thenbodySize(+ header bytes), then decompressed content size. Different tools populate these differently. - Bundled public-suffix subset. eTLD+1 grouping uses a small built-in
subset of the Public Suffix List, not the full list. Common suffixes
(including multi-label ones like
co.uk) are covered; exotic suffixes fall back to a "last two labels" heuristic. Verify unusual domains manually. - Provider database is finite. Unknown origins are labelled
other; you can still act on their metrics, they just lack a friendly name and category. - Single page per run. Only the first
log.pages[0]timing context is used.
- Third-party resource impact mapping
- Replacing third-party embeds with facades
- Measuring tag-manager blocking time
- Strategic preconnect / dns-prefetch usage
Issues and pull requests are welcome. The project has zero runtime dependencies by design; please keep it that way.
node --test # run the full test suitePlease add or update node:test tests for any behavior change, and run the
tool against the example HAR in each output mode
before opening a pull request.
MIT © 2026 Network Priority. See LICENSE.
Built by Network Priority — practical guidance on browser loading mechanics and resource prioritization.