SvelteKit hCaptcha Contact Form: Keeping Bots Away SvelteKit hCaptcha Contact Form: Keeping Bots Away
In this post we look at a SvelteKit hCaptcha contact form for your Svelte site. hCaptcha is an
alternative to Google reCAPTCHA. Both can be used to reduce spam submissions on your site's forms. hCaptcha claims to protect user privacy
There are two parts to the hCaptcha verification. The first is on the client side (frontend), where we ask the user to complete the challenge. We send the user challenge responses to hCaptcha straight away (from the client). hCaptcha then responds with a response code. That response code is needed in the second part of the process, which is completed in the backend. We will see how you can use Cloudflare workers to perform the backend part if you want to build a static SvelteKit site. If, however, you prefer server side rendered, we cover you back with some sample code for handling that in SvelteKit too.
If that all sounds exciting, why don't we crack on?
The plan of action is the following:
Clone the SvelteKit blog MDsveX starter
, so we can hit the ground running.
- Add a contact form.
- Add the hCaptcha client code.
- Look at how Cloudflare workers can be used for the server side verification.
- Try an alternative server side rendered implementation.
Let's get started by cloning the SvelteKit blog MDsveX starter
We will also use some components from a SvelteKit component library
Finally, you will need hCaptcha credentials to test out your code. See instructions on setting up a free hCaptcha account in the article on Serverless hCaptcha or just head to the hCaptcha site
The first two credentials will be accessed by the client side so they need the
PUBLIC_ prefix. Finally, we allow access to client components in
src/lib/config/website.js — click to expand code.
With the setup out of the way, if this is your first time using the starter, have a skim through
the files and folders of the project. Also head to localhost:5173/
We just need to tweak the hooks configuration for everything to run smoothly. The
src/hooks.server.js file in the project includes Content Security Policy (CSP) headers. These are an added security
measure which only allow the browser to connect to certain hosts. For any site you build with the starter,
you will probably need to tweak this file. We need to allow connections to hCaptcha and our Cloudflare
worker for this project:
You will need to make these changes during development, whether you are creating a static or server side rendered site. For a static production site, the file is not used. You can add HTTP headers to achieve the same effect. Check how to do this with your hosting platform.
Here's the code for the basic contact form. We are using the component library to save us typing
out all the boiler plate needed for accessible form inputs. You can see how to create you own SvelteKit component library in a recent video post. Paste the code into a new file at
TextInputField components come from the component library. They make use of Svelte's component events
name variables in this component. Follow the previous link to
the Svelte tutorial if you are not yet familiar with this API.
To stop this post getting too long, we won't go into detail on the rest of the form code here. That said, do let me know if you would appreciate a separate post on Svelte forms and binding form fields to variables.
We will add the client hCaptcha script directly to the DOM. You have probably seen this pattern if
you have looked at tracking or analytics code previously. In SvelteKit, you will see you don't
need to add any extra packages to make this work. Before we do that, let's actually load the
script in the component
We are adding an “invisible” hCaptcha, so we will use the
hcaptchaWidgetID variable to identify it. The first lines are just there to keep types consistent and to be able
to link and unlink the hCaptcha script to a local variable during component creation and destruction.
We add our hCaptcha site key in the hCaptcha initialisation, within
Next we need a
The function starts with a
hcaptcha.execute function call. This displays
the captcha and waits for the user to complete it. It then contacts hCaptcha to get a
response which we will need for the second part. Interestingly,
information on mouse movement while solving the challenge as well as the user answers.
The rest of the function includes two possibilities. If we have a static site, we can send our form data and the hCaptcha response to a Cloudflare worker for processing. If you are a SvelteKit purist and go for a server side rendered site, you can send the request to a SvelteKit endpoint. Let's look at both ways in more detail in a moment.
As we mentioned earlier, we can add the hCaptcha script to the DOM:
Then we need a placeholder div for it to render:
Importantly, we should import the
ContactForm component on the contact
page, so we can render it:
Cloudflare workers run in a Web Assembly (WASM) environment, which means you can write your code
are building client sites in SvelteKit as well as other frameworks, you only need to maintain one
codebase for parts of your backend. You can use the same code for contact form submission from
your SvelteKit and Next apps. Rust also offers opportunities for code optimisation. You can see how to set up a Rust Cloudflare service worker to handle hCaptcha in a recent post. For local testing, you will probably have your worker running on
http://127.0.0.1:8787, which is the value we defined in the
.env file. You will just need
to set it up to listen for
POST requests on the
Finally let's check the SvelteKit way to handle the hCaptcha server side work. Create a new file
src/routes/verify/+server.js and paste in the following code:
The hCaptcha request needs to be submitted as form data and the response is JSON. A
successful field on the response indicates whether hCaptcha considers the user a bot or not. For more details
pull up the hCaptcha docs
If you get CORS errors testing the site, you should try tweaking your DNS settings. This involves
creating a hostname proxy for 127.0.0.1 (localhost). On MacOS you can add the following line to
Then, instead of accessing the site via
your browser use
http://test.localhost.com:3030. This worked for
me on macOS. The same will work on typical Linux and Unix systems, though the file you change will
/etc/hosts. If you are using DNSCryprt Proxy or Unbound, you
can make a similar change in the relevant config files. If you use windows and know how to do
this, please drop a comment below to help out other windows users.
We have just covered the basics here. In a real-world app, you should add verification, at least on the server side. Feedback on the client side is a good idea too to improve user experience.
In this post we learned:
- how to use hCaptcha with SvelteKit,
- a way to integrate Rust Cloudflare workers into a static site, making it easier to share code across different frameworks,
tweaking the Content Security Policy via the
hooks.jsfile to allow connection to external hosts.
I do hope there is at least one thing in this article which you can use in your work or a side project. As always get in touch with feedback if I have missed a trick somewhere!
Have you found the post useful? Do you have your own methods for solving this problem? Let me know your solution. Would you like to see posts on another topic instead? Get in touch with ideas for new posts. Also if you like my writing style, get in touch if I can write some posts for your company site on a consultancy basis. Read on to find ways to get in touch, further below. If you want to support posts similar to this one and can spare a few dollars, euros or pounds, please consider supporting me through Buy me a Coffee.
Finally, feel free to share the post on your social media accounts for all your followers who will
find it useful. As well as leaving a comment below, you can get in touch via @askRodney