Build1 publisher3 min readPublished
Express upload guide moves the GPS-tag check to the re-encoded file it publishes
Express image uploads should be re-encoded and the exact output parsed for seven GPS EXIF tags before a public URL is returned, a dev.to guide argues. That boundary costs quality and bandwidth, and the sample checks fewer location fields than the post's own rule lists.
The Engineer · Build desk

What happened
- A dev.to guide for public Express image endpoints says to decode the pixels, re-encode a fresh copy and inspect that exact output for GPS EXIF before returning a URL.
- The author once trusted an upstream strip step and still found a location field in a test fixture; the audit flagged it as META_GPS_01.
- In the guide's design the Express handler only orchestrates, and the object key stays private until the image worker's verification passes.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- cost Teams moving from a cheap lossless metadata rewrite to a full re-encode pay in image quality and bandwidth, and have to measure that loss on their own images.
- constraint Any thumbnailer, orientation fixer or CDN transform running after the audit writes new bytes, so the guarantee holds only if the check runs on the file actually served.
- contradiction The sample enforces a narrower rule than the post states, so a file carrying GPSPosition or a maker-note location would not be flagged by the code as written.
- decision Evidentiary files, medical scans and lossless diagrams force a two-store design, with the original held in restricted storage and only a derived copy published.
Checking `original.getexif()` "only proves what arrived, not what a downstream encoder emitted," the author wrote [8]. So the post runs the assertion after `save`, on the candidate bytes the function is about to return [7]. "The useful lesson was boring: inspect the final bytes," the author wrote [17].
The audit is in Python with Pillow. The same function can run in a worker, a release test or a local fixture while the request handler stays in Express [6]. It opens the upload once to `verify()` it, then a second time to convert to RGB. A code comment says the conversion is there so "the output does not inherit the source container" [7]. After the metadata test, it also rejects any change in width or height [7]. I like that second assertion. Without it, an encoder that changed the dimensions would pass the privacy test and still break the published image.
The tag set is narrower than the rule. The invariant bans a GPS IFD, GPSLatitude, GPSLongitude, GPSPosition and maker-note location equivalents [3]. `GPS_NAMES` has seven entries: latitude and longitude with their reference tags, altitude, and destination latitude and longitude [7]. GPSPosition is not in the set, and the function never parses maker notes [1]. Before this check gated a public bucket, I would want a fixture for each location form the invariant names, and a test showing the assertion trips on every one.
The post does not put a number on the quality or bandwidth cost. The function defaults to JPEG quality 88 with `optimize=True` [7]. The author's workload is an edtech app. A tutor may photograph a worksheet at home, and the same path may publish a student profile image [16]. For tiny worksheet text, the author calls a quality setting alone "a weak policy." The recommendation is to measure OCR or human readability on representative images and set a minimum dimension as well [11]. A default of 88 is a claim about the author's images. It transfers only if your text is set at a similar size and your readers put up with similar compression artifacts.
Byte limits sit in stage one, next to checks on declared and detected media type and on dimensions [5]. They bound decode work. Coordinates survive at any file size. A 12 MB phone photo may be rejected before decode, while a 300 KB thumbnail can still reveal the same coordinates [12]. The thumbnail is one-fortieth the size of the photo [2]. The post says to use the metadata assertion as the privacy test and never output size [12].
Decode cost also decides how the handler is built. According to the post, a queue helps when that cost is spiky. Synchronous processing is fine for small profile images if the request timeout is explicit [14]. Each request should keep a correlation ID and record input and output hashes, dimensions, encoder settings and the metadata verdict [15]. With both hashes on record, a review can show which bytes the worker cleared. The upload path here publishes student photos to a CDN, and a surviving GPS tag in a CDN object counts as a privacy incident [16]. In that context I think the re-encode cost is worth paying.
What to watch
- Whether the author publishes measured readability or byte-size changes for worksheet photos re-encoded at quality 88.
- Whether the sample's GPS_NAMES set is extended to cover GPSPosition and maker-note location fields, matching the post's own invariant.