Outils développeurOpen sourceEn ligne

exif-scrub

See what your photos say about you — GPS, serial number, the lot — and take it out without re-encoding a pixel. Runs in your browser or your terminal, offline.

JavaScript

exif-scrub

README du dépôt

exif-scrub

English · Français

Your photos say more about you than you think. Where they were taken, to within a few metres. Which camera, down to its serial number. When, to the second. This shows you exactly what is in there, then takes it out — without re-encoding a single pixel.

CI node dependencies offline license

→ Drop a photo in, right here

No install, no upload, no account. The page is static and has no server to send anything to.

$ npx github:CedricPoint/exif-scrub holiday.jpg

  holiday.jpg  JPEG · 2.4 MB

  ● Where the photo was taken  48.8582, 2.2945, 324 m up
    To within a few metres. On a photo taken at home, that is your address.
    https://www.openstreetmap.org/?mlat=48.8582&mlon=2.2945#map=17/48.8582/2.2945

  ● Your particular camera, not just the model  BodySerialNumber: F2LW4015HG7K
    Every photo you publish can be tied to every other photo from the same body.

  ● The camera or phone  Apple iPhone 15 Pro · iPhone 15 Pro back triple camera 6.765mm f/1.78
  ● When it was taken  2026:07:14 21:08:33
  ● A private block from the manufacturer  MakerNote, 512 bytes
  ● An embedded preview  a second, smaller copy of the image
    Editors have been known to leave the old preview behind — the one from before you cropped.

  carried in  APP1 (exif)  — 1.4 kB

  remove it with  exif-scrub holiday.jpg --write

Taking it out

exif-scrub photo.jpg --write        # writes photo.clean.jpg beside it
exif-scrub *.jpg --in-place         # overwrite, for a whole folder
exif-scrub photo.jpg --out ./safe   # collect the copies somewhere
exif-scrub photo.jpg --json         # the whole report, for a script

The pixels are never touched. A JPEG is a handful of marked segments followed by the compressed image; cleaning it copies the file across without the segments that hold metadata. Nothing is decoded, nothing is re-encoded, no quality is lost. The test suite asserts it the strict way: scrub a photo that had metadata injected, and you get back the original file byte for byte.

That is the difference from "re-saving it in an editor", which quietly costs you a generation of quality every time.

What it keeps, on purpose

keptremoved
the colour profile (ICC), the Adobe colour block, the JFIF headerEXIF, GPS, XMP, IPTC, maker notes, comments
the orientation flag — dropping it turns photos sidewaysthe embedded preview thumbnail
in PNG: iCCP, gAMA, sRGBin PNG: tEXt, zTXt, iTXt, eXIf, tIME

A colour profile is not metadata about you, it is how the picture is meant to look. --color drops it too, when you want the smallest possible file and do not care.

Nothing leaves your machine

  • No network code exists in what ships. Not disabled, not opt-out: absent. A CI job greps src/, web/ and the page for fetch, XMLHttpRequest, sendBeacon, WebSocket and hard-coded URLs, and fails the build if one turns up. There are no dependencies to sneak one in either, and CI checks that too.
  • The browser page is the same code, imported straight from src/. There is no separate "web version" that could behave differently.
  • The only outbound click is the map link, which hands the coordinates to OpenStreetMap, because that is what you asked it to do.

As a library

import { inspect, scrub } from 'exif-scrub';

const report = inspect(bytes);          // { findings, coordinates, carriers, … }
if (report.findings.length) {
  const { bytes: clean } = scrub(bytes);
}

Works unchanged in Node and in a browser: it takes a Uint8Array and returns one, and touches nothing else.

Reading hostile files

A photo is an untrusted file, and EXIF is a structure made almost entirely of offsets that point wherever the file says. So the reader bounds-checks every read, caps the number of entries in a directory, refuses absurd lengths, and keeps a set of visited offsets so a directory that points at itself cannot loop. A corrupt or malicious file produces a partial answer and a warning — never an exception, never a hang. There are tests that truncate a valid block at every offset, point a directory at itself, and claim four billion values for one tag.

Tests

31, over real files: the JPEG and PNG fixtures were encoded by a browser, complete with the colour profile a browser attaches, and the EXIF is written by a separate writer in the test folder — so the reader is checked against bytes it did not produce.

npm test
npm run check:offline

License

MIT