Your Photos Are Telling Strangers Where You Live: A Developer's Guide to EXIF and Privacy

Your Photos Are Telling Strangers Where You Live: A Developer's Guide to EXIF and Privacy

1 7
calendar_todayschedule4 min read

Every JPEG your phone or camera produces ships with a hidden passenger: EXIF metadata. Most people never look at it. Most developers never strip it. And that gap has leaked everything from celebrities' home addresses to the exact location of an active-duty soldier's base.

If you build anything that touches user-uploaded images — a marketplace, a social app, a photo gallery — this is one of those quiet responsibilities that's easy to ignore until it's a headline. Here's what's actually in that metadata, how to read it, and how to strip it in a few lines of code.

What's hiding in a photo

EXIF (Exchangeable Image File Format) is a block of key–value data embedded in the file itself. A typical photo carries:

  • GPS coordinates — latitude, longitude, sometimes altitude. Precise enough to drop a pin on your front door.
  • Timestamps — when the shot was taken, down to the second.
  • Camera & lens — make, model, and often the camera body's serial number.
  • Settings — ISO, aperture, shutter speed, focal length.
  • Software — what edited the file and when.

Individually, harmless. Together, they're a movement log. Geotagged photos posted over time draw a map of where you live, work, sleep, and travel — and the timestamps tell anyone when you're not home.

Reading EXIF (so you can see the problem)

Python (Pillow)

from PIL import Image
from PIL.ExifTags import TAGS, GPSTAGS

img = Image.open("photo.jpg")
exif = img.getexif()

for tag_id, value in exif.items():
    tag = TAGS.get(tag_id, tag_id)
    print(f"{tag:25}: {value}")

# GPS lives in its own sub-block (IFD)
gps_ifd = exif.get_ifd(0x8825)  # GPSInfo
for key, val in gps_ifd.items():
    print(f"{GPSTAGS.get(key, key):25}: {val}")

Turning the raw GPS tuples into decimal degrees:

def to_decimal(dms, ref):
    degrees, minutes, seconds = [float(x) for x in dms]
    dec = degrees + minutes / 60 + seconds / 3600
    return -dec if ref in ("S", "W") else dec

lat = to_decimal(gps_ifd[2], gps_ifd[1])   # GPSLatitude, GPSLatitudeRef
lon = to_decimal(gps_ifd[4], gps_ifd[3])   # GPSLongitude, GPSLongitudeRef
print(f"https://maps.google.com/?q={lat},{lon}")

Run that on a geotagged photo and you'll get a clickable map link straight to wherever it was taken. That's the whole point — it's that easy for anyone else, too.

JavaScript (browser, with exifr)

import exifr from 'exifr';

// Read just the GPS — works on a File from an <input>
const gps = await exifr.gps(file);
console.log(gps?.latitude, gps?.longitude);

// Or pull everything
const data = await exifr.parse(file);
console.log(data.Make, data.Model, data.DateTimeOriginal);

Command line (ExifTool)

exiftool photo.jpg            # dump everything
exiftool -gps:all photo.jpg   # just the location data

Stripping EXIF (so you stop being the problem)

One-off, CLI

# ExifTool — removes all metadata
exiftool -all= photo.jpg

# ImageMagick — re-encode without metadata
convert photo.jpg -strip clean.jpg

Python

# piexif: surgical removal, keeps the pixels untouched
import piexif
piexif.remove("photo.jpg", "clean.jpg")

# Pillow: re-save without the exif kwarg
from PIL import Image
img = Image.open("photo.jpg")
img.save("clean.jpg")  # no exif= passed → metadata dropped

Server-side on upload (Node, sharp)

This is the one that matters in production. Strip metadata as part of your upload pipeline, automatically, for every image. Don't trust users to do it.

import sharp from 'sharp';

await sharp(inputBuffer)
  .rotate()            // bake in EXIF orientation BEFORE you drop it
  .jpeg({ quality: 82 })
  .toFile('clean.jpg'); // sharp strips metadata by default

⚠️ Gotcha: EXIF has an Orientation tag that tells viewers to rotate the image. If you strip metadata without applying that rotation first, photos that looked fine suddenly appear sideways. Always auto-orient (sharp's .rotate() with no args) before discarding metadata.

Client-side, before it ever uploads

Drawing an image onto a <canvas> and re-exporting it discards EXIF as a side effect:

function stripExif(file) {
  return new Promise((resolve) => {
    const img = new Image();
    img.onload = () => {
      const canvas = document.createElement('canvas');
      canvas.width = img.naturalWidth;
      canvas.height = img.naturalHeight;
      canvas.getContext('2d').drawImage(img, 0, 0);
      canvas.toBlob(resolve, 'image/jpeg', 0.9);
    };
    img.src = URL.createObjectURL(file);
  });
}

(Same orientation caveat applies — handle rotation before you rely on this.)

"Doesn't the platform strip it for me?"

Sometimes. Most large social networks strip EXIF on upload — but the behavior is inconsistent and changes without notice. Direct file delivery (email attachments, cloud links, raw downloads, messaging apps that send "as file") frequently keeps everything intact. The safe assumption: if you didn't strip it, it's still there.

There's a real trade-off, too. Photographers and archivists often want metadata — copyright, camera settings, capture time. So the right rule isn't "always nuke it," it's be deliberate: strip location and serial numbers from anything public-facing, and keep the rest only where it's genuinely useful and the user consents.

Where this shows up in the real world

When you run an image-heavy product, metadata handling stops being a curiosity and becomes part of your duty of care. At Fotio, where the whole business is delivering professional photo galleries from shoots across Spain, this is exactly the kind of detail that separates a polished pipeline from a leaky one — high-resolution images that look great, without quietly broadcasting a client's GPS trail. If you're curious how an image-first product is put together end to end, you can poke around at fotio.app.

The one-line takeaway

Treat EXIF like any other untrusted, potentially sensitive payload: inspect it, strip what's risky, and never assume someone else already handled it.

Add a metadata-stripping step to your upload pipeline today. It's a handful of lines, and it closes a privacy hole most apps don't even know they have.


Do you strip EXIF in your projects — client-side, server-side, or not at all? And have you ever found something alarming in a photo's metadata? Share it in the comments.

1 Comment

0 votes
🔥 Join developers growing publicly
Share your knowledge, build in public, and grow your developer presence with a global community.

More Posts

The Sovereign Vault — A Comprehensive Guide to Protocol-Driven AI

Ken W. Algerverified - Jun 4

I’m a Senior Dev and I’ve Forgotten How to Think Without a Prompt

Karol Modelskiverified - Mar 19

The Privacy Gap: Why sending financial ledgers to OpenAI is broken

Pocket Portfolio - Feb 23

MCP Is the USB-C of AI. So Why Are You Plugging Everything In?

Ken W. Algerverified - Jun 10

Cavity on X-Ray: A Complete Guide to Detection and Diagnosis

Huifer - Feb 12
chevron_left
162 Points8 Badges
2Posts
0Comments
2Connections
Fotio App is a photography service that connects travelers with local photographers in Spain's most ... Show more

Related Jobs

View all jobs →

Commenters (This Week)

1 comment
1 comment
1 comment

Contribute meaningful comments to climb the leaderboard and earn badges!