inyourbrowser.com

/ VERIFY · TECHNICAL

Technical verification guide

A walkthrough for developers and security researchers. Confirms with browser DevTools and console commands that inyourbrowser.com cannot exfiltrate user data. For the friendlier, point-and-click version, see the verify page.

The site’s in-browser claim is enforced by the browser itself via a strict Content Security Policy. This page lists every check you can run, in roughly the order from cheapest to most thorough.

1. Echo of the active CSP

Below is the exact Content-Security-Policy header that production responses carry. The actual header on your current response is identical (read it in DevTools to confirm, per step 2).

Content-Security-Policy:
  default-src     'self'
  script-src      'self' 'unsafe-inline'
  style-src       'self' 'unsafe-inline'
  img-src         'self' blob: data:
  font-src        'self'
  worker-src      'self' blob:
  connect-src     'none'
  object-src      'none'
  base-uri        'none'
  form-action     'none'
  frame-ancestors 'none'
  manifest-src    'self'
  frame-src       'none'
  media-src       'self'
  upgrade-insecure-requests

Directive-by-directive:

About 'unsafe-inline' in script-src

Mozilla HTTP Observatory and similar static-analysis tools dock points (typically −20) for any CSP that contains 'unsafe-inline' in script-src. We are intentionally accepting that penalty. The explanation is below in full so any security reviewer reading this can audit the trade-off rather than infer it.

Why it is there. The site is a pure static export. There is no server runtime, no edge middleware, and no per-request response generator. The HTML for each page contains roughly 23 inline <script> tags:

The three commonly-cited ways to remove 'unsafe-inline' are:

Why the practical risk is low. 'unsafe-inline'becomes exploitable only if an attacker can inject a <script>tag into the HTML. For that to happen, the site would need an XSS vulnerability: a place where user-controlled input is rendered into the DOM without escaping. The codebase has been audited for XSS sinks — every dangerouslySetInnerHTMLcarries either static framework output or JSON-LD serialized through a jsonLdString() helper that escapes <, U+2028, and U+2029. The markdown previewer renders user text into a fully-sandboxed iframe (no allow-scripts, no allow-same-origin). YAML parses with the safe schema. There is no eval, no new Function, no string-argument setTimeout.

And even if a vulnerability slipped in, the connect-src 'none' directive prevents any injected script from sending data anywhere. The script could deface the page or modify the DOM, but it cannot exfiltrate files, keystrokes, or anything else. The form-action 'none' directive blocks form submission as a second exfiltration channel. The object-src 'none' blocks plugins as a third. img-src 'self' blob: data: blocks the common XSS-via-image-beacon pattern of using new Image().src = "https://attacker/?leak=".

The net effect:the audit penalty reflects an exfiltration risk that this site has explicitly closed at the network layer. The penalty is real in the general web sense but does not apply to this site's actual threat model. If you are evaluating this site for use with sensitive files, the audit score is the wrong question; the right question is what happens to your file when you drop it in, and the answer is enforced by the rest of this page.

2. Verify the CSP in DevTools

  1. Open DevTools (F12 or Ctrl+Shift+I / Cmd+Option+I).
  2. Network tab. Reload the page if the request list is empty.
  3. Click the very first row (the document fetch).
  4. Headers panel. Find content-security-policy under Response Headers and confirm it contains connect-src 'none'.
Headers · Response · / (Document)content-security-policy:default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';connect-src 'none';object-src 'none'; base-uri 'none'; form-action 'none';frame-ancestors 'none'; manifest-src 'self'; frame-src 'none';strict-transport-security: max-age=31536000; includeSubDomains; preloadx-frame-options: DENYcross-origin-opener-policy: same-origin

Illustration of what the Response Headers panel should look like. Real browser UI will differ in colour and exact layout.

3. Console commands to confirm each defence

Paste each of the following into the DevTools Console. The expected behaviour follows each command.

fetch("https://example.com").then(() => "SENT").catch(e => e.message)
// Expected: a string starting with "Refused to connect to ..." from the CSP.
new EventSource("https://example.com").readyState
// Expected: throws or readyState 2 (CLOSED) almost immediately. CSP blocks it.
navigator.sendBeacon("https://example.com", "leak")
// Expected: returns false (the beacon was rejected).
document.cookie
// Expected: "" — no cookies are set by this site.
Array.from({length: localStorage.length}, (_, i) => localStorage.key(i))
// Expected: [] or a subset of ["theme", "nc_dismissed"]. No other keys.
// "theme" = dark/light preference; "nc_dismissed" = whether the floating
// network-counter widget was closed by the user. Both are UI state only,
// never user file content.
(await indexedDB.databases?.() || []).map(db => db.name)
// Expected: [] in browsers that support indexedDB.databases().
Console · default levels> fetch("https://example.com").then(()=>"SENT").catch(e=>e.message)⊘ Refused to connect to 'https://example.com/' because it violates the following Content Security Policy directive: "connect-src 'none'".< "Failed to fetch"

Illustration of what the Console output should look like for the first command.

4. Network tab during tool use

Reload, clear the request log, and use any tool: drop a PDF into merge, paste JSON into the formatter, encode a QR. After the tool finishes, the request log should still be empty (or contain only same-origin resource fetches for lazy-loaded libraries, never a POST or PUT carrying user content).

Network · Fetch/XHR filter · Recording ⏺NameStatusTypeInitiatorSizeNo network activity recordedDrop a file into the tool above and watch this stay empty0 requests · 0 B transferred

Illustration of an empty Network tab after tool use.

5. Application tab: storage audit

Open Application (Chrome / Edge) or Storage (Firefox). Under Local Storage and Cookies for this origin, you should see at most one entry: theme in localStorage if you have ever toggled dark mode. Session Storage, Cookies, IndexedDB, and Cache Storage should all be empty for this origin.

ApplicationStorageLocal Storage ▾https://www.inyourbrowser.comSession StorageIndexedDBCookiesLocal Storage · https://www.inyourbrowser.comKeyValuethemedarkCookies · 0 entries · IndexedDB · 0 databases

Illustration of the Application tab showing only the theme key.

6. Per-API breakdown

Browser APIUsed byCan it leak data?
fetch / XMLHttpRequestNothing on this site.No. connect-src 'none' blocks both.
WebSocket / EventSourceNothing on this site.No. Same CSP directive blocks both.
navigator.sendBeaconNothing on this site.No. Returns false under connect-src 'none'.
FormData submitSearch form (handled in-page; never submitted to a server).No. form-action 'none' blocks any HTML form submission.
FileReaderEvery tool that reads a dropped file.No. Reads bytes into memory; cannot send them.
Canvas + toDataURL / toBlobAll image tools, favicon generator, PDF-to-image.No. Result is held in a JavaScript variable, downloaded via a same-origin blob URL.
Web Crypto (crypto.subtle.digest)SHA-256, SHA-512, SHA-1, password generator entropy.No. Pure compute; no network.
Web WorkersPDF.js renderer, regex worker, diff worker.No. Worker scripts are same-origin; CSP applies to workers too.
navigator.mediaDevices.getUserMediaQR code scanner only.No. Stream is consumed locally by jsQR; cannot be sent because connect-src 'none'.
postMessageNothing on this site (no iframes, no opener).No. frame-src 'none', no window.open().
navigator.clipboard.writeTextAll Copy buttons.No. Write-only access; the site never reads your clipboard.
localStorageTwo UI-state keys only: theme (dark/light) and nc_dismissed (network-counter widget visibility). No tool persists user content.No, by definition. Stored only on your device.

7. Lazy-loaded libraries

The heavy libraries (pdf-lib, pdfjs-dist, qrcode, jsqr, jsbarcode, marked, js-yaml, sql-formatter, cronstrue) are bundled at build time and loaded lazily on first interaction. They are fetched from the same origin (your own browser’s cache after the first load), never from a CDN. You can verify this in the Network tab the first time you use a given tool: every script you see come down is served from www.inyourbrowser.com.

8. Other security headers

The following headers apply to every HTML page and every tool route on the site:

Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
X-Frame-Options:               DENY
X-Content-Type-Options:        nosniff
Referrer-Policy:               no-referrer
Cross-Origin-Opener-Policy:    same-origin
Cross-Origin-Resource-Policy:  same-origin
X-DNS-Prefetch-Control:        off
Origin-Agent-Cluster:          ?1
Permissions-Policy:            camera=(self), microphone=(), geolocation=(),
                               usb=(), payment=(), serial=(), bluetooth=(),
                               magnetometer=(), accelerometer=(), gyroscope=(),
                               midi=(), autoplay=(), clipboard-read=(),
                               clipboard-write=(self), display-capture=(),
                               browsing-topics=(), attribution-reporting=(),
                               idle-detection=(), screen-wake-lock=(),
                               xr-spatial-tracking=(), interest-cohort=()

Per-route exception: Cross-Origin-Resource-Policy on public images

Five asset routes return Cross-Origin-Resource-Policy: cross-origininstead of the default same-origin:

These five routes exist solely to be loaded by external clients (social card previewers, the browser tab UI, PWA install flows, search-result thumbnails). They are deterministic brand images derived from URL parameters and contain no user data, so a permissive cross-origin value carries no information disclosure risk. The relaxation is per-route: every HTML page and every tool endpoint continues to return same-origin, and the connect-src 'none' directive that prevents data exfiltration is unchanged. You can confirm both states with a one-liner:

curl -sI https://www.inyourbrowser.com/json-formatter | grep -i cross-origin-resource
// Expected: Cross-Origin-Resource-Policy: same-origin

curl -sI https://www.inyourbrowser.com/og?title=Test | grep -i cross-origin-resource
// Expected: Cross-Origin-Resource-Policy: cross-origin

9. External validators you can run yourself

Independent scanners will confirm the headers above. Each link opens with the site URL pre-filled:

The limits of in-browser verification

Self-serve verification proves the current page is behaving as described. It does not prove the site cannot be changed in the future. Two robust ways to harden your trust over time:

← Back to the non-technical check page