Skip to main content

End-to-End Encrypted Messaging on WordPress

· 6 min read
Creator of Better Messages

Most WordPress messaging plugins store every message in plaintext in the database. Anyone with access to the database — a site admin, a host's support engineer, an attacker who exfiltrates a database backup — can read every conversation. For most sites that is acceptable. For some sites — therapists with clients, lawyers with opposing parties, journalists with sources, founders discussing acquisition terms — it is not. Better Messages 2.13 introduced optional per-thread end-to-end encryption: messages are encrypted in the sender's browser, stay encrypted at rest, and are decrypted in the recipient's browser. The database holds ciphertext only.

This post covers how the encryption works, what stays encrypted versus what does not, the trade-offs, and how to enable it.

What end-to-end means here

End-to-end encryption means the encryption keys never leave the participants' browsers. The server stores ciphertext and public keys; it has no way to decrypt messages.

Concretely for a Better Messages thread with E2E enabled:

  • Each participant's browser generates a keypair (public + private). The public key is uploaded to the server. The private key never leaves the device.
  • A symmetric thread key is generated for the thread, encrypted to every participant's public key, and stored — once per participant — in the database.
  • Each new message is encrypted with the thread key in the sender's browser before it is sent to the server.
  • Every recipient's browser decrypts the thread key with their private key, then decrypts each message with the thread key.

The database stores: ciphertext message bodies, encrypted per-participant copies of the thread key, public keys. The database does not store: any private key, any cleartext message body for an encrypted thread.

What stays encrypted

DataEncrypted at rest
Message body textyes
File attachments uploaded inside the encrypted threadyes
Reactions, replies, edits, mentionsyes
Thread participant listno
Thread title / subjectno
Timestampsno
User identitiesno
Read stateno

The trade-off: an admin with database access can still see who talked to whom and when. They cannot see what was said.

What you give up to use E2E

End-to-end encryption is a trade-off. Some features have to be turned off for an E2E thread because they require the server to read message content:

  • Server-side search — the search index cannot include encrypted bodies, so search on E2E threads is browser-only and only covers messages already decrypted on the device.
  • Email notification preview — email notifications for E2E threads carry a generic "new encrypted message" line, not the message body.
  • AI bots in the thread — AI bots are server-side actors; they cannot participate in an E2E thread.
  • Server-side moderation hooks — pre-moderation, bad-words filter, content scanning, and the bot-detection layer all bypass E2E threads.

For most threads on most sites, none of these trade-offs matter. For the threads that need encryption, those compromises are usually exactly what you want.

Per-thread, not per-site

E2E is enabled per thread, not per site. The same Better Messages installation can host:

  • Public chat rooms (no encryption, full search and moderation)
  • Standard private threads (no encryption, full features)
  • E2E-encrypted private threads (encryption on, server-side features off for those threads)

A user starts an E2E-encrypted thread the same way they start a regular thread, then toggles encryption on for that thread. The toggle is one-way: once a thread is encrypted you cannot unencrypt the historical messages (the server never had the cleartext).

Enabling E2E on the site

Encryption is available on the WebSocket version. Two things to configure:

  1. Better Messages → Settings → General → Encryption — global on / off for the encryption capability. When off, the encryption toggle on individual threads is hidden.
  2. Permissions → "Allow encrypted threads" — per-role control. You can let only certain roles start encrypted threads (e.g. paid members, staff).

Once both are configured, the thread sidebar shows a lock icon that users toggle to encrypt the thread.

Performance

Encryption adds CPU work on the sender and receiver, not on the server. For typical text messages on modern devices the overhead is imperceptible. For large attachments the encryption pass is noticeable but still under a second.

The plugin lazy-loads the crypto bundle on the first encrypted thread interaction, so the page-weight cost is only paid by users who actually use the feature.

Who needs this

E2E is the right tool when:

  • You handle data that has a legal classification (HIPAA-adjacent health info, attorney-client privilege, PII under GDPR Article 9).
  • You handle data whose disclosure would cause direct harm (journalists with sources, security teams with vulnerability reports).
  • You promise users that no one — including you — can read their messages.

E2E is over-engineering when:

  • Your site is a community / marketplace / LMS and the conversations are not unusually sensitive.
  • You want server-side search across all threads.
  • You rely on AI bots, moderation, or bad-words filtering for every thread.

If only some threads in a community fit the first category, that is exactly the right shape: leave E2E disabled by default and let the relevant roles toggle it on per thread.

Frequently asked questions

Can I recover messages if I lose my private key?

No — by design. If a user clears browser storage on every device, their private key is lost and any encrypted thread they participate in becomes unreadable for them. This is the trade-off of true end-to-end encryption.

Does it work with the mobile app?

Yes — the mobile app generates and stores the private key on the device. Switching devices requires a key transfer.

Is encryption FIPS-validated?

The browser's Web Crypto API uses NIST-standard primitives (P-256, AES-GCM). It is not FIPS-validated as a cryptographic module. If FIPS validation is a contractual requirement, talk to support before relying on Better Messages E2E.

Can I export an encrypted thread for compliance / discovery?

Yes — participants in the thread can export the decrypted thread from their browser. The server cannot perform the export, by design.

What happens if a new member joins an existing encrypted thread?

When a new participant joins, every existing participant's browser re-encrypts the thread key to the new participant's public key. The new member can read messages sent after their join, not historical ones — unless an existing participant explicitly shares historical content.

Does it work with file uploads?

Yes — file attachments in an E2E thread are encrypted before upload. The server stores ciphertext; recipients decrypt the file on download.

See also

Install Better Messages from WordPress.org →