How to set up Web Key Directory (WKD) for your domain.

Twenty minutes to set up self-hosted OpenPGP key discovery for your email domain. Mail clients querying you@yourdomain.com will automatically find your public key via WKD — no keyserver lookup, no manual fingerprint exchange.

~20 minutes Web server control required Self-sovereign discovery
// at a glance
  1. Export your public key as binary
  2. Compute the WKD hash with gpg
  3. Choose Direct or Advanced method
  4. Create the .well-known directory structure
  5. Place the key binary and policy file
  6. Configure web server headers
  7. Test with gpg --locate-keys
Prerequisites
  • An OpenPGP key with an email at a domain you control
  • Web server control for that domain (Nginx, Apache, static hosting, or CDN)
  • Valid TLS certificate (Let\'s Encrypt or any other) — WKD requires HTTPS
  • Desktop gpg for computing the hash and testing
// step 01

Export your public key as binary.

WKD serves OpenPGP binary, not ASCII armored. From PGPony, get the public key out via the Exchange tab → Show My KeyShare (gives you the ASCII-armored block; convert to binary on the desktop). Or use a desktop gpg copy if you already have one: If PGPony only outputs ASCII armored, convert on the desktop:

gpg --import your-public-key.asc
gpg --export YOUR_FINGERPRINT > pubkey.gpg

The resulting pubkey.gpg is what goes on the web server.

// step 02

Compute the WKD hash.

Run on desktop gpg:

gpg --with-wkd-hash --list-keys you@yourdomain.com

Look for the line below your User ID:

uid           [ultimate] You <you@yourdomain.com>
                          7we1qpoqxxh1ie3wxxxxxxxxxxxxxxxx@yourdomain.com

The hash on the left of @ is the localpart-hash you need. Z-base-32 encoded SHA-1 of the lowercased localpart. This is your filename on the server.

// step 03

Decide on Direct or Advanced method.

Direct method: WKD lives on your main domain.
URL pattern: https://yourdomain.com/.well-known/openpgpkey/hu/HASH

Advanced method: WKD lives on a dedicated subdomain.
URL pattern: https://openpgpkey.yourdomain.com/.well-known/openpgpkey/yourdomain.com/hu/HASH

Clients try Advanced first, fall back to Direct. Setting up both is most robust; Direct alone is simpler and usually sufficient.

// step 04

Create the directory structure.

On your web server (using Direct method, document root /var/www/yourdomain.com):

mkdir -p /var/www/yourdomain.com/.well-known/openpgpkey/hu
cd /var/www/yourdomain.com/.well-known/openpgpkey
// step 05

Place the public key binary.

Copy your pubkey.gpg into the hu/ directory, renaming it to the hash from Step 2 — no file extension:

cp pubkey.gpg /var/www/yourdomain.com/.well-known/openpgpkey/hu/7we1qpoqxxh1ie3wxxxxxxxxxxxxxxxx
// step 06

Add the policy file.

Create an empty policy file at .well-known/openpgpkey/policy. Its existence signals WKD support; its content is not used (in practice):

touch /var/www/yourdomain.com/.well-known/openpgpkey/policy
// step 07

Configure web server headers.

The WKD endpoint should serve with Content-Type: application/octet-stream and permissive CORS (so JavaScript-based clients can fetch). Nginx example:

location /.well-known/openpgpkey/ {
    default_type application/octet-stream;
    add_header Access-Control-Allow-Origin "*";
    add_header Cache-Control "public, max-age=3600";
}

Reload Nginx: sudo nginx -s reload.

// step 08

Test from a desktop.

Confirm WKD returns your key:

gpg --auto-key-locate=clear,wkd --locate-keys you@yourdomain.com

Successful WKD discovery prints your key. Failure prints a "no public key" error or fetch failure — recheck the hash, file path, and Content-Type header.

Verify it worked.

  • curl -I https://yourdomain.com/.well-known/openpgpkey/hu/HASH returns HTTP 200 with application/octet-stream.
  • gpg --auto-key-locate=wkd --locate-keys you@yourdomain.com returns your key.
  • A mail client that supports WKD (Thunderbird with OpenPGP, GnuPG-aware tools) auto-discovers your key when composing to you.

Common questions.

Direct vs Advanced?

Direct: WKD on main domain. Advanced: WKD on openpgpkey.YOURDOMAIN subdomain. Clients try Advanced first, fall back to Direct. Setting up both is most robust; Direct alone is usually sufficient.

Why the cryptic hash?

Z-base-32 SHA-1 of the lowercased localpart. Avoids case sensitivity, encoding ambiguity, and full-email-in-URL leakage. gpg --with-wkd-hash computes it.

Special format for the key file?

Binary OpenPGP format (not ASCII armored). No file extension on the filename. Policy file is empty; its existence signals WKD support.

Multiple email addresses on the key?

Each address gets its own WKD entry — same key file, hashed filename per localpart. Addresses on different domains need WKD on each domain.

Cloudflare or other CDN?

Works as long as the file is served with correct Content-Type. WKD is static lookup over HTTPS. Watch cache-control if you rotate keys — invalidate CDN cache on update.

Why WKD if keyservers exist?

WKD is automatic from the mail client; keyservers are user-initiated. WKD is also self-sovereign — you control the publication. If keys.openpgp.org disappears, your WKD still works.

Next steps.

Get PGPony

Free OpenPGP encryption for iOS and Android. No accounts, no tracking.