Plugin Version: 1.0.2+
Requires: WordPress 5.8+, PHP 7.4+
Settings Location: WordPress Admin → Settings → HashCash
Captcha - HashCash for WordPress on RicheyWeb.com | Captcha – HashCash on WordPress.org
captcha-hashcash folder to /wp-content/plugins/No API keys. No account required. No third-party setup.
Navigate to Settings → HashCash to access all configuration options.
Default: 1800 (30 minutes)
Range: 300–3600 seconds
How long a completed proof-of-work remains valid. When a user loads a form, HashCash begins calculating in the background. The result is timestamped. On submission, the server checks that the timestamp is within this window of the current time.
This prevents replay attacks — where a bot captures a valid proof-of-work solution and reuses it repeatedly. A solution that’s more than 30 minutes old is rejected regardless of whether it’s mathematically valid.
HashCash automatically refreshes the calculation before it expires, so users who leave a form open for extended periods won’t encounter submission errors.
When to change this: The default is appropriate for most sites. Reduce it (to 300–600 seconds) on high-security forms like login or registration. Increase it only if your users frequently take longer than 30 minutes to complete a form.
Default: 1
Range: 1–12 (3–5 recommended for SHA-based algorithms)
Controls how hard the proof-of-work calculation is. Higher levels require more computation, which means stronger bot protection but longer solve times for users.
For SHA-based algorithms:
| Level | Security | Approximate Solve Time | Notes |
|---|---|---|---|
| 1 | Low | ~3ms | Near-instant. Deters simple bots only. |
| 2 | Low | ~35ms | Imperceptible to users. |
| 3 | Medium | ~212ms | Comfortable for most sites. |
| 4 | Medium | ~4.6s | Noticeable pause. Stops most automated abuse. |
| 5 | High | ~26s | Very long. Consider PBKDF2+64K at lower levels instead. |
For Argon2id: Keep difficulty at 1. Security with Argon2id is controlled entirely by the Memory, Iterations, and Parallelism parameters — not the difficulty level. Level 2 with Argon2id takes over a minute on modern hardware and is not usable.
Default: SHA-256
Selects the cryptographic algorithm used for proof-of-work calculation. Each option represents a different balance of compatibility, speed, and bot resistance.
SeeWhich Algorithm Should I Use? for a full comparison.
⚠️ iOS Device Compatibility Note: All iPhones and iPads use Apple’s WebKit browser engine regardless of which browser icon the user taps. HashCash requires iPhone 11 (2019) or newer for full algorithm support. Older iOS devices may experience slow or incomplete hash calculations. If your audience includes users on older iPhones, SHA-256 or SHA-384 at low difficulty levels are the safest choices.
Visible only when Argon2id is selected as the Hash Algorithm
Argon2id’s strength comes from three parameters that control how much memory and CPU time each calculation requires. Unlike SHA-based algorithms, difficulty level has minimal effect on Argon2id — security is controlled here instead.
Memory (KiB)
Default: 16384 (16 MB) | Recommended starting point: 65536 (64 MB)
RAM required per calculation attempt. Higher values make GPU and ASIC attacks impractical — each parallel attempt needs dedicated RAM, which limits how many a bot farm can run simultaneously.
Iterations
Default: 2 | Recommended starting point: 3
Number of passes over the allocated memory. Increases CPU cost without requiring more RAM. Raise this if you want more computational cost without increasing memory requirements.
Parallelism
Default: 1
Number of parallel threads used per calculation. Should remain at 1 for web form protection — increasing this primarily benefits password storage scenarios, not CAPTCHA use cases.
Recommended starting configuration: Memory 65536 / Iterations 3 / Parallelism 1. Adjust from there based on observed solve times on your target devices.
Delay mining until form interaction (recommended)
Default: Enabled
HashCash waits for real user interaction — a mouse movement, keypress, or click — before starting the proof-of-work calculation. This blocks bots that submit forms instantly without any interaction, and bots that load the page and wait without doing anything.
Disabling this starts the calculation immediately on page load. Not recommended — it removes a significant layer of bot detection.
Punish suspicious bots (solution becomes impossible)
Default: Enabled
When suspicious behavior is detected, HashCash sets the difficulty to a level that is computationally unsolvable. The bot’s browser spins indefinitely attempting a calculation it can never complete. Legitimate users are completely unaffected.
Without this enabled, detected bots are simply ignored — the form remains submittable. Punishment mode is more aggressive and more effective.
Enable CDP / DevTools detection
Default: Enabled
Detects Chrome DevTools Protocol (CDP) automation — the technology used by headless browsers like Puppeteer and Playwright to automate web interactions. When CDP signatures are found in the browser runtime, the request is treated as suspicious.
Enable nonce field (extra bot protection)
Default: Enabled
Adds a hidden field with a randomly generated value that legitimate browsers handle correctly and automated submissions typically don’t. Provides an additional signal for bot detection without any user-facing impact.
Trigger custom event on bot detection
Default: Disabled
When enabled, HashCash fires a JavaScript custom event (plg_captcha_hashcash) on the window object when suspicious behavior is detected. Intended for developers who want to integrate HashCash’s bot detection signals into custom logging, analytics, or security workflows.
See JavaScript Eventsfor event details.
| Algorithm | GPU Resistance | iOS Support | Best For |
|---|---|---|---|
| SHA-256 | Low | Full | Default — maximum compatibility |
| SHA-384 | Low | Full | Light upgrade, same compatibility |
| SHA-512 | Low–Medium | Full | Blog comments, public forms |
| PBKDF2 | Medium | Full | Enhanced protection without memory cost |
| PBKDF2 (64KB) | High | Full | High-traffic targets, aggressive bots |
| Argon2id | Maximum | iPhone 11+ only | Financial, medical, high-value targets |
Start with SHA-256 at difficulty 3. It’s fast, universally compatible, and stops the vast majority of spam bots. Most sites never need anything stronger.
Step up to PBKDF2 or PBKDF2+64KB if you’re seeing sophisticated bot traffic that gets through SHA-based protection, or if your forms are high-value targets.
Use Argon2id if you’re protecting financial systems, medical records, or any environment where well-resourced attackers with GPU farms are a realistic threat. Check your analytics for iOS device share before enabling — older iPhones are not fully supported.
The following forms are protected automatically — no configuration required. Protection applies to non-logged-in users only. Logged-in users are exempt from all HashCash verification.
| Form | Protection Method |
|---|---|
| WordPress comment form | Automatic |
| WordPress login form | Automatic |
| WordPress registration form | Automatic |
| WordPress lost password form | Automatic |
| Contact Form 7 | Automatic |
| Any other HTML form | use shortcode |
When a user loads a protected form, HashCash begins calculating in the background. While the calculation runs, a brief indicator appears:
⟳ Securing this form…
The submit button is disabled during this time. When the calculation completes — typically within seconds at default settings — the indicator disappears and the submit button re-enables. The user can then submit normally.
There is nothing for the user to click, solve, or interact with. The indicator is feedback, not a challenge.
“There was an error trying to send your message” in Contact Form 7
This is a mail delivery error, not a HashCash error. If invalid_fields is empty in the CF7 response, HashCash validated successfully. Check your WordPress mail configuration (WP Mail SMTP or similar).
Form submits but HashCash validation fails
Check that your server’s system clock is accurate. HashCash compares the proof-of-work timestamp against the current server time — significant clock drift will cause validation failures.
Solve time is very long for legitimate users
Reduce difficulty level or switch to a less intensive algorithm. At SHA-256 difficulty 3, solve time should be under 250ms on any modern device.
Older iPhones are having trouble
Switch to SHA-256 or SHA-384 at difficulty 1–2. All iPhones use WebKit regardless of browser — iPhone 11 (2019) or newer is required for full algorithm support.
CF7 field not appearing
Ensure HashCash is activated. CF7 support is automatic but requires both plugins to be active. If the field still doesn’t appear, try deactivating and reactivating HashCash.
If a misconfiguration makes your login form unsolvable and you can’t access the WordPress admin:
wp-config.php in a text editor (via FTP, cPanel File Manager, or SSH)/* That's all, stop editing! */ line:define('CAPTCHA_HASHCASH_OVERRIDE', true);
wp-config.php⚠️ Important: Remove the override constant immediately after fixing your settings. Leaving it in place disables HashCash protection entirely.
For any form not automatically supported, use the shortcode.
[hashcash]
Or call the static method directly in PHP:
HashCash::field(); // Echoes the field directly
$field = HashCash::field(false); // Returns the field as a string
The field renders a hidden input and the “Securing this form…” indicator. Ensure the form has a submit button with type="submit" — HashCash disables it during calculation and re-enables it on completion.
Minimum required form structure:
<form method="post">
<!-- your fields -->
[hashcash]
<button type="submit">Submit</button>
</form>
wpcf7_form_elementsHashCash uses this filter to automatically inject the proof-of-work field into Contact Form 7 forms. If you need to prevent automatic injection for a specific form, you can remove the filter:
remove_filter('wpcf7_form_elements', [$hashcash_instance, 'cf7_add_hashcash_field']);
wpcf7_spamHashCash validates CF7 submissions via this filter at priority 10. To adjust priority relative to other spam filters:
remove_filter('wpcf7_spam', [$hashcash_instance, 'cf7_validate_spam'], 10);
add_filter('wpcf7_spam', [$hashcash_instance, 'cf7_validate_spam'], 20); // run later
preprocess_commentComment validation runs at priority 1. HashCash calls wp_die() on failure — if you need softer handling, remove this filter and implement your own validation using HashCash::check_answer().
HashCash fires custom events on the form element during the proof-of-work lifecycle. Use these to build custom UI or integrate with analytics.
plg_captcha_hashcash_started
Fired when proof-of-work calculation begins.
form.addEventListener('plg_captcha_hashcash_started', () => {
console.log('Mining started');
});
plg_captcha_hashcash_finished
Fired when proof-of-work calculation completes successfully.
form.addEventListener('plg_captcha_hashcash_finished', () => {
console.log('Mining complete');
});
plg_captcha_hashcash_progress
Fired every 1,000 iterations during calculation. The event detail includes the current iteration count.
form.addEventListener('plg_captcha_hashcash_progress', (e) => {
console.log('Iterations completed:', e.detail.count);
});
plg_captcha_hashcash (window event)
Fired on the window object when bot detection is triggered. Only fires when “Trigger custom event on bot detection” is enabled in settings.
window.addEventListener('plg_captcha_hashcash', (e) => {
console.log('Bot detection triggered', e.detail);
});
HashCash checks for a constant defined in wp-config.php that allows site-wide bypass of all verification. This is intended for emergency recovery and local development environments.
// In wp-config.php:
define('CAPTCHA_HASHCASH_OVERRIDE', true); // Bypass all verification
define('CAPTCHA_HASHCASH_OVERRIDE', false); // Normal operation (same as not defining it)
If CAPTCHA_HASHCASH_OVERRIDE is not defined, HashCash operates normally.
Do not leave this set to true in production. It disables all spam protection.
Standard SHA-2 variants implemented via the browser’s Web Cryptography API (SubtleCrypto). Fast, universally supported, zero memory overhead. Vulnerable to GPU parallelization at higher difficulty levels — a GPU can attempt many hashes simultaneously, reducing effective difficulty. Appropriate for most sites at levels 1–3.
Password-Based Key Derivation Function 2, implemented via SubtleCrypto.deriveBits(). Adds 1,000 iterations of SHA-256 per attempt, increasing CPU cost without significant memory overhead. Provides moderate GPU resistance. Salt: WordPressHashCashSalt.
PBKDF2 with a memory-hard extension — 15,000 iterations with a dynamically derived salt (SHA-256 of the input). Pushes GPU solve times into the 8–18 second range while keeping legitimate user solve times under 3 seconds. Appropriate for high-traffic targets facing sophisticated bot attacks.
Winner of the Password Hashing Competition (2015). Memory-hard by design — each attempt requires the full configured memory allocation, making parallel GPU/ASIC attacks expensive regardless of hardware budget. Implemented via WebAssembly (argon2.umd.min.js) in the browser worker, and via PHP’s sodium_crypto_pwhash() on the server.
Salt: WordPressHashCashSalt (fixed, 16 bytes)
Output: 32-byte hash, hex-encoded
Server requirement: PHP sodium extension (available by default in PHP 7.2+)
iOS requirement: iPhone 11 (2019) or newer
Argon2id parameter guidance:
| Parameter | Default | Effect of Increasing |
|---|---|---|
| Memory (KiB) | 16384 | Raises RAM cost per attempt — primary GPU defense |
| Iterations | 2 | Raises CPU cost without more RAM |
| Parallelism | 1 | Minimal effect for CAPTCHA use cases — leave at 1 |
OWASP minimum recommendation for Argon2id: Memory 19456 KiB / Iterations 2 / Parallelism 1. HashCash default (16384/2/1) is slightly below this — consider raising memory to 19456 or higher for security-sensitive deployments.
Server-side verification reconstructs the hash using the submitted iteration count and compares it against the difficulty pattern. The verification string is: {client_ip}{timestamp}{iteration_count}. The server checks that the resulting hash begins with the required number of leading zeros (SHA-based) or matches the Argon2id output pattern.
Timestamp validation window: ±60 seconds future, maximum max_age seconds past. This prevents both future-dated submissions and replay attacks.