CleanExif is a static browser-only image metadata inspector and cleaner. It reads EXIF, XMP, IPTC, comments, PNG text metadata, and PNG C2PA/JUMBF Content Credentials where the browser libraries can access them, then creates cleaned downloadable images in the browser.
Live app: https://0k-lab.github.io/cleanexif/
No backend is required. Images stay local in the browser and are never uploaded by this app.
- Drag-and-drop or file picker image selection
- Per-image preview, filename, type, dimensions, and size
- Searchable metadata table for readable EXIF/XMP/IPTC/comments/PNG text tags
- AI-generation metadata hints for common prompt, workflow, model, seed, software, and C2PA provenance fields
- Per-image cleaned download that strips metadata without re-encoding PNG, JPEG, and WebP when possible
- Batch cleaned downloads, with ZIP output for multiple images
- Before/after metadata check on cleaned blobs
- GitHub Pages compatible Vite build
This sample pair shows a PNG image with C2PA/JUMBF AI provenance metadata and the cleaned file produced by CleanExif. Both images remain 1536 x 1024 PNGs; the visible pixels are unchanged.
The original sample intentionally keeps its C2PA provenance block so the example can demonstrate metadata removal. It does not contain GPS coordinates, local file paths, or personal author fields.
| Original with metadata | Cleaned output |
|---|---|
![]() |
![]() |
Run this comparison with ExifTool:
diff <(exiftool -x FileModifyDate -x FileAccessDate -x FileInodeChangeDate -x FilePermissions docs/examples/image1_chatgpt.png) \
<(exiftool -x FileModifyDate -x FileAccessDate -x FileInodeChangeDate -x FilePermissions docs/examples/image1_chatgpt-clean.png)Abridged output:
2c2
< File Name : image1_chatgpt.png
---
> File Name : image1_chatgpt-clean.png
15,50d14
< JUMD Type : (c2pa)-0011-0010-800000aa00389b71
< JUMD Label : c2pa
< C2PA Icon Type : image/svg+xml
< C2PA Actions V2 Salt : e2bfff9b87457e59c83ba6e2d999ed1b
< Actions Action : c2pa.created, c2pa.converted, c2pa.watermarked.unbound
< Actions When : 2026:07:01 00:00:00Z, 2026:07:01 00:00:00Z, 2026:07:01 00:00:00Z
< Actions Software Agent Name : gpt-image
< Actions Software Agent Version : 2.0
< Actions Digital Source Type : http://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia
< Ocsp Vals : [long certificate status data omitted]
< Name : jumbf manifest
< Alg : sha256
< Instance ID : xmp:iid:0e4c6de6-b33f-4050-818e-407ddae58be7
< Claim Generator Info Name : OpenAI Media Service API
< Claim Generator Info Spec Version: 2.2.0
< Signature : self#jumbf=/c2pa/urn:c2pa:0ec25b22-eb96-445c-b881-3e9ef018e4bb/c2pa.signature
< Created Assertions Url : self#jumbf=c2pa.assertions/c2pa.icon, self#jumbf=c2pa.assertions/c2pa.actions.v2, self#jumbf=c2pa.assertions/c2pa.certificate-status, self#jumbf=c2pa.assertions/c2pa.hash.data
< Title : image.png
< Item 0 : (Binary data 1877 bytes, use -b option to extract)
< Item 1 Sig Tst 2 Tst Tokens Val : (Binary data 5258 bytes, use -b option to extract)
< Item 1R Vals Ocsp Vals : (Binary data 1058 bytes, use -b option to extract)
< Item 1 Pad : (Binary data 9493 bytes, use -b option to extract)
< Item 2 : null
< Item 3 : (Binary data 64 bytes, use -b option to extract)The filename difference is expected. The deleted block is the embedded
C2PA/JUMBF provenance metadata, including the gpt-image software agent,
digital source type, manifest, signature, and related assertion data. Structural
PNG fields such as file type, MIME type, width, and height remain.
npm install
npm run devBuild the static app:
npm run buildPreview the production build:
npm run previewThis project includes .github/workflows/deploy-pages.yml, which builds pull requests and deploys dist/ to GitHub Pages on pushes to main.
- Push the repository to GitHub.
- In GitHub, open Settings > Pages.
- Set Build and deployment to GitHub Actions.
- Push to
main.
The workflow sets Vite's BASE_PATH to /${{ github.event.repository.name }}/, so the app works when hosted from a GitHub Pages project subpath such as https://USER.github.io/REPOSITORY/.
For local builds, vite.config.js uses ./ as the default base path so the generated files remain portable.
The app includes a descriptive title and meta description, canonical URL, Open Graph and Twitter summary tags, JSON-LD for the web app and FAQ content, robots.txt, sitemap.xml, site.webmanifest, and llms.txt.
The current canonical and sitemap URL is https://0k-lab.github.io/cleanexif/. If you deploy under a different GitHub owner, repository name, or custom domain, update this URL in:
index.htmlpublic/robots.txtpublic/sitemap.xmlpublic/llms.txt
CleanExif has no analytics, tracking scripts, or upload endpoints. Metadata inspection, byte-level metadata stripping, fallback canvas export, before/after checks, and ZIP creation all happen in the user's browser.
Cleaned downloads remove known metadata containers from PNG, JPEG, and WebP without re-encoding the compressed image data, preserving the original quality and compression for those formats. There is no JPEG quality or output-format setting for the normal cleaning path because changing those settings would require re-encoding. If a format cannot be stripped safely in bytes, the app falls back to browser canvas export; that fallback can recompress the image, change file size, change output format, and flatten animated images to a single frame. PNG and WebP fallback output preserves transparency when the browser encoder supports it. JPEG fallback output uses a white background for transparent pixels.

