Last month I helped a friend figure out why a stalker knew her daily routine. The answer was in her Instagram stories — not the content, but the metadata baked into every JPEG she posted. GPS coordinates, timestamps accurate to the second, even her phone model. Instagram strips EXIF on upload, but she’d been sharing originals in a group chat first.
Most developers know EXIF exists. Fewer know exactly what’s in there, how to parse it programmatically, or how to strip it without degrading image quality. I spent a weekend building a browser-based EXIF stripper that never uploads your files, and learned more about the JPEG binary format than I expected.
What EXIF Actually Contains (It’s Worse Than You Think)
EXIF (Exchangeable Image File Format) lives in the APP1 marker segment of JPEG files, right after the SOI (Start of Image) marker at bytes 0xFFD8. The structure follows TIFF IFD (Image File Directory) format — a linked list of tagged key-value pairs.
Here’s what a typical iPhone photo contains:
GPS Latitude: 37.7749 N
GPS Longitude: 122.4194 W
GPS Altitude: 12.3m above sea level
DateTime Original: 2026:05:20 14:32:07
Make: Apple
Model: iPhone 15 Pro Max
Lens: iPhone 15 Pro Max back camera 6.765mm f/1.78
Software: 18.4.1
Orientation: Rotate 90 CW
Focal Length: 6.765mm (equiv 24mm)
Exposure: 1/120s at f/1.78, ISO 50
Unique Image ID: 4A3B2C1D-...
That’s 40+ fields in a single photo. The GPS data alone is accurate to about 3 meters with modern phones. Post enough photos from your apartment and anyone with exiftool can pinpoint your building.
The Binary Structure: Parsing EXIF in JavaScript
If you want to strip EXIF without re-encoding (which would lose quality), you need to understand the byte layout. A JPEG with EXIF looks like this:
FF D8 - SOI marker (Start of Image)
FF E1 [len] - APP1 marker (EXIF data lives here)
45 78 69 66 00 00 - "Exif\0\0" header
[TIFF header + IFD entries + GPS sub-IFD]
FF E0 [len] - APP0 marker (JFIF, optional)
FF DB [len] - DQT (quantization tables)
FF C0 [len] - SOF (frame header)
... - actual image data
FF D9 - EOI marker
The key insight: you can remove the entire APP1 segment without touching image pixels. The compressed image data starts at SOF and is completely independent of the metadata. Here’s the core logic I use:
function stripExif(arrayBuffer) {
const view = new DataView(arrayBuffer);
if (view.getUint16(0) !== 0xFFD8) return arrayBuffer;
const segments = [];
let offset = 2;
while (offset < view.byteLength) {
const marker = view.getUint16(offset);
if (marker === 0xFFDA) {
segments.push(arrayBuffer.slice(offset));
break;
}
const segLen = view.getUint16(offset + 2);
if (marker !== 0xFFE1 && marker !== 0xFFED) {
segments.push(arrayBuffer.slice(offset, offset + 2 + segLen));
}
offset += 2 + segLen;
}
const soi = new Uint8Array([0xFF, 0xD8]);
const parts = [soi, ...segments.map(s => new Uint8Array(s))];
const result = new Uint8Array(parts.reduce((a, p) => a + p.length, 0));
let pos = 0;
for (const part of parts) {
result.set(part, pos);
pos += part.length;
}
return result.buffer;
}
This approach is lossless — zero re-encoding, zero quality loss. The output file is typically 5-50KB smaller than the input because you’re removing the metadata block entirely.
Why “Browser-Only” Matters for This
Think about the irony: you want to strip location data from your photos for privacy… so you upload them to a random website? That site now has your original files, complete with GPS coordinates, before stripping anything.
I built the orthogonal.info image tool to process everything client-side using the Canvas API and ArrayBuffer manipulation. Your files never leave your browser tab. Verify by opening DevTools Network tab — zero upload requests during processing.
const file = input.files[0];
const buffer = await file.arrayBuffer();
const stripped = stripExif(buffer);
const blob = new Blob([stripped], { type: 'image/jpeg' });
const url = URL.createObjectURL(blob);
What About PNG and WebP?
PNG stores metadata differently — in tEXt, iTXt, and eXIf chunks rather than APP1 markers. The chunk-based format makes it straightforward to filter: read each chunk’s 4-byte type identifier, skip the ones you don’t want, concatenate the rest.
WebP uses RIFF container format with an EXIF chunk. Same principle: parse chunks, drop the EXIF one, rebuild.
Tools I Actually Use
For batch processing on my homelab, I use exiftool:
# Strip ALL metadata from every JPEG in a directory
exiftool -all= -overwrite_original *.jpg
# Keep orientation (so photos display correctly) but strip everything else
exiftool -all= -tagsfromfile @ -Orientation -overwrite_original *.jpg
That second command is important — if you strip the Orientation tag, portrait photos will display sideways in some viewers. Common gotcha.
For quick one-off checks before sharing, I use our browser-based tool — compress and strip in one step, no install needed. For developers building apps that handle user uploads, the piexifjs library (3KB gzipped) handles read/write/strip operations well.
If you’re processing images on a server, a Raspberry Pi 5 running an exiftool batch script works great as a dedicated metadata sanitizer on your network — keeps processing local and costs about $80 total with a case and SD card.
Platforms That Strip vs. Don’t
I tested 12 platforms in May 2026:
Strip EXIF on upload: Instagram, Twitter/X, Facebook, LinkedIn, Discord, iMessage
Preserve EXIF (danger zone): Email attachments, Signal (original quality), Telegram (as file), Google Drive, Dropbox shared links, most forum software
Signal strips EXIF when you send as a compressed photo, but preserves everything when you tap “original quality.” Most people don’t realize the distinction. Telegram behaves the same way: compressed = stripped, sent as file = full metadata intact.
The Real Risk Model
For most people, the threat isn’t nation-state actors. It’s:
- Selling items online with photos taken at home (Craigslist, Facebook Marketplace)
- Sharing “original quality” photos in group chats with acquaintances
- Uploading images to forums, bug trackers, or documentation sites
- Dating app photos with location data if the platform doesn’t strip
A privacy screen protector stops shoulder-surfers, but EXIF metadata is the silent leak most people never think about. Strip it before sharing. Every time.
If you handle images in any application — whether it’s a side project or production — add EXIF stripping to your upload pipeline. It’s 20 lines of code and it protects your users from themselves.
Related: Developer Tools Guide | DevSecOps in Practice
Join Alpha Signal for free market intelligence — daily signals, no spam.
📧 Get weekly insights on security, trading, and tech. No spam, unsubscribe anytime.
Leave a Reply