Honeypot
Add in an unecessary field (hidden from human) to trick the bot to fill up the field. If the field is sent to the server, it is assumed to be spam.
<input id="inputAgree" type="checkbox" name="agree" autocomplete="off"><styles>#inputAgree { display: none !important;}</styles>
The following is a full example using Bootstrap
.
<form> <div class="form-row justify-content-md-center"> <div class="col-md-auto mb-3"> <label for="inputEmail" class="sr-only">Your email</label> <input name="email" type="email" id="inputEmail" placeholder="Your email" value="" class="form-control"> </div> <div class="col-md-auto mb-3"> <div class="form-check sr-only d-none"> <input type="checkbox" id="inputAgree" autocomplete="off" checked="checked" class="form-check-input"> <label for="inputAgree" class="form-check-label">I agree with the TOS</label> </div> </div> <div class="col-md-auto mb-3"> <button type="submit" class="btn btn-primary">Subscribe</button> </div> </div></form>
Dynamic Field
Attached a onfocus
and onblur
event to the input field, and fill up an additional field like timestamp when such event happened.
On the server side, check for the timestamp and make sure it is within range (maybe 1h plus and minus).
NOTE: JavaScript Get Current Datetime in UTC
Store and Check IP Address
Store IP address from each form request, and start to block IP address if too many request from the same IP.
NOTE: You might consider blocking the IP without raising an error, to prevent alerting the spammer of the detection.
NOTE: Cloud Functions/Flask Get Client Ip Address (Python)
HMAC Signature
Generate HMAC Sign for all or certain form parameters, and verify the signature on the server.
Akismet API
https://akismet.com/development/api/
NOTE: I have yet to try this.
reCaptcha
If all else fail, use https://www.google.com/recaptcha