Learning & reference

File signatures: identify a file by its first bytes

The extension lies. The magic number at the start of a file is what really identifies the format — here is the hex for every format you are likely to test, plus the traps where signatures collide.

Why the extension is not the file type

A file name is just a label a user can change. The bytes at the start of the file - the magic number or file signature - are what actually identify the format. This is how file(1), browsers, virus scanners and well-written upload validators decide what they are looking at.

The upload-validation rule

Never trust the extension or the Content-Type header - both are supplied by the client and both are trivially faked. Read the first bytes and match a signature, then check the extension agrees.

Renaming proves it

Rename photo.png to invoice.pdf and upload it. A validator that only looks at the name accepts it; one that reads 89 50 4E 47 rejects it. That is the single most common file-upload bug.

Signatures can collide

Every OOXML and OpenDocument file is a ZIP, so DOCX, XLSX, PPTX, ODT, EPUB, JAR and APK all begin PK 03 04. You must look inside the archive to tell them apart.

File signature table

Offset is where the magic starts. .. means "any byte".

FormatExtensionHex signatureASCIIOffsetNotes
PNG.png89 50 4E 47 0D 0A 1A 0A.PNG....0The CRLF/EOF bytes are a deliberate trap that detects a corrupted text-mode transfer.
JPEG.jpg .jpegFF D8 FFÿØÿ0Ends with FF D9. A truncated JPEG still has a valid header.
GIF (87a).gif47 49 46 38 37 61GIF87a0Original 1987 spec, no animation.
GIF (89a).gif47 49 46 38 39 61GIF89a0Adds animation and transparency.
PDF.pdf25 50 44 46 2D%PDF-0Version follows, e.g. %PDF-1.7. Ends with %%EOF.
ZIP.zip50 4B 03 04PK..0PK 05 06 = empty archive, PK 07 08 = spanned.
GZIP.gz1F 8B 08..0Third byte is the compression method (08 = deflate).
BZIP2.bz242 5A 68BZh0Next byte is the block-size digit 1-9.
7-Zip.7z37 7A BC AF 27 1C7z...0
RAR.rar52 61 72 21 1A 07 00Rar!...0RAR5 appends 01 00.
TAR.tar75 73 74 61 72ustar257Not at byte 0 - the magic sits 257 bytes in, inside the first header block.
BMP.bmp42 4DBM0
TIFF (LE).tif .tiff49 49 2A 00II*.0Intel byte order.
TIFF (BE).tif .tiff4D 4D 00 2AMM.*0Motorola byte order.
WebP.webp52 49 46 46 .. .. .. .. 57 45 42 50RIFF....WEBP0 and 8RIFF container - bytes 4-7 are the length.
WAV.wav52 49 46 46 .. .. .. .. 57 41 56 45RIFF....WAVE0 and 8Same RIFF container as WebP/AVI.
AVI.avi52 49 46 46 .. .. .. .. 41 56 49 20RIFF....AVI 0 and 8
ICO.ico00 00 01 00....0Third byte is the type: 1 = icon, 2 = cursor.
MP3 (ID3).mp349 44 33ID30Tagged files start with ID3; raw frames start FF FB.
MP4 / MOV.mp4 .m4a .mov66 74 79 70ftyp4Offset 4 - the first 4 bytes are the box length.
OGG.ogg .opus4F 67 67 53OggS0
FLAC.flac66 4C 61 43fLaC0
Photoshop.psd38 42 50 538BPS0
SQLite DB.db .sqlite53 51 4C 69 74 65 20 66 6F 72 6D 61 74 20 33 00SQLite format 3.016 bytes including the trailing NUL.
ELF binary(none)7F 45 4C 46.ELF0Linux/Unix executables and shared objects.
Windows EXE/DLL.exe .dll4D 5AMZ0The DOS stub; initials of Mark Zbikowski.
Java class.classCA FE BA BE....0The original "cafe babe".
WOFF.woff77 4F 46 46wOFF0Web font, a compressed sfnt wrapper.
WOFF2.woff277 4F 46 32wOF20Brotli-compressed successor.
TrueType font.ttf00 01 00 00 00.....0
OpenType (CFF).otf4F 54 54 4FOTTO0
RTF.rtf7B 5C 72 74 66{\rtf0
XML.xml .svg3C 3F 78 6D 6C<?xml0May be preceded by a UTF-8 BOM.

The ZIP-family trap

These all share one signature. To identify them you have to open the container and look for a marker entry.

FormatSignatureHow to actually tell
DOCX / XLSX / PPTX50 4B 03 04Contains [Content_Types].xml; the part names say which Office app.
ODT / ODS / ODP50 4B 03 04First entry is an uncompressed mimetype file naming the format.
EPUB50 4B 03 04First entry is mimetype containing application/epub+zip.
JAR / APK50 4B 03 04Contains META-INF/MANIFEST.MF; APK adds AndroidManifest.xml.

QA trap: a scanner that identifies by signature alone will label every one of these "ZIP archive". A policy that blocks .zip uploads but allows .docx is therefore not really blocking anything.

Byte-order marks (BOM)

A BOM is a signature for text encoding. It is invisible in an editor and breaks parsers that do not strip it.

EncodingBOM bytesNotes
UTF-8EF BB BFOptional and often unwanted - it shows up as  in the first field of a CSV.
UTF-16 LEFF FEWindows "Unicode" default.
UTF-16 BEFE FF
UTF-32 LEFF FE 00 00Ambiguous with UTF-16 LE unless you check 4 bytes.

QA trap: a UTF-8 BOM makes the first CSV header id instead of id, so a lookup for the "id" column fails on a file that looks perfect on screen.

Check a signature yourself

Linux / macOS

file myfile.bin identifies by signature.
xxd -l 16 myfile.bin shows the first 16 bytes.

Windows PowerShell

Format-Hex -Path myfile.bin -Count 16

In your browser

Drop a file into the file hash verifier - it reads the bytes locally and never uploads anything.

Test it with real files

The catalog has valid and deliberately-corrupted files for most formats above - ideal for proving your validator reads bytes, not names.

Browse test files MIME types →