A JPEG is a stream of segments, each announced by a two-byte marker. Once you know the marker system you can read the structure of any JPEG in a hex editor, find exactly where its metadata lives, and understand why certain edits leave traces behind. This page covers the whole layout.
The structure
Every marker is FF followed by a code byte. The file starts with FF D8 (Start of Image) and ends with FF D9 (End of Image). Between them, most markers are followed by a two-byte length and their data. The compressed pixels come last, after FF DA.
The marker system
| Marker | Name | Contains |
|---|---|---|
FF D8 | SOI | Start of Image β no length |
FF E0 | APP0 | JFIF header, density, optional thumbnail |
FF E1 | APP1 | EXIF or XMP metadata |
FF E2 | APP2 | ICC colour profile |
FF ED | APP13 | Photoshop IRB, IPTC data |
FF DB | DQT | Quantisation table |
FF C0 | SOF0 | Baseline frame header |
FF C2 | SOF2 | Progressive frame header |
FF C4 | DHT | Huffman table |
FF DD | DRI | Restart interval |
FF DA | SOS | Start of Scan β compressed data follows |
FF D0βFF D7 | RST0β7 | Restart markers inside the data |
FF FE | COM | Free-text comment |
FF D9 | EOI | End of Image β no length |
A typical camera JPEG looks like this in order:
Where the metadata lives
APP1 is the important segment for anyone thinking about privacy or provenance.
The EXIF block is a TIFF file embedded inside a JPEG, with its own byte-order marker and its own directory structure. This is a historical artefact β EXIF was defined as an extension of TIFF β and it explains why EXIF parsers look like TIFF parsers and why the byte order inside the EXIF block can differ from anything else in the file.
| EXIF directory | Typically contains |
|---|---|
| IFD0 | Camera make and model, orientation, resolution, software, timestamps |
| Exif IFD | Exposure, aperture, ISO, focal length, flash, lens |
| GPS IFD | Latitude, longitude, altitude, direction |
| IFD1 | The embedded thumbnail |
| Interop IFD | Compatibility identifiers |
| Maker notes | Vendor-specific, often including a serial number |
π¨ The thumbnail that outlives the edit
IFD1 holds a small JPEG preview β typically 160 Γ 120 β so that image browsers can show a grid quickly without decoding full-size files.
The problem is that some editors write a modified main image and leave the thumbnail untouched. A photo that was cropped to remove someone, or had a region painted over to redact it, can still carry a thumbnail showing the original, unedited scene. This has produced real disclosures β the visible image is clean and the metadata is not.
Always strip metadata rather than trusting an editor to update it:
One caution that catches people: stripping all metadata also removes the Orientation tag. If the pixels were stored sideways with a tag telling viewers to rotate them, removing the tag leaves the photo permanently sideways. Bake the rotation into the pixels first with magick -auto-orient, then strip.
DQT β the quality setting, made concrete
The quantisation table is 64 values, one per DCT coefficient. Each stored coefficient is divided by its table entry and rounded, which is the step where information is discarded.
There is no "quality: 85" field anywhere in a JPEG. The quality setting exists only in the encoder β it scales a reference table, and what is stored is the result. Tools that report a quality figure are inferring it by comparing the embedded table against known references.
This is also how JPEG forensics works. Different cameras and software use characteristic tables, so the DQT can indicate which program produced a file, and the presence of tables inconsistent with the claimed source suggests re-encoding.
SOF β dimensions and subsampling
Each component is three bytes: an identifier, sampling factors packed into one byte, and which quantisation table it uses. The sampling factors encode the chroma subsampling:
| Y factor byte | Subsampling | Colour resolution |
|---|---|---|
0x11 | 4:4:4 | Full |
0x21 | 4:2:2 | Half horizontally |
0x22 | 4:2:0 | Half in both directions |
That single byte determines whether red text in the image will look sharp or fringed. 0x22 β the default for photographs β discards three quarters of the colour information, which is invisible on a photograph and very visible on saturated text.
Byte stuffing
Compressed data is a bitstream, and it can naturally produce a byte with the value FF. Since FF introduces every marker, a decoder would misread it as the start of a segment.
The solution is byte stuffing: the encoder writes FF 00 whenever the data produces a genuine FF, and the decoder discards the 00. So FF 00 in the compressed region is data, and FF followed by anything else is a real marker.
β οΈ Data hides after EOI
Most decoders stop at FF D9 and ignore everything after it. That makes the tail of a JPEG a convenient hiding place β a common technique appends a complete ZIP archive after EOI, producing a file that is simultaneously a valid image and a valid archive.
If you accept image uploads, truncate at FF D9, or better, re-encode every upload. Re-encoding produces a file containing only what your own encoder wrote, which removes appended data, unexpected segments and malformed structures in one step.
Baseline and progressive
| Baseline (SOF0) | Progressive (SOF2) | |
|---|---|---|
| Structure | One scan, top to bottom | Several scans of increasing detail |
| Partial load | Top portion, sharp | Whole image, blurry, then sharpens |
| File size | Baseline | 2β10% smaller |
| Decode cost | Lower | Higher β multiple passes |
| Memory | Row by row | Whole image buffered |
Progressive is usually the better choice on the web: smaller files, and a full-frame preview appears sooner, which measures better on perceived-performance metrics. The exception is very large images on memory-constrained devices, where buffering the whole frame is a real cost.
Operations that do not re-encode
Because the compressed data is organised in 8Γ8 blocks, some transformations can be performed by rearranging blocks without decoding them. No quality is lost at all.
-optimize is worth knowing: it recomputes the Huffman tables for the actual data rather than using generic ones, typically saving 3β8% with no quality change whatsoever. Many encoders skip it because it requires a second pass.
Rotation is only fully lossless when the dimensions are multiples of 8 (or 16 with subsampling). Otherwise the edge blocks cannot be rearranged cleanly and the tool either trims a few pixels or falls back to re-encoding.
Inspecting a file
Strip metadata before you share
Remove GPS coordinates, camera details and embedded thumbnails in your browser β the photo is never uploaded anywhere.
Open the EXIF Remover βSummary
- Every marker is
FFplus a code. Files run fromFF D8toFF D9. - EXIF lives in APP1 and is a complete TIFF structure embedded inside the JPEG.
- The embedded thumbnail can survive an edit and show the original scene.
- There is no quality field β only the quantisation table the setting produced.
- One byte in SOF sets chroma subsampling, which decides whether text looks sharp.
FF 00is stuffed data; any otherFFpair is a real marker.- Data after EOI is ignored by decoders. Re-encode uploads to remove it.
- Rotation, progressive conversion and Huffman optimisation are lossless via
jpegtran.
Frequently Asked Questions
What are the first bytes of a JPEG file?
FF D8, the Start of Image marker, usually followed immediately by FF E0 or FF E1 for the first application segment. Every JPEG begins FF D8 and ends FF D9, and every marker in between begins with an FF byte followed by a code identifying what it is.
Where is EXIF data stored in a JPEG?
In the APP1 segment, marker FF E1, which begins with the string Exif followed by two null bytes. The EXIF block itself is a TIFF structure β complete with its own byte order marker β embedded inside the JPEG, which is why EXIF parsing code so often looks like TIFF parsing code.
Why does a JPEG contain a thumbnail and why does that matter?
Cameras embed a small preview in the EXIF block so image browsers can display it quickly. The privacy issue is that some editors update the main image without regenerating the thumbnail, so a photo that was cropped or redacted can still contain a thumbnail showing the original.
What is byte stuffing in JPEG?
Compressed image data can contain a legitimate FF byte, which would otherwise look like the start of a marker. The encoder inserts a 00 after every such FF, and the decoder removes it. That is why you see FF 00 sequences throughout the compressed data of every JPEG.
What is the difference between baseline and progressive JPEG?
Baseline stores the image in one top-to-bottom pass, marked by SOF0. Progressive stores it in several passes of increasing detail, marked by SOF2, so a partial download shows the whole image blurry rather than the top part sharp. Progressive files are usually 2 to 10% smaller as well.