PGP Encrypted Contact Form Emails

It’s time! Time to check off another to-do on the “random dev ideas” list!

I have a contact form on my website. It emails the enquirer’s name, email, and message to me using AWS SES (Simple Email Service).

A contact form email with test data

You see that? Plain text email! HTML has no place in emails.

Old Implementation

My static site is currently hosted on Netlify and the contact form was handled by a Netlify function. That was a small server-side JavaScript function that accepted the POST data and called the AWS SES API to send an email. That’s the Amazon Web Service Simple Email Service Application Programming Interface, FYI.

For various reasons I’m also fronting my website with Cloudflare, despite Netlify’s advice. That means data is passed through Cloudflare, Netlify, and Amazon, before it gets to Proton. Data is sent encrypted over TLS but each service can technically read it before passing it along. That’s a whole lotta privacy policies…

The Idea

I’m a happy paying Proton Mail user. So any email I receive is encrypted on arrival (if not already). Only I can decrypt and read the contents. A stark difference from Gmail for example, where Google actively read, index, and catalogue your private emails. Proton Mail uses the PGP encryption standard. If emails are encrypted before they’re sent to me no service along the way can read the contents.

So my idea is simple:

Encrypted the contact form email with PGP as early as possible.

New Implementation

Theoretically I could encrypt the email in the web browser making it truly end-to-end encrypted. However, the OpenPGP.js library is rather big. Maybe there are smaller libraries but OpenPGP is maintained by Proton so I trust it. Anyway, requiring JavaScript to submit a form isn’t a compromise I’m willing to make.

Initially I had envisioned adding the encryption to my existing Netlify function. Then I realised I could use a Cloudflare Worker to handle the POST request and bypass Netlify entirely.

Time for some code!

The email is sent to [email protected] which has this PGP public key:



Encrypting an email with the openpgp library is simple. The example code below is inside a POST request handler. In my case, that’s a Cloudflare Worker.

import * as openpgp from 'openpgp';

// TODO: server-side validation...
const data = await request.formData();

// The email body template (could be HTML)
const message = await openpgp.createMessage({
  text: `
Name: ${data.get('name')}
Email: ${data.get('email')}


const encryptionKeys = await openpgp.readKey({
  armoredKey // the PGP public key above

const encrypted = await openpgp.encrypt({

With that encrypted.toString() returns the email body like:



Only my private key can decrypted it — and that’s a secret!

Sending this via AWS SES is just as easy. Assuming you’ve got everything configured on the AWS side… which is far from easy.

const client = new SESv2Client({
  region: env.AWS_REGION,
  credentials: {
    accessKeyId: env.AWS_ACCESS_KEY_ID,
    secretAccessKey: env.AWS_SECRET_ACCESS_KEY

await client.send(
  new SendEmailCommand({
    FromEmailAddress: env.SES_RECIPIENT,
    Destination: {
      ToAddresses: [env.SES_RECIPIENT]
    Content: {
      Simple: {
        Subject: {
          Data: "Enquiry ("
        Body: {
          Text: {
            Charset: 'UTF-8',
            Data: encrypted.toString()

The email subject cannot be encrypted so I keep that generic.

I could put the enquirer’s email in ReplyToAddresses for convenience but headers aren’t encrypted either. I can’t CC them a copy because they’ll only see the encrypted body.

With this new implementation only the Cloudflare Worker sees the data. Netlify is bypassed, AWS only knows I’m sending an encrypted email to myself, and Proton Mail can only decrypt the email client-side.

Is it worth the effort?

As you might have guessed, when I follow up on enquiries I’m almost always replying with unencrypted email. Very few people use PGP in the real world.

So why bother? I’ll admit this is largely academic but I think it’s worth the effort. It certainly can’t harm to cut out several middlemen from the process.

Most importantly, I can check that to-do off my list.

☑️ PGP encrypt contact form email? (done)