XPI File Documentation
Summary
An XPI (Cross-Platform Installer) file is a Mozilla browser or mail extension package, an add-on for Firefox, Thunderbird or SeaMonkey stored as a ZIP archive. Its MIME type is application/x-xpinstall. You do not open it in a viewer; you install it into the app by dragging the .xpi onto about:addons. Release Firefox installs only add-ons Mozilla has digitally signed. Because it is a ZIP, you can rename it to .zip to read its code.
Technical details
| Feature | Value |
|---|---|
| Full name | Cross-Platform Installer (Mozilla add-on package) |
| File extension | .xpi |
| MIME type | application/x-xpinstall |
| Format type | ZIP archive containing a browser/mail extension (WebExtension) |
| Container / base format | PKZIP (same structure as .zip) |
| Developer | Mozilla |
| Introduced | 1990s (Netscape/Mozilla XPInstall); WebExtension XPIs since Firefox 57 (2017) |
| Extension model | WebExtension (manifest.json), Manifest V2 and V3 |
| Manifest | manifest.json at the archive root |
| Magic number (hex) | 50 4B 03 04 (ASCII PK) |
| Byte order | Little-endian (ZIP records) |
| Signing | PKCS#7 (META-INF/mozilla.rsa) plus optional COSE; issued by the Mozilla add-on PKI |
| Signing required | Yes on release/beta Firefox (since Firefox 43, 2016) |
| Payload | JavaScript, HTML, CSS, icons, _locales |
| Runs in | Firefox, Thunderbird, SeaMonkey (not Chrome) |
| Open standard | Partial — container is ZIP; manifest is a Mozilla/W3C-aligned dialect |
| Related extensions | .zip, .crx, .jar |
| Specification | extensionworkshop.com/documentation/publish/ |
What is an XPI file?
XPI (pronounced “zippy”, short for Cross-Platform Installer) is Mozilla’s package format for browser and mail add-ons. An .xpi is the installer for an extension or theme in Firefox, in Thunderbird (mail and calendar add-ons), and in the SeaMonkey suite. The name dates to Netscape’s 1990s XPInstall system, but the modern meaning is narrow: today an XPI holds a WebExtension, the sandboxed add-on model that replaced the old XUL/XPCOM extensions when Firefox 57 (Quantum) shipped in November 2017.
Structurally an XPI is a ZIP archive, so it begins with the ZIP magic bytes 50 4B 03 04 and its MIME type is application/x-xpinstall. You do not “open” an XPI in a viewer; you install it into a Mozilla application, which reads the archive, validates it and loads the add-on’s code. Everything below is about what the archive must contain and the signing gate that controls whether Firefox will accept it.
manifest.json: the entry point of every WebExtension
The one file every modern XPI must contain is manifest.json, and it must sit at the root of the archive, not inside a subfolder. It is a JSON document that both identifies the add-on and wires up its behaviour. A small manifest looks like this:
{
"manifest_version": 3,
"name": "Example Highlighter",
"version": "1.2.0",
"permissions": ["storage", "activeTab"],
"host_permissions": ["*://*.example.com/*"],
"background": { "service_worker": "bg.js" },
"content_scripts": [
{ "matches": ["<all_urls>"], "js": ["highlight.js"] }
],
"icons": { "48": "icons/48.png" }
}
The manifest_version key selects the platform generation: 2 for the older Manifest V2, 3 for Manifest V3. That number changes several other rules. Under V2, an add-on declares every permission, including host access, in a single permissions array. Under V3, host access moves into separate host_permissions and optional_host_permissions keys, and background pages become an event-driven service worker. The content_scripts array lists scripts injected into matching pages, and background registers code that runs for the add-on’s lifetime. Firefox reads this file first; if it is missing, malformed, or not at the root, the XPI is not a valid extension.
Inside the archive: scripts, UI, locales and icons
Beyond the manifest, an XPI is an ordinary tree of web assets, all referenced from manifest.json by archive-relative path:
manifest.json (root — required)
background.js background service worker / script
highlight.js content script injected into pages
popup.html + popup.js the toolbar popup UI
options.html settings page
icons/ toolbar and listing icons (16/32/48/96 px)
_locales/en/messages.json translated UI strings (i18n)
META-INF/ signing files, added by AMO (see below)
The _locales folder is how an add-on ships in several languages: each locale has a messages.json, and the manifest references strings by a __MSG_name__ placeholder. Because all of this is plain text and images inside a ZIP, renaming a copy of the file to .zip and extracting it lets you read exactly what an add-on does before trusting it, including the full permissions list in the manifest.
AMO signing: the META-INF folder and the mozilla.rsa block
Since Firefox 43 (2016), release and beta Firefox install only extensions that Mozilla has digitally signed. Signing happens on Mozilla’s side when an add-on is submitted to addons.mozilla.org (AMO): the service adds a META-INF/ directory to the XPI, using the same three-layer scheme as a signed JAR. A manifest.mf lists a digest of every file in the archive; a mozilla.sf file holds digests of those manifest entries; and mozilla.rsa is a PKCS#7 (Cryptographic Message Syntax) detached signature over mozilla.sf, together with the certificate chain. Newer add-ons additionally carry a COSE signature (using an algorithm such as ES256 or PS256) alongside the PKCS#7 one.
The end-entity certificate is issued by an intermediate of Mozilla’s private add-on PKI, and its subject common name must equal the add-on’s ID. At install time Firefox recomputes the digests, verifies the signature against that PKI, and confirms the certificate matches the add-on ID. Any edit to a signed XPI invalidates the signature, which is why you cannot legitimately tweak a file inside a released add-on and reinstall it on normal Firefox.
Installing an XPI and getting past the signing wall
The normal path never exposes the file: clicking “Add to Firefox” on AMO downloads and installs the XPI in one step. You meet a bare .xpi when you download an add-on manually (a beta, an older version, an add-on pulled from AMO, or a Thunderbird extension). To install one, open about:addons and drag the file onto the page, or use the gear menu’s “Install Add-on From File”. Thunderbird uses the identical XPI format through its own Add-ons Manager.
If an XPI is unsigned, release Firefox refuses it outright. There are three escape hatches, each deliberately awkward. Firefox Developer Edition, Nightly and ESR expose the xpinstall.signatures.required preference, which can be set to false to allow unsigned add-ons. The about:debugging page can “Load Temporary Add-on”, which loads an unsigned XPI for the current session only and drops it on restart, the usual route while developing. Standard release and beta builds provide no override at all: the signing requirement there is not a preference you can flip.
XPI versus CRX: why a Firefox add-on will not load in Chrome
An XPI and a Chrome/Edge CRX look superficially similar: both are ZIP-based, both centre on a manifest.json. They are not interchangeable. A CRX wraps the ZIP in its own header carrying a Chrome-specific signature, and although both platforms now use a WebExtensions-style manifest, the available APIs and some manifest keys differ between Firefox and Chromium. Renaming an XPI to .crx does not produce a working Chrome extension; a developer has to port the add-on, adjust the manifest and any browser-specific API calls, and repackage it for the Chrome Web Store. Cross-browser add-ons exist, but they are built for each store, not converted file-to-file.
Is an XPI file safe? The permission model
An installed extension runs code inside the browser with whatever access its manifest requests, and that is the whole risk. An add-on that declares broad host_permissions such as <all_urls> can read and modify the content of every page you visit, which is enough to capture form data, inject content, or redirect navigation. Mozilla’s signing requirement exists to blunt this: release Firefox installs only add-ons that passed AMO review and signing, which stops most casual malware from installing at all. The exposure rises exactly when you defeat that gate, by using a Developer/Nightly build with signing off, by loading temporarily through about:debugging, or by installing an XPI sourced from outside AMO. In those cases the defence is manual: because an XPI is a ZIP, open it with 7-Zip first and read the permissions and host_permissions in manifest.json, and install only add-ons whose requested access matches what they claim to do. Unexpected .xpi files sitting in a Downloads folder are harmless installers and safe to delete.
Frequently asked questions
Why won’t Firefox install my unsigned XPI?
Because release and beta Firefox enforce add-on signing and provide no override. To run an unsigned add-on, use Firefox Developer Edition, Nightly or ESR and set xpinstall.signatures.required to false, or load it for one session through about:debugging with “Load Temporary Add-on”. For anything you intend to distribute, submit it to AMO so Mozilla signs it.
How can I check what an XPI does before installing it?
Treat it as the ZIP it is. Rename a copy to .zip or open it with 7-Zip, then read manifest.json. The permissions and host_permissions keys tell you what the add-on can touch; the content and background scripts show what it actually does. An add-on requesting access to all sites when it only claims to change one is worth a second look.
References
- Firefox Extension Workshop — Publishing and signing add-ons
- MDN — manifest.json reference
- MozillaWiki — Add-ons/Extension Signing
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.