Deep Dives

What Is HEIC? A Technical Deep Dive into File Signatures, Detection, and Conversion

koboshiCo-founder
·6 min read
What Is HEIC? A Technical Deep Dive into File Signatures, Detection, and Conversion
Summary

HEIC is not just a smaller JPEG. It is a container format based on ISO Base Media File Format with HEVC-encoded images inside. Here is how it works, how to detect it by reading raw bytes, and how to convert it when compatibility fails.

Open a HEIC file in a hex editor. The first twelve bytes:

00 00 00 18 66 74 79 70 68 65 69 63

That is ISOBMFF — the same container standard MP4 uses. 0x18 (24 bytes) is the box size, ftyp is the file type box, heic is the major brand. A HEIC file is not an image encoding. It is a container with a brand marker, holding HEVC-encoded images.

What HEIC Actually Is

HEIC = High Efficiency Image Container. Apple's branded take on HEIF, standardized by MPEG as ISO/IEC 23008-12 in 2015. JPEG is both a compression algorithm and a file format. HEIC is only a container. The compression inside is HEVC (H.265), the same codec used for 4K video.

One HEIC file can hold:

  • One primary image
  • Multiple alternate images (burst shots)
  • Image sequences (Live Photos: still + 3-second video)
  • Alpha channels and depth maps
  • 16-bit color depth per channel (JPEG caps at 8-bit)

The container uses boxes — same structure as MP4:

BoxPurpose
ftypFile type and compatibility brands
metaItem metadata and properties
mdatRaw encoded image data (HEVC bitstream)
ilocItem locations within mdat

That extensibility lets HEIC do things JPEG never could. Parsing it means understanding the box hierarchy, not just reading a raw bitstream.

Why Apple Switched

Apple did not invent HEIC. MPEG published HEIF in 2015. Apple adopted it as the iPhone default in iOS 11 (2017).

The switch was arithmetic, not marketing. A 12MP iPhone photo in JPEG is ~3.5 MB. In HEIC, ~1.8 MB. At 50 GB of free iCloud storage, that gap means roughly 14,000 extra photos. Apple sells storage tiers. The math writes itself.

There was also ecosystem alignment. Apple already used HEVC for video (H.265 in iOS 11). Reusing the same codec for still images meant shared hardware decode blocks on A-series chips, lower power draw, and one licensing path.

The Trade-offs

HEIC beats JPEG on nearly every metric except compatibility.

AspectHEICJPEG
Compression efficiency~40–50% smaller at same qualityBaseline
Color depthUp to 16-bit8-bit
TransparencyYesNo
Multiple images per fileYesNo
Lossless re-editYesNo
Native Windows supportRequires HEIF extensionUniversal
Web browser supportSafari onlyUniversal
Android supportNative on 9+Universal
Patent licensingHEVC patent poolJPEG is royalty-free

HEVC sits under multiple patent pools (MPEG LA, Access Advance). Apple covers the fees for iOS users. Third-party vendors do not have that luxury. That uncertainty is a large part of why adoption outside the Apple ecosystem stalled.

Detecting HEIC by Reading the File Signature

Do not trust the .heic extension. Read the first 32 bytes and parse the ftyp box.

Exact byte layout of a valid HEIC header:

Bytes 0–3:   Box size (big-endian uint32)
Bytes 4–7:   Box type: "ftyp" (0x66 0x74 0x79 0x70)
Bytes 8–11:  Major brand: "heic" or "heif" or "mif1"
Bytes 12–15: Minor version (usually 0x00000000)
Bytes 16+:   Compatible brands list (e.g., "mif1", "heic", "MiHE")

TypeScript in the browser:

async function isHeic(file: File): Promise<boolean> {
  const buffer = await file.slice(0, 32).arrayBuffer()
  const bytes = new Uint8Array(buffer)

  if (String.fromCharCode(...bytes.slice(4, 8)) !== "ftyp") return false

  const brand = String.fromCharCode(...bytes.slice(8, 12))
  return ["heic", "heif", "mif1", "msf1"].includes(brand)
}

Our converter runs this check before attempting decode. Wrong brand = instant rejection, no wasted CPU.

Python

def is_heic(path: str) -> bool:
    with open(path, "rb") as f:
        header = f.read(32)

    if len(header) < 12:
        return False

    if header[4:8].decode("ascii", errors="ignore") != "ftyp":
        return False

    return header[8:12].decode("ascii", errors="ignore") in {"heic", "heif", "mif1", "msf1"}

Higher-level option with filetype:

import filetype

kind = filetype.guess("photo.heic")
if kind and kind.extension in ("heic", "heif"):
    print(kind.mime)  # image/heic

Or pillow-heif:

from pillow_heif import is_heif

if is_heif("photo.heic"):
    # valid container
    pass

Go

func isHeic(path string) bool {
	f, err := os.Open(path)
	if err != nil {
		return false
	}
	defer f.Close()

	buf := make([]byte, 32)
	if _, err := f.Read(buf); err != nil {
		return false
	}

	if string(buf[4:8]) != "ftyp" {
		return false
	}

	brand := string(buf[8:12])
	return brand == "heic" || brand == "heif" || brand == "mif1" || brand == "msf1"
}

PHP

function isHeic(string $path): bool {
    $header = file_get_contents($path, false, null, 0, 32);
    if (strlen($header) < 12) return false;
    if (substr($header, 4, 4) !== 'ftyp') return false;

    return in_array(substr($header, 8, 4), ['heic', 'heif', 'mif1', 'msf1'], true);
}

With fileinfo:

$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file('photo.heic');
// image/heic or image/heif

ImageMagick CLI

ImageMagick 7+ with libheif:

magick identify -verbose photo.heic | grep "Format:"
# Format: HEIC (High Efficiency Image Container)

Without libheif, you get no decode delegate for this image format. On most Linux distros, libheif-examples provides heif-convert as a fallback.

The Conversion Path

Knowing a file is HEIC does not open it. Windows needs the HEIF Image Extensions. Most browsers refuse to render HEIC in <img> tags. Android 9+ handles it natively; older devices do not.

The fix: convert. Our HEIC to JPG converter runs the entire pipeline in your browser:

  1. Drop files — single photos or entire folders, batch processing automatic.
  2. Signature validation — the exact byte check above runs on every file. Non-HEIC files are rejected immediately with a clear mismatch message.
  3. Client-side decoding — a WebAssembly build of libheif runs locally. No uploads. No server.
  4. Quality-preserving output — JPG at 90% quality keeps original resolution and color accuracy.
  5. Batch download — individual files or a single ZIP archive.

For lossless output with transparency: HEIC to PNG. For web-optimized sizes: HEIC to WebP.

Your files never leave your device. That is the point.

More blog posts to read