SQLite Doesn't Come With Encryption. Here's What Developers Actually Use.

A map of the SQLite encryption landscape: SQLCipher, SEE, SQLite3MultipleCiphers, and modern alternatives, plus how page-level AEAD encryption actually works.

Cover image for SQLite Doesn't Come With Encryption. Here's What Developers Actually Use.

You're preparing for your first enterprise customer. The security review is going well until someone asks about encryption at rest. You pull up the SQLite database file, and there it is: every row, every column, completely readable. No encryption. No access controls at the file level. Just plaintext.

You search "sqlite encryption" and find a library from 2008, a $2,000 per-developer license from the SQLite project itself, and a community fork that exists because of an API removal most developers haven't heard of. The options are messier than they look.

This article maps them. We'll cover what exists, how page-level encryption actually works at the architecture level, and how to think about choosing between options in 2026. If you walk away from this piece and never read another word about SQLite encryption, you should still have enough context to make an informed decision.

#Why SQLite Has No Built-In Encryption

SQLite's design philosophy treats the database as a file, not a server. There are no daemons, no access control lists, no authentication layers. The database is just bytes on disk. Encryption was deliberately left to the application or operating system layer. For embedded systems and single-user apps, that was a reasonable choice. The OS could handle file permissions. Full-disk encryption could protect the storage layer. The database didn't need to duplicate those concerns.

For years, third-party solutions filled the gap using an undocumented compile-time hook called SQLITE_HAS_CODEC. This let extensions intercept page reads and writes, encrypting data on the way to disk and decrypting it on the way back. It worked. But it was never officially supported, never documented in SQLite's public API, and the SQLite team never committed to maintaining it.

On February 7, 2020, they removed it. The change shipped in SQLite 3.32.0, and the commit note was blunt: "Simplify the code by removing the unsupported and undocumented SQLITE_HAS_CODEC compile-time option."

Every third-party encryption extension that depended on that hook had to adapt or stop working with new SQLite releases. Some did. Some didn't. That moment is the dividing line in the modern SQLite encryption story. Everything before it is one era. Everything after it is another.

#The Incumbent: SQLCipher

SQLCipher has been the default answer to "how do I encrypt SQLite" since 2008. It has earned that position. Over 15 years in production. A strong mobile track record (Signal used it, among many others). The community edition is BSD-licensed and free. The commercial edition starts at $999/year per application and includes pre-built binaries and support.

If you have SQLCipher running in production today and it's working, there's no urgent reason to rip it out.

The problems are real but specific.

Major version migrations are painful. SQLCipher v1 through v4 each use different encryption parameters, and databases created with one version cannot be opened with another without explicit migration steps. The v3-to-v4 upgrade changed the KDF algorithm, page size defaults, and HMAC behavior. This isn't a theoretical concern. There are GitHub issues describing apps crashing after upgrading from 3.x to 4.x, with users having to roll back to old builds to recover their data. If you've been through a SQLCipher version migration, you know.

SQLCipher also offers a single cipher: AES-256-CBC with HMAC (in v4). No AEGIS. No ChaCha20-Poly1305. And it wasn't designed for cloud-native BYOK (Bring Your Own Key) or per-database key isolation at scale. It was built for a world of mobile apps and single-user embedded databases, and it's very good at that. But that world is not the only one developers are building for anymore.

The verdict: SQLCipher works. If you're starting a new project today, it's worth evaluating the newer options before committing. If you're already running it, don't panic.

#The Official Option: SQLite Encryption Extension (SEE)

SEE is the encryption extension sold by the SQLite project itself. $2,000 for a perpetual source code license. It supports multiple cipher modes (RC4, AES-128 in OFB mode, AES-128 in CCM mode, AES-256 in OFB mode), it's maintained by the core SQLite team, and it carries the official SQLite project's name.

That last part matters in certain enterprise procurement contexts. Some organizations will only use software from the original vendor. SEE satisfies that requirement.

The catch is significant: SEE is proprietary. You can't view the source without purchasing a license, which makes independent security audits difficult. If your compliance team requires auditable encryption implementations, and many do, this is a blocker. Open-source projects can't use it at all.

SEE is the right choice for a narrow set of use cases: enterprise contexts where official provenance matters more than auditability, and where the $2,000 per-developer cost is a rounding error in the security budget.

#The Community Response: SQLite3MultipleCiphers

When SQLITE_HAS_CODEC was removed in 2020, Ulrich Telle started work on SQLite3MultipleCiphers almost immediately. The project was created specifically to fill the gap: a new encryption extension compatible with SQLite 3.32.0 and later.

SQLite3MultipleCiphers supports multiple cipher schemes, including SQLCipher-compatible modes. That last detail is important. If you're maintaining an existing SQLCipher deployment or need to read legacy encrypted databases, this project gives you a path forward without being locked to SQLCipher's release cycle.

It's actively maintained and genuinely useful. Worth knowing about, especially if you're working with encrypted databases that predate the 2020 API removal.

#How Page-Level AEAD Encryption Actually Works

This is the section that matters if you want to understand what's happening under the hood, not just which library to pick. Every serious SQLite encryption solution uses the same fundamental approach: page-level encryption with AEAD ciphers. Understanding the architecture helps you reason about the trade-offs, evaluate new solutions, and debug problems when they come up.

#Pages: the encryption boundary

SQLite stores data in fixed-size pages. The default is 4,096 bytes (4 KiB). Every read and write operation happens at the page level. Tables, indexes, and internal structures are all organized as B-trees that span multiple pages. The pager layer is the component that reads pages from disk and writes them back.

This makes the page the natural unit for encryption. Each page gets encrypted independently. When the database needs to read a specific row, it reads and decrypts only the pages containing that row. Not the entire file. For a database with millions of rows, this is the difference between practical and unusable. You don't need to load the whole file into memory just to run a SELECT query.

One exception: the first 100 bytes of the database file (the SQLite header) remain unencrypted. This is intentional. It allows tools to identify the file as a SQLite database without needing the key. Everything after that header is encrypted.

#Per-page nonces

Each page gets a unique nonce (number used once). The nonce is combined with the encryption key to produce a unique keystream for that page. If you reuse a nonce with the same key, the consequences are catastrophic for most AEAD ciphers. It can expose the keystream, which means an attacker can recover plaintext by XORing two ciphertexts together. Per-page nonces prevent this.

The nonce is typically derived from the page number or generated randomly, depending on the implementation. Either way, the guarantee is the same: no two pages encrypted under the same key will ever share a nonce.

#What AEAD means and why authentication matters as much as encryption

AEAD stands for Authenticated Encryption with Associated Data. The name tells you what it does: it encrypts your data and authenticates it in a single operation.

The "authenticated" part deserves attention because it's easy to overlook. Encryption alone protects confidentiality. An attacker who copies your database file can't read the contents. Good. But without authentication, that same attacker can still modify the encrypted data. They can flip bits in a ciphertext page, and when you decrypt it, you'll get garbage. The problem is that you won't know it's garbage. Your application will try to process corrupted data as if it were legitimate.

AEAD solves this. Every encryption operation produces an authentication tag alongside the ciphertext. The tag is a short cryptographic value (typically 16 bytes) that acts like a checksum over the encrypted data. When you decrypt a page, the algorithm checks the tag first. If anyone has modified even a single bit of the encrypted page (or if you're using the wrong key), decryption fails explicitly. You get an error, not silent corruption.

For a database, this property is critical. Databases are long-lived. Files get copied, backed up, moved between systems, stored on hardware you don't control. The ability to detect tampering is not a nice-to-have. It's a requirement for any serious encryption implementation.

#The three cipher families

Three AEAD cipher families are relevant to SQLite encryption today. Each has a clear use case.

AES-GCM (AES-128-GCM and AES-256-GCM) is the NIST-approved standard. It's widely understood, well-audited, and accepted by compliance frameworks including HIPAA, PCI-DSS, and SOC2. If your auditor needs to recognize the cipher name, AES-GCM is the right choice. It performs well on hardware with AES-NI instructions, which covers most modern x86 and ARM processors.

AEGIS (AEGIS-128L, AEGIS-256, and parallel variants) is a newer cipher family designed for CPUs with hardware AES instructions. It uses the AES round function as its building block and was selected as a finalist in the CAESAR competition for authenticated encryption. According to the IRTF draft specification, AEGIS "offers performance that significantly exceeds AES-GCM on CPUs with AES instructions." AEGIS-256 uses 256-bit nonces, which virtually eliminates nonce collision risk even with random nonce generation.

An important distinction: AEGIS is currently an IRTF informational RFC in final review at the RFC Editor (draft-irtf-cfrg-aegis-aead-18). IANA has already assigned algorithm IDs for all AEGIS variants. But it is a proposed standard that has not yet been finalized, and it is not a NIST-approved standard. The specification itself states: "It is not an IETF product and is not a standard." This distinction matters for compliance. If you need to check a box that says "NIST-approved cipher," AEGIS doesn't qualify today. If you have the freedom to choose based on performance and security properties, AEGIS is the faster option.

ChaCha20-Poly1305 is the software-optimized choice. It doesn't require hardware AES instructions to perform well. If you're targeting older mobile hardware, some embedded systems, or IoT devices that lack AES-NI, ChaCha20-Poly1305 gives you strong AEAD encryption without depending on specialized CPU features.

#Where the nonce and auth tag live

Each encrypted page needs to store two pieces of metadata: the nonce used for encryption and the authentication tag produced by the AEAD algorithm. These have to live somewhere.

SQLite has a feature called "reserved space." Each page can have a small reserved area at the end that SQLite itself doesn't use. The B-tree logic ignores these bytes entirely. Encryption extensions store the nonce and auth tag in this reserved space.

This is why encrypted databases have a slightly different effective page layout than their unencrypted counterparts. The usable space per page is reduced by the size of the nonce plus the auth tag. For most ciphers, this is on the order of 28 to 48 bytes per page. On a 4 KiB page, that's roughly 1% of the page space. A small price for authenticated encryption.

#What's Missing Across All These Solutions

SQLCipher, SEE, and SQLite3MultipleCiphers all solve the problem they were designed to solve: encrypting a single SQLite database file for a single user or application. They were built for a world of mobile apps and desktop software where one database serves one user.

The world has moved.

Developers are now building cloud-native applications that need Bring-Your-Own-Key encryption where the cloud provider never holds the keys. They're building multi-tenant SaaS platforms that need millions of individually-keyed databases, one per customer, with cryptographic isolation between tenants. They're deploying AI agents that manage private user data across thousands of devices, each needing its own encrypted local store that syncs to the cloud.

None of the existing solutions were designed for these use cases. SQLCipher doesn't offer a cloud-native BYOK model. SEE is proprietary and can't be embedded in open-source projects. SQLite3MultipleCiphers is an extension bolted onto SQLite's existing architecture, not a ground-up implementation. None of them support modern ciphers like AEGIS. And none publish transparent benchmarks showing exactly what encryption costs in terms of performance.

This isn't a knock on those projects. They solved real problems for real developers, and they continue to work. But the gap between what they offer and what modern applications need has grown wide enough that new approaches are worth evaluating.

#Choosing Your Direction

If you're reading this article, you're probably in one of a few situations. Here's how to think about your options:

Stick with SQLCipher if you have an existing deployment that works, especially on mobile. The version migration pain is real, but if you're on v4 and stable, there's no fire. Don't migrate for the sake of migrating.

Look at SEE if you're in an enterprise context where official SQLite project provenance matters more than auditability, and the $2,000 per-developer license fits your budget.

Look at SQLite3MultipleCiphers if you need to maintain compatibility with legacy encrypted databases or want the flexibility of multiple cipher schemes in a community-maintained project.

Consider modern options like Turso if you're starting a new project, need cloud-native BYOK, want modern ciphers like AEGIS, need per-database key isolation at scale, or care about open-source auditability. Turso is a ground-up rewrite of SQLite in Rust with encryption native to the engine, designed from the start for cloud-native deployment and per-database key isolation, things the older tools weren't built to handle.

That last category is where things get interesting.