Guides

Inside a ZIP: local headers, the central directory, and why salvage works

A ZIP is unusual among file formats: it writes its table of contents twice. Every entry carries its own little header next to its data, and the whole archive is summarized again in an index at the very end. Most tools only ever read the index — which is why a ZIP whose tail was cut off looks empty even though the files are right there. That same double bookkeeping is the reason salvage works so well.

How a ZIP is laid out, front to back

A ZIP is not one compressed blob. It is a sequence of independently compressed entries, one per file, followed by an index that catalogues them. Each entry is a local file header — carrying the filename, the compression method, and the sizes — immediately followed by that file's compressed data. Entries are simply concatenated, one after another, in the order they were added.

A ZIP, front to back: each file sits behind its own local header, and a central directory at the end catalogues them all with pointers back to where each one starts.

After the last entry comes the central directory: a run of records, one per entry, each repeating the filename and metadata and adding the byte offset where that entry's local header sits earlier in the file. It ends with a small End Of Central Directory record that states how many entries exist and where the directory begins. Very large archives add ZIP64 variants of these records to carry offsets bigger than four gigabytes, but the shape is the same.

The file list is stored twice, on purpose

Here is the detail that defines how ZIPs break and how they heal: the metadata for every entry exists in two places. Once in the local file header next to the data, and again in the central directory at the end. This is not an accident or waste — it is what makes the format both streamable and randomly accessible. A writer can emit entries on the fly, one local header and its data at a time, without knowing the full list in advance, then stamp the complete catalogue at the end once everything is written.

One wrinkle follows from streaming: when a writer doesn't know a file's compressed size before writing it, it sets the sizes in the local header to zero and appends a small data descriptor after the data with the real numbers. The authoritative sizes then live only in the data descriptor and the central directory — a subtlety that matters when the central directory is gone and salvage has to work from the front.

Why readers trust the end, and why that backfires

Almost every ZIP tool opens an archive the same way: it seeks to theend, finds the End Of Central Directory record, reads the central directory, and presents the list of files. It never scans the body unless you ask it to extract something, at which point it jumps straight to the offset the directory gave it. Reading the index first is fast and lets a tool list a huge archive instantly.

The backfire is obvious once you see the layout. The central directory is the last thing in the file, so it is the first thing lost when a download stops early, a copy is interrupted, or a transfer is truncated. With the tail gone, the reader can't find a valid End Of Central Directory record and declares the archive corrupt or empty — even though every compressed file is sitting intact in the body, each behind its own local header. This is the precise mechanism behind"unexpected end of archive", and it is why the symptom looks so much worse than the reality.

How salvage rebuilds the archive

Because the file list was stored twice, losing the central directory is recoverable. Salvage simply stops trusting the missing index and reads the archive the other way — from the front.

A truncated ZIP: the central directory is gone with the tail, but each file still sits behind its own local header. Salvage scans forward, finds those headers, and rebuilds the index.

The scan walks the body looking for the four-byte signature that marks a local file header. At each one it reads the entry's name, compression method, and sizes, then follows the compressed data to the next signature. Every entry it finds is decompressed and its data verified against the CRC stored with the entry, so a recovered file is not just extracted but checked. From the entries it recovers, it writes a brand-new central directory and End Of Central Directory record, producing a clean archive that ordinary tools open without complaint. The distributed local headers are, in effect, a backup index the format gave you for free.

What salvage can't bring back

The redundancy is generous but not infinite, and an honest tool draws the line clearly.

  • The truncated last entry. When a download is cut off, the final file's compressed data is usually incomplete. Its header is there, but the bytes stop partway. Deflate can't resynchronize after a gap, so that entry recovers only up to the cut — sometimes usable, sometimes not.
  • Mid-stream corruption inside an entry. A bad byte in the middle of a compressed file breaks the deflate stream from that point on; the entry decompresses cleanly up to the damage and fails after it. The CRC check flags exactly this.
  • An encrypted archive without the password. Salvage can recover the structure, but it can't decrypt the contents without the key.
  • A file that is only the tail. If what survived is the central directory but not the body, there are pointers to data that isn't present — an index with nothing to index.

For the family of archive formats and how their differences change the odds, see RAR vs 7z when the archive is damaged.

FAQ

What is the central directory in a ZIP file?

It is the archive's master index, written at the very end of the file. It holds one record per entry — the filename, compression method, sizes, and, crucially, the byte offset where that entry's data begins earlier in the file — followed by an End Of Central Directory record that says how many entries there are and where the directory starts. Almost every ZIP reader opens the file by seeking to the end and reading this directory first, which is why damage to the tail makes the whole archive look unreadable.

What is the difference between a local file header and the central directory?

They describe the same entries from two places. A local file header sits immediately before each file's compressed data, in the body of the archive, and carries that entry's name and metadata. The central directory is a single consolidated list of all entries at the end of the file, with pointers back to each local header. The local headers are distributed and travel with the data; the central directory is centralized and read first. Having both is deliberate redundancy.

Can a ZIP be recovered if the central directory is missing?

Usually a large fraction of it, yes. Because every entry also has a local file header next to its data, a salvage can ignore the missing index and scan the archive from the front, finding each local header by its signature, reading the entry, and rebuilding a fresh central directory from what it finds. This is exactly how recovery handles a truncated download that lost its tail.

Why does my ZIP say it is empty or corrupt when the files are inside?

Because the reader looked for the central directory at the end and didn't find a valid one — often because the download or copy was cut short and the tail is gone. The compressed files are still present in the body of the archive, each behind its own local header; the reader simply has no index telling it they exist. Scanning for the local headers directly finds them.

Related reading:"unexpected end of archive" explained,RAR vs 7z, and how the same ZIP-of-XML idea underpins Office files inwhat "corrupt" really means for a DOCX.