You may have already notice the contact form on this blog. Until recently, it was handled by Netlify and used a serverless function to send an email both to me and the submitter. This worked fine, but was far from ideal because I had to trust the data to a third party (Netlify).

As I was already running a server (with Nextcloud and Mastodon), I looked for a self-hosted solution and found MailyGo from Jan-Lukas Else. A small tool written in Go that allows send HTML forms through email. Exactly what I was looking for!

I just needed to tailor it a little bit to my use case. A good challenge as this was my first time working with Go. Keep that in mind and be kind when you review my code, please!

So, there’s the list of changes I made:

Fighting Spam

When a form is placed online it’s a matter of time until it get some bots' attention and being targeted with spam. To fight it, the original version of MailyGo already used an honeypot field, a spamlist and integrated Google Safe Browsing to check URLs. And that seemed enough.

But I didn’t want to use any third party, even less Google! And unfortunately the honeypot field and spamlist alone shown insufficient.

I got some bots sending a particular field (submit) that wasn’t part of the form. And so, I created a denylist, a list of fields names that MailyGo will look for and, if present, mark the submission as spam.

I too got bots that grabbed the URL of MailyGo and posted a submission directly to it, bypassing the form. To prevent this, I created the ability to use a token to assure only the submissions that come from the form are handle by MailyGo. This token can be any combination of letters and numbers. If a TOKEN is defined on configuration, MailyGo will look for a field named _token. If this field doesn’t exist or its value doesn’t match the one defined on configuration the submission will be marked as spam.

I’m using this set of solutions for more than a week now with zero spam. Seems enough. At least for now!

An that’s it, my fork of MailyGo!

A big thanks to Jan-Lukas Else. Follow his blog and know more of his projects.