Skip to content

File Signature (Magic Byte) Reference

The signatures — magic bytes — that sit in the first few bytes of a file. Use them when identifying file types and implementing validation.

What magic bytes are

Most file formats begin with a fixed byte sequence, known as the magic bytes or file signature. Operating systems and applications read it to decide what kind of file they are looking at. An extension can be changed in a second; the magic bytes are part of the binary itself, which is what makes them a dependable basis for validation.

The file command on Linux and PHP's finfo class both determine the MIME type this way.

Magic bytes of common formats (first N bytes) PNG JPEG GIF PDF ZIP WebP MP4 89 50 4E 47 .PNG FF D8 FF SOI marker 47 49 46 38 "GIF8" 25 50 44 46 "%PDF" 50 4B 03 04 "PK\x03\x04" 52 49 46 46 "RIFF" + WEBP at offset 8 66 74 79 70 "ftyp" at offset 4
Diagram: leading bytes (magic bytes) of common formats

Images

FormatExtensionMagic bytes (hex)ASCIIOffset
PNG.png89 50 4E 47 0D 0A 1A 0A‰PNG\r\n\x1a\n0
JPEG.jpg/.jpegFF D8 FF0
GIF87a.gif47 49 46 38 37 61GIF87a0
GIF89a.gif47 49 46 38 39 61GIF89a0
WebP.webp52 49 46 46 ?? ?? ?? ?? 57 45 42 50RIFF????WEBP0
BMP.bmp42 4DBM0
TIFF (little-endian).tiff/.tif49 49 2A 00II*\00
TIFF (big-endian).tiff/.tif4D 4D 00 2AMM\0*0
AVIF / HEIF.avif/.heif66 74 79 70ftyp4
ICO.ico00 00 01 000
SVG.svg<?xml or <svgText0 (no BOM)

Documents and archives

FormatExtensionMagic bytes (hex)Notes
PDF.pdf25 50 44 46 2D%PDF-
ZIP.zip50 4B 03 04PK\x03\x04
ZIP (empty archive).zip50 4B 05 06
GZIP.gz1F 8B
7-Zip.7z37 7A BC AF 27 1C7z\xBC\xAF'\x1C
RAR4.rar52 61 72 21 1A 07 00Rar!\x1A\x07\x00
RAR5.rar52 61 72 21 1A 07 01 00
TAR.tar75 73 74 61 72ustar (offset 257)
DOCX / XLSX / PPTX.docx and similar50 4B 03 04ZIP-based; told apart by extension
XLS (legacy).xlsD0 CF 11 E0 A1 B1 1A E1OLE2 container
DOC (legacy).docD0 CF 11 E0 A1 B1 1A E1OLE2 container

Text and data formats

FormatExtensionMagic bytes / BOMNotes
UTF-8 with BOM.txt, .csv and similarEF BB BFBOM(Byte Order Mark)
UTF-16 LE BOM.txt and similarFF FELittle-endian
UTF-16 BE BOM.txt and similarFE FFBig-endian
UTF-32 LE BOM.txt and similarFF FE 00 00
JSON (typical).json7B or 5B{ or [
CSV (typical).csvText (no BOM)No signature
XML.xml3C 3F 78 6D 6C<?xml

Audio and video

FormatExtensionMagic bytes (hex)Notes
MP3.mp3FF FB / FF F3 / FF F2Or an ID3 tag: 49 44 33
MP4 / M4A / M4V.mp4 and similar66 74 79 70ftyp (offset 4)
WAV.wav52 49 46 46 ?? ?? ?? ?? 57 41 56 45RIFF????WAVE
OGG.ogg/.ogv4F 67 67 53OggS
FLAC.flac66 4C 61 43fLaC
AVI.avi52 49 46 46 ?? ?? ?? ?? 41 56 49 20RIFF????AVI
MKV / WebM.mkv/.webm1A 45 DF A3EBML header

Executables and others

FormatExtensionMagic bytes (hex)Notes
ELF (Linux executable)none / .elf7F 45 4C 46\x7FELF
PE (Windows executable).exe/.dll4D 5AMZ
Mach-O (macOS executable)noneCF FA ED FE64bit
PHP script.php3C 3F 70 68 70<?php
SQLite.sqlite/.db53 51 4C 69 74 65 20 66 6F 72 6D 61 74 20 33 00SQLite format 3\0
ISO.iso43 44 30 30 31Offset 32769: CD001

Validating magic bytes in PHP

 ['offset' => 0, 'bytes' => "\x89PNG\r\n\x1a\n", 'length' => 8],
        'jpg'  => ['offset' => 0, 'bytes' => "\xFF\xD8\xFF",       'length' => 3],
        'gif'  => ['offset' => 0, 'bytes' => "GIF8",               'length' => 4],
        'pdf'  => ['offset' => 0, 'bytes' => "%PDF-",              'length' => 5],
        'zip'  => ['offset' => 0, 'bytes' => "PK\x03\x04",         'length' => 4],
        'exe'  => ['offset' => 0, 'bytes' => "MZ",                 'length' => 2],
    ];

    if (!isset($signatures[$expectedType])) {
        throw new \InvalidArgumentException("Unknown file type: $expectedType");
    }

    $sig = $signatures[$expectedType];
    $handle = fopen($filePath, 'rb');
    if ($sig['offset'] > 0) fseek($handle, $sig['offset']);
    $header = fread($handle, $sig['length']);
    fclose($handle);

    if (substr($header, 0, $sig['length']) !== $sig['bytes']) {
        throw new \RuntimeException("Magic bytes do not match (expected: $expectedType)");
    }
}

// Example use
validateMagicBytes($_FILES['upload']['tmp_name'], 'png');
echo "This is a genuine PNG file";

Related resources

❓ Frequently Asked Questions

Why is checking the extension not enough?
The extension is just part of the filename and an attacker can change it at will: rename shell.php to shell.jpg and an extension check passes. Magic bytes look at the actual start of the data, so renaming does not fool them. They are still weak against polyglot files that fake the first few bytes, so cross-check the extension, the MIME type and the magic bytes together.
How many bytes do I need to identify PNG and JPEG?
PNG needs eight bytes (89 50 4E 47 0D 0A 1A 0A) and JPEG only the first three (FF D8 FF). In practice reading about twelve bytes covers all the common formats at once, including ZIP (50 4B 03 04) and PDF (25 50 44 46). There is no need to load the whole file into memory.
Office documents and APKs are detected as ZIP.
That detection is correct. docx, xlsx, pptx, APK, JAR and EPUB are all ZIP containers and all start with 50 4B 03 04. Telling them apart means opening the archive and looking inside — [Content_Types].xml for docx, AndroidManifest.xml for an APK. You cannot allow docx and reject APK on magic bytes alone.