Blog · EPUB

What Is an EPUB File? Structure, Versions and How It Works

An ebook that is really a small website

An EPUB file ends in .epub, and if you rename a copy to .zip you can open it with any archive program. Inside is a handful of XML files, one HTML-like page per chapter, a stylesheet or two, and the images. Your reading app is a very small web browser that knows how to flip those pages in the right order.

That design explains almost everything people notice about the format. Text reflows when you change the font size because it is HTML, not a fixed page. The file is small because the pages are text. And most problems are boring ones: a path that does not match, a missing field, a file in the wrong place.

What is actually inside

Unzip any EPUB and you will find the same skeleton.

PartWhat it is for
mimetypeA tiny file containing exactly application/epub+zip. It must be the first entry in the archive and stored without compression, so that programs can recognise an EPUB by looking at the first bytes.
META-INF/container.xmlTells the reader where the package document lives. Nothing else in the book is found until this file is read.
Package document (.opf)The book’s index card: metadata (title, author, language, identifier), a manifest of every file, and the spine, which is the reading order.
NavigationAn HTML nav document in EPUB 3, or an .ncx file in EPUB 2. This is the table of contents you see in the app.
Content documentsXHTML files, usually one per chapter or section.
ResourcesStylesheets, images, fonts, and in EPUB 3 optionally audio and video.

The manifest and the spine are two different things, and mixing them up causes a lot of confusion. The manifest lists every file the book is allowed to use. The spine picks which of those files are read, and in which order. A chapter can be in the manifest and absent from the spine; the reader then never shows it.

EPUB 2 and EPUB 3

EPUB 2 arrived in 2007 and EPUB 3 in 2011. Today the standard is maintained by the W3C, and the current revision is EPUB 3.3. Many books in circulation are still EPUB 2 because every reader supports it.

A reading app that supports EPUB 3 also opens EPUB 2. The reverse is not always true for the advanced features.

Why a book fails to open

When a reader rejects a file, one of these is almost always the cause:

  1. The mimetype entry is not first, or is compressed, usually because someone re-zipped the folder with a general-purpose tool.
  2. container.xml points to a package file that was moved or renamed.
  3. The manifest names a file that is not in the archive.
  4. A required metadata field is empty.
  5. A chapter is not well-formed XML, for example because it uses   without it being declared, or has an unclosed tag.
  6. The content is protected by DRM, which scrambles it so only an authorised reader can show it.

The first five are mechanical, which means a tool can detect them. PDFSwift’s EPUB Validator lists them with the file concerned, and the repair tool fixes the ones that are safe to fix automatically. The sixth is a different matter: DRM-protected books cannot be inspected or edited by these tools.

Looking inside your own book

To see what you have, the quickest route is the EPUB Reader, which shows the chapters in reading order, and the metadata editor, which shows the title, authors, language and other package fields. Both work in your browser; the book is not uploaded. Opening the file as a ZIP is also instructive: look at the .opf file in a text editor and match its manifest against the files in the archive.

A minimal package, line by line

You do not need a special tool to see how little an EPUB needs. This is the entire container.xml of a small book:

<container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container">
  <rootfiles>
    <rootfile full-path="OEBPS/content.opf" media-type="application/oebps-package+xml"/>
  </rootfiles>
</container>

And this is the skeleton of the package file it points to:

<package version="3.0" unique-identifier="uid" xmlns="http://www.idpf.org/2007/opf">
  <metadata xmlns:dc="http://purl.org/dc/elements/1.1/">
    <dc:identifier id="uid">urn:uuid:12345678-...</dc:identifier>
    <dc:title>The Test Book</dc:title>
    <dc:language>en</dc:language>
    <meta property="dcterms:modified">2026-01-02T03:04:05Z</meta>
  </metadata>
  <manifest>
    <item id="nav" href="nav.xhtml" media-type="application/xhtml+xml" properties="nav"/>
    <item id="ch1" href="ch1.xhtml" media-type="application/xhtml+xml"/>
  </manifest>
  <spine><itemref idref="ch1"/></spine>
</package>

Every rule of thumb about EPUB troubleshooting can be read off those two snippets. The reader starts at container.xml, follows full-path to the package, matches itemref idref values in the spine to item id values in the manifest, and opens the href it finds. If any link in that chain breaks, the book is partly or wholly unreadable.

Identifiers, and why they matter

The dc:identifier is what library software uses to decide whether two files are the same book. Reading apps often use it to match your reading position and highlights across devices. It is usually an ISBN or a UUID. Editing a book’s text without changing the identifier is fine; two different books sharing one identifier can confuse a library.

Size and what makes a file large

Text compresses very well, so a novel without pictures is often well under a megabyte. Size comes from images and embedded fonts. A cookbook with full-page photographs can exceed 50 MB. If a book is large, the cause is almost always images stored at print resolution, which a reading screen cannot show.

Other things you will meet