3-Tools.com
Free Online Tool

Free Online Website Evidence Capture Tool — Verifiable Snapshots with Timestamp & Hash

Free online website evidence capture tool that saves a verifiable HTML snapshot with timestamp, redirect trail, headers, and SHA-256 hashes for journalism and legal proof

🧾

Website Evidence Capture

Capture a verifiable HTML snapshot with timestamp, redirect trail, headers, and SHA-256 hashes — built for newsroom and legal workflows.

Shortcuts: Ctrl/⌘+Enter capture • Shift+Enter add to queue • Ctrl/⌘+K focus • Ctrl/⌘+S download manifest • Esc clear
API: /api/capture Idle
Paste one URL or multiple (one per line). Use Shift+Enter to add the current line to the queue.
0 URL(s) detected
Quickly apply a known-good configuration.
Some sites vary content by UA. Choose a preset.
Hard cap for response body to avoid huge downloads.
Inline same-origin CSS/small assets up to this size.
Bulk capture queue
Queue runs sequentially to preserve redirect trails and reduce rate-limits.
0 / 0
Capture output
Download the evidence bundle: snapshot.html, metadata.json, and manifest.txt (hashes).
No capture yet
Run a capture to see redirect chain, headers, and hashes.
Redirect chain
Header forensics (allowlist)
Hashes
Optional private share link
If enabled and supported by your Worker, you’ll get a short link with expiration.
Recent captures
Stored locally (last 20). Re-download bundles anytime.
Tip: Use “Share Settings” to send non-sensitive options + URL list via the URL hash. Evidence files are never uploaded unless you enable private share links.

About

free online website evidence capture tool helps you preserve a verifiable record of what a webpage showed at a specific moment — including a timestamp, redirect trail, allowlisted response headers, and SHA-256 hashes. It’s designed for situations where “I saw it on the site” isn’t enough: you need a repeatable, tamper-evident evidence bundle you can hand to an editor, attorney, or compliance team.

Unlike basic “save page” features or screenshots that lose provenance, this tool produces a structured evidence package: a rewritten HTML snapshot for replayability, a metadata JSON file describing the capture context (final canonical URL, status codes, redirect hops, and header forensics), and a manifest file that records cryptographic hashes. Together, these files make it easier to demonstrate integrity and chain-of-custody in journalism, investigations, and legal discovery workflows.

Common users include investigative reporters documenting changing claims, fact-checkers tracking edits, attorneys preserving online statements before they disappear, compliance teams auditing public disclosures, and researchers collecting reproducible web artifacts. The built-in bulk queue supports newsroom-scale tracking with retries and exports, while the verification mode lets reviewers confirm that files haven’t been altered since capture.

Practical use cases

  • Document a public statement, policy page, or product claim before it’s edited or removed.
  • Capture redirect behavior (e.g., geo-based routing, affiliate redirects, or cloaking) with a readable hop timeline.
  • Preserve caching signals (ETag, Last-Modified, Cache-Control, Date) for evidentiary context.
  • Run bulk captures for a list of sources and export CSV/JSONL for editorial tracking and case notes.

Why this is better than alternatives: screenshots are easy to dispute and hard to verify; browser “Save Page As” often breaks assets and doesn’t record headers or redirects; third-party archives may be public, slow, or unsuitable for sensitive investigations. This tool focuses on verifiability (SHA-256 hashing + manifest), forensic context (redirect chain + header allowlist), and repeatable workflows (history, presets, queue, and exports). For JS-heavy pages or blocked fetches, Print Capture Mode provides a consistent fallback cover sheet to standardize OS-level “Save as PDF” captures.

FAQ

  • Does this upload my captured pages?
    By default, captures are fetched by your configured Worker endpoint and the resulting files are downloaded to your device. Optional private share links require explicit opt-in and Worker support for KV storage with expiration.
  • What makes the bundle “tamper-evident”?
    The tool records SHA-256 hashes for snapshot.html and metadata.json in a manifest. If any byte changes later, the recomputed hash will not match.
  • How do editors or lawyers verify integrity later?
    Use Court-Ready Verification Mode: upload snapshot.html + metadata.json and the tool recomputes hashes in-browser and generates a one-page verification report suitable for printing to PDF.
  • Why are only some headers shown?
    For evidentiary clarity and privacy, the tool focuses on an allowlisted set (Date, Cache-Control, ETag, Last-Modified, plus basic content headers). Your Worker can be configured to capture more, but allowlisting reduces noise and risk.

Most Popular

Upgrade to Pro

Unlimited usage & power features — less than a coffee per week

Free

  • ✓ 5 uses per day
  • ✓ Basic features
  • ✗ No batch processing
  • ✗ No priority support

Pro — €4.99/mo

  • Unlimited uses
  • ✓ All features unlocked
  • ✓ Batch processing
  • ✓ Priority email support
Get Pro Access →

Cancel anytime · Secure payment via Stripe · Instant activation

Related Tools

`; } function generatePrintSheet(){ const url = (els.printUrl.value || '').trim(); if (!isValidHttpUrl(url)){ toast('error', 'Invalid URL', 'Enter a valid http(s) URL for the print capture sheet.'); return; } const notes = (els.printNotes.value || '').trim(); const ts = nowIso(); const html = ` Print Capture Sheet
Website Evidence — Print Capture Sheet
Use this sheet as a consistent cover page when saving a webpage to PDF via your OS/browser print dialog.
Generated at
${escapeHtml(ts)}

Target URL

${escapeHtml(url)}

Operator notes

${escapeHtml(notes || '—')}

Checklist

Generated by the Free Online Website Evidence Capture Tool (local-only print sheet).
`; state.printSheetHtml = html; els.btnOpenPrintSheet.disabled = false; toast('success', 'Print sheet generated', 'Open it and use Print → Save as PDF.'); } function openPrintSheet(){ if (!state.printSheetHtml) return; const w = window.open('', '_blank', 'noopener,noreferrer'); if (!w){ toast('error', 'Popup blocked', 'Allow popups to open the print sheet.'); return; } w.document.open(); w.document.write(state.printSheetHtml); w.document.close(); setTimeout(() => { try { w.focus(); w.print(); } catch {} }, 250); } function wireEvents(){ els.urlInput.addEventListener('input', () => { updateUrlCount(); state.settings.lastUrls = els.urlInput.value; saveToLS(); }); els.uaSelect.addEventListener('change', () => { els.customUaWrap.style.display = (els.uaSelect.value === 'custom') ? '' : 'none'; readSettingsFromUI(); }); els.presetSelect.addEventListener('change', () => { const v = els.presetSelect.value; applyPreset(v); }); els.btnToggleAdvanced.addEventListener('click', () => { const open = els.advancedWrap.style.display !== 'none'; els.advancedWrap.style.display = open ? 'none' : ''; els.btnToggleAdvanced.textContent = open ? 'Advanced options' : 'Hide advanced options'; }); for (const el of [ els.customUaInput, els.maxBytesInput, els.inlineLimitInput, els.optCaptureHeaders, els.optStrictSSRF, els.optInlineAssets, els.optStoreShare, els.ttlInput, els.retriesInput ]){ el.addEventListener('change', readSettingsFromUI); el.addEventListener('input', () => { // avoid spamming LS for checkboxes if (el.type === 'checkbox') return; readSettingsFromUI(); }); } els.btnSavePreset.addEventListener('click', () => { readSettingsFromUI(); state.settings.myPreset = { ua: state.settings.ua, customUa: state.settings.customUa, maxBytes: state.settings.maxBytes, inlineLimit: state.settings.inlineLimit, captureHeaders: state.settings.captureHeaders, strictSSRF: state.settings.strictSSRF, inlineAssets: state.settings.inlineAssets, storeShare: state.settings.storeShare, ttlHours: state.settings.ttlHours, retries: state.settings.retries }; saveToLS(); toast('success', 'Saved', 'Saved as “My Preset” (local to this device).'); // Add a visible option if not present if (![...els.presetSelect.options].some(o => o.value === 'my')){ const opt = document.createElement('option'); opt.value = 'my'; opt.textContent = 'My Preset'; els.presetSelect.appendChild(opt); } }); els.btnAddToQueue.addEventListener('click', () => { const urls = normalizeUrls(els.urlInput.value); if (!urls.length){ toast('error', 'No URLs', 'Paste at least one URL.'); return; } addUrlsToQueue(urls); }); els.btnCapture.addEventListener('click', runCaptureFromInput); els.btnRunQueue.addEventListener('click', runQueue); els.btnExportQueue.addEventListener('click', exportQueue); els.btnClearQueue.addEventListener('click', () => { state.queue = []; saveToLS(); renderQueue(); toast('success', 'Queue cleared', ''); }); els.btnTryExample.addEventListener('click', async () => { // Use realistic URLs; may redirect depending on environment. const example = [ 'https://example.com', 'https://www.iana.org/domains/reserved', 'https://httpbin.org/redirect/2' ].join('\n'); els.urlInput.value = example; updateUrlCount(); readSettingsFromUI(); addUrlsToQueue(normalizeUrls(example)); setTab('capture'); await runQueue(); }); els.btnShareState.addEventListener('click', encodeStateToHash); els.btnClear.addEventListener('click', () => { clearAll(); toast('info', 'Cleared', 'Inputs and outputs cleared.'); }); els.btnClearHistory.addEventListener('click', () => { state.history = []; saveToLS(); renderHistory(); toast('success', 'History cleared', ''); }); els.tabBtns.forEach(b => b.addEventListener('click', () => setTab(b.getAttribute('data-tab')))); els.btnDownloadSnapshot.addEventListener('click', () => { const out = getCurrentOutput(); if (!out) return; downloadText(out.filenames.snapshot, out.snapshot_html, 'text/html;charset=utf-8'); }); els.btnDownloadMetadata.addEventListener('click', () => { const out = getCurrentOutput(); if (!out) return; downloadJson(out.filenames.metadata, out.metadata); }); els.btnDownloadManifest.addEventListener('click', () => { const out = getCurrentOutput(); if (!out) return; downloadText(out.filenames.manifest, out.manifest_txt, 'text/plain;charset=utf-8'); }); els.btnCopyManifest.addEventListener('click', () => { const out = getCurrentOutput(); if (!out) return; copyToClipboard(out.manifest_txt); }); els.btnCopyRedirects.addEventListener('click', () => { const out = getCurrentOutput(); if (!out) return; const chain = out.metadata?.redirect_chain || []; const text = chain.map((h,i)=>`#${i+1} ${h.status || ''} ${h.url || h.from || ''}${h.location || h.to ? ` -> ${h.location || h.to}` : ''}`).join('\n') || '—'; copyToClipboard(text); }); els.btnCopyHeaders.addEventListener('click', () => { const out = getCurrentOutput(); if (!out) return; const headers = out.metadata?.headers_allowlisted || out.metadata?.headers || {}; const text = Object.keys(headers).sort().map(k => `${k}: ${headers[k]}`).join('\n') || '—'; copyToClipboard(text); }); els.btnCopyHashes.addEventListener('click', () => { const out = getCurrentOutput(); if (!out) return; const hashes = out.metadata?.hashes || {}; const text = Object.keys(hashes).sort().map(k => `${k}: ${hashes[k]}`).join('\n') || '—'; copyToClipboard(text); }); els.btnCopyShareLink.addEventListener('click', () => { if (!state.lastShareLink) return; copyToClipboard(state.lastShareLink); }); els.btnVerify.addEventListener('click', verifyFiles); els.btnDownloadReport.addEventListener('click', () => { if (!state.lastVerifyReportHtml) return; downloadText(`verification_report_${nowIso().replace(/[:.]/g,'-')}.html`, state.lastVerifyReportHtml, 'text/html;charset=utf-8'); }); els.btnPrintReport.addEventListener('click', () => { if (!state.lastVerifyReportHtml) return; const w = window.open('', '_blank', 'noopener,noreferrer'); if (!w){ toast('error', 'Popup blocked', 'Allow popups to print the report.'); return; } w.document.open(); w.document.write(state.lastVerifyReportHtml); w.document.close(); setTimeout(() => { try { w.focus(); w.print(); } catch {} }, 250); }); els.btnGeneratePrintSheet.addEventListener('click', generatePrintSheet); els.btnOpenPrintSheet.addEventListener('click', openPrintSheet); // Queue remove delegation is handled per item // Keyboard shortcuts document.addEventListener('keydown', (e) => { const isMac = navigator.platform.toLowerCase().includes('mac'); const mod = isMac ? e.metaKey : e.ctrlKey; if (e.key === 'Escape'){ e.preventDefault(); clearAll(); return; } if (mod && (e.key === 'k' || e.key === 'K')){ e.preventDefault(); els.urlInput.focus(); return; } if (mod && e.key === 'Enter'){ // Capture if (state.busy) return; e.preventDefault(); setTab('capture'); runCaptureFromInput(); return; } if (mod && (e.key === 's' || e.key === 'S')){ // Download manifest const out = getCurrentOutput(); if (!out) return; e.preventDefault(); downloadText(out.filenames.manifest, out.manifest_txt, 'text/plain;charset=utf-8'); toast('success', 'Downloaded', 'manifest.txt saved.'); return; } // Shift+Enter in URL box adds to queue if (e.shiftKey && e.key === 'Enter' && document.activeElement === els.urlInput){ e.preventDefault(); const urls = normalizeUrls(els.urlInput.value); if (urls.length) addUrlsToQueue(urls); return; } }); } function init(){ loadFromLS(); // Add My Preset option if exists if (state.settings.myPreset && ![...els.presetSelect.options].some(o => o.value === 'my')){ const opt = document.createElement('option'); opt.value = 'my'; opt.textContent = 'My Preset'; els.presetSelect.appendChild(opt); } applySettingsToUI(); restoreStateFromHash(); wireEvents(); // Smart defaults: if empty, prefill if (!els.urlInput.value.trim()){ els.urlInput.value = state.settings.lastUrls; updateUrlCount(); } setCaptureOutput(null); setBusy(false, 'Idle'); // If API endpoint is unreachable, show a hint on first interaction els.btnCapture.addEventListener('click', () => { // no-op; already wired }, { once: true }); } init(); })();

⚡ Love this tool? Get more with Pro

3-Tools Pro €4.99/mo

Unlimited usage, no ads, priority features & early access to new tools.

🛠️ Get new free tools every week

Join 3-Tools newsletter — no spam, unsubscribe anytime.