A lot of small online shops take product photos on a phone, upload them straight to the store, and move on. That is fast, but the original file usually carries more than pixels. It can include the GPS coordinates of where the photo was taken, the phone model, the exact date and time, and sometimes editing software details.

If you shoot products at home, those coordinates point at your home.

This post covers three things:

  1. What is actually inside a photo's metadata
  2. How to read it in the browser with a few lines of JavaScript
  3. How to strip it in the browser, and the orientation bug that bites almost everyone the first time

What EXIF can contain

EXIF is a block of metadata that cameras and phones write into the image file. The fields that matter for privacy:

Field Example Why it matters
GPSLatitude / GPSLongitude 52.52, 13.40 Where the photo was taken, often to within a few meters
DateTimeOriginal 2026:10:11 14:32:07 When you were there
Make / Model Apple iPhone 15 Your device
Software a photo editor version What you used
Orientation 6 How the image should be rotated for display

Phones only write GPS when location access is turned on for the camera app, and many people never turn it off. JPEG is the usual suspect, but WebP, PNG and HEIC files can carry EXIF too.

Some big platforms remove metadata when you upload. Don't assume yours does. The only reliable test is to download the file your visitors actually receive and inspect it.

Step 1: Check a file for metadata

On the command line, exiftool is the standard:

exiftool -gps:all -DateTimeOriginal -Model product-photo.jpg 

In the browser, the exifr library parses metadata locally, so the file never has to leave the user's device:

import exifr from "exifr"; async function inspect(file) { const gps = await exifr.gps(file); // { latitude, longitude } or undefined const tags = await exifr.parse(file, ["Make", "Model", "DateTimeOriginal"]); return { hasLocation: Boolean(gps), location: gps ? `${gps.latitude.toFixed(5)}, ${gps.longitude.toFixed(5)}` : null, device: [tags?.Make, tags?.Model].filter(Boolean).join(" ") || null, takenAt: tags?.DateTimeOriginal ?? null, }; } document.querySelector("input[type=file]").addEventListener("change", async (e) => { for (const file of e.target.files) { console.log(file.name, await inspect(file)); } }); 

Run that over a folder of phone photos once. It is usually a surprise.

Step 2: Strip it by re-encoding

The simplest way to remove everything in the browser is to decode the image and encode it again through a canvas. The canvas only knows pixels, so the new file has no EXIF at all: no GPS, no device, no timestamps.

async function stripMetadata(file, type = "image/jpeg", quality = 0.9) { // "from-image" applies the EXIF Orientation tag while decoding. const bitmap = await createImageBitmap(file, { imageOrientation: "from-image" }); const canvas = new OffscreenCanvas(bitmap.width, bitmap.height); canvas.getContext("2d").drawImage(bitmap, 0, 0); bitmap.close(); return canvas.convertToBlob({ type, quality }); } 

Then confirm the result really is clean:

const clean = await stripMetadata(file); console.log(await exifr.parse(clean)); // undefined: nothing left to read 

The orientation trap

Here is the bug. Phones often save the pixels sideways and add an Orientation tag that says "rotate this 90 degrees when you display it". Browsers respect that tag, so the photo looks fine on screen.

If you strip the metadata without baking the rotation into the pixels first, the tag disappears and the photo shows up sideways.

That is why the code above decodes with imageOrientation: "from-image". The bitmap comes out already rotated, so the pixels are correct and no tag is needed. If you write your own pipeline, test it with a portrait photo straight off a phone before you ship.

Trade-offs to know about

  • Re-encoding is lossy for JPEG and WebP. Each pass at quality 0.9 costs a little detail. If you want to remove metadata without touching pixels, use exiftool -all= -overwrite_original photo.jpg instead.
  • Color profiles are replaced, not dropped. A canvas works in its own color space, sRGB by default, and the encoder tags the output with it: in Chrome 154 the stripped JPEG still carried a 456-byte sRGB ICC profile, so the file is not profile-less. What does not survive is the original profile, so a wide-gamut source comes out converted to sRGB and can shift slightly.
  • HEIC needs converting first. Most browsers can't decode HEIC, so there is nothing to draw onto the canvas. Convert it to JPEG or WebP, which removes the metadata at the same time.
  • Do it before upload. Stripping on your server still means the original, with its GPS, was sent over the network and possibly logged.

A workflow for a product catalog

For a shop, the easy habit is to strip metadata in the same step where you already resize and compress:

  1. Export photos from the phone.
  2. Resize to the size your store actually displays (often 1200 to 2048 px).
  3. Convert to WebP or JPEG. The re-encode removes EXIF as a side effect.
  4. Spot-check one output file with exiftool or exifr.

If you'd rather not write code, I built a free EXIF viewer and remover that does steps 1 and 4 in the browser, and the bulk image compressor covers steps 2 and 3 for a whole folder. Both run locally, so the photos never upload.

Summary

  • Phone photos can carry your location, device and timestamps.
  • Check with exiftool or exifr before you publish.
  • A canvas re-encode removes all of it. Decode with imageOrientation: "from-image" so portrait photos stay upright.
  • Re-encoding is lossy and re-tags the file as sRGB; use exiftool when you need lossless removal.

Have you found GPS data in images on a site you run? I'm curious how common it still is.

I wrote this with help from an AI assistant. Both code samples were run against real phone photos and in a real browser before publishing.