WP Manifestindependent plugin directory
manifest / forms / wordpress-cloudflare-turnstile-contact

Turnstile Contact Form

A plugin that adds a simple contact us form and validator with cloudflare turnstile widgets

by Derryn Simmons · github.com/derryn-simmons/wordpress-cloudflare-turnstile-contact · website

★ 0stars
0forks

Install

No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:

wp plugin install https://github.com/derryn-simmons/wordpress-cloudflare-turnstile-contact/archive/refs/heads/main.zip

A lightweight WordPress contact form block protected by Cloudflare Turnstile, the privacy-friendly CAPTCHA alternative.

Messages are emailed to you and stored as private records in wp-admin, so nothing is lost if email delivery fails.

Authored by Derryn Simmons — derrynsimmons.com

Features

  • Contact form block (ds-contact/form) for the block editor, rendered server-side. Its only front-end script is the Turnstile widget, which loads only on pages that contain the form.
  • Cloudflare Turnstile verification on the server, which fails closed and checks the action and hostname.
  • Layered spam protection: honeypot field, per-IP rate limit (5 messages per hour), nonce check and Turnstile.
  • Message log: every submission is saved under Contact → Messages, visible to administrators only, and shows whether the email was sent.
  • Works without JavaScript in the form itself. It uses a standard POST and a redirect, and if there are errors the visitor's input is kept.
  • Privacy: visitor IPs are stored only as salted hashes, never in full.
  • Follows your theme: styles use the theme's theme.json colour, spacing and font presets, with neutral fallbacks.
  • Accessible: labelled fields, inline errors linked with aria-describedby, and status messages announced to screen readers.

Requirements

  • WordPress 6.4+
  • PHP 8.1+
  • A free Cloudflare account (for Turnstile keys). Your site does not need to be proxied through Cloudflare.

Installation

  1. Download or clone this repository into wp-content/plugins/:
    cd wp-content/plugins
    git clone https://github.com/derryn-simmons/Wordpress-Cloudflare-Turnstile-Contact.git turnstile-contact-form
  2. Activate Turnstile Contact Form under Plugins.
  3. Configure it under Settings → Contact (see below).
  4. Add the Contact form block to any page.

No build step and no Composer install are needed. The plugin has its own autoloader.

Configuration

1. Create a Turnstile widget

  1. Sign in to the Cloudflare dashboard.
  2. Go to Turnstile → Add widget.
  3. Add your site's hostname, for example example.com.
  4. Choose widget mode Managed.
  5. Copy the Site key and Secret key.

2. Plugin settings (Settings → Contact)

Setting Description
Site key Public Turnstile site key.
Secret key Turnstile secret key. It is write-only: once saved it is never shown again, and leaving the field blank keeps the stored value.
Widget theme Auto (matches the visitor's system), Light or Dark.
Cloudflare proxy Tick this only if your site's DNS is proxied through Cloudflare (orange cloud). The plugin then uses the CF-Connecting-IP header to identify visitors for rate limiting. Leave it unticked otherwise, because that header can be forged.
Send messages to Recipient address. Defaults to the site admin email.

Development / staging

If no keys are set and wp_get_environment_type() is anything other than production, the plugin uses Cloudflare's always-pass test keys, so the form works in local development straight away.

In production with no keys set, the form is hidden from visitors. Administrators see a notice with a link to the settings page.

Block options

Option Default
Heading Send a message (leave empty to hide)
Show subject field On

How a submission is processed

Form block → POST admin-post.php?action=ds_contact_submit
  → nonce check + input sanitising
  → honeypot (bots receive a fake success)
  → field validation
  → rate limit (5 per hour per hashed IP)
  → Turnstile siteverify (server-side)
  → store as a private "Message" record
  → wp_mail() to the recipient, with Reply-To set to the sender
  → redirect back to the form (Post/Redirect/Get)

The message is stored before the email is sent, so a mail failure never loses it. The message list shows whether each email was sent.

Developer hooks

/**
 * Fires after a message is stored (and emailed, if possible).
 *
 * @param int   $post_id Stored message ID (post type `ds_contact_message`).
 * @param array $data    { name, email, subject, message }
 */
add_action( 'ds_contact_message_received', function ( int $post_id, array $data ) {
    // e.g. forward to Slack, a CRM, etc.
}, 10, 2 );

Uninstalling

Deleting the plugin removes its settings. Stored messages are kept by default. To delete them too, run this before uninstalling:

update_option( 'ds_contact_keep_messages', 0 );

Email deliverability

The plugin sends mail with WordPress's wp_mail(). For reliable delivery, configure your site to send through an SMTP or transactional mail service. Every message is also saved under Contact → Messages regardless.

Development

composer install     # PHPCS + WordPress Coding Standards
composer lint        # phpcs
composer lint:fix    # phpcbf

License

MIT — see LICENSE.

© Derryn Simmons — derrynsimmons.com