How to add a feedback form to a static website without a backend.
A static website is a fast, cheap, and reliable way to launch a landing page, promo page, documentation, or product site. However, it has a typical problem: how to accept submissions if there is no backend?
For example, you have a site on Cloudflare Pages, GitHub Pages, Netlify, Vercel, S3/Selectel Object Storage, or any other static hosting. HTML, CSS, and JavaScript work great, but the feedback form alone cannot send data to Telegram, Email, or CRM.
In this article, we will explore how to add a form to a static site without a backend, what options are available, and what to pay attention to so that submissions are not lost.

Problem
A regular HTML form looks simple:
<form>
<input name="name" placeholder="Your name" />
<input name="email" placeholder="Email" />
<textarea name="message" placeholder="Message"></textarea>
<button type="submit">Send</button>
</form>
But such a form does nothing useful until it has a handler.
To ensure that the submission actually reaches you, you need to:
- accept the form data;
- verify that the request came from an allowed site;
- protect against spam;
- save the submission;
- send a notification to Telegram, Email, or Webhook;
- handle delivery errors;
- provide the user with a clear result: successful or not.
If there is no backend, all of this has to be solved in another way.
Bad option: mailto:
Sometimes a form is replaced with a link to email:
<a href="mailto:sales@example.com">Write to us</a>
Or they create a form that opens the user's email client.
This is a bad option for lead generation.
Cons:
- the user may not have a configured email client;
- the submission is not saved in the system;
- there is no analytics;
- fields cannot be properly validated;
- protection against spam is not possible;
- data cannot be sent directly to Telegram, CRM, or webhook;
- the user may simply close the email window and send nothing.
mailto: is only suitable as a backup contact, but not as the main submission form.
Option 1: write your own backend
The classic solution is to create your own endpoint:
POST /api/contact
It accepts the form data and sends it further.
Pros:
- full control;
- you can implement any business logic;
- you can integrate with CRM, Telegram, Email, analytics.
Cons:
- you need a backend developer;
- you need to host the API somewhere;
- you need to maintain security;
- you need to monitor notification delivery;
- you need to implement anti-spam;
- you need to log errors;
- you need to maintain infrastructure.
For a large product, this is normal. For a landing page, promo page, or MVP — it is often excessive.
Option 2: serverless function
You can create a form handler on Cloudflare Workers, Vercel Functions, Netlify Functions, or AWS Lambda.
It looks something like this:
export default {
async fetch(request) {
const data = await request.json()
// validate data
// send telegram notification
// send email
// return response
return Response.json({ ok: true })
}
}
Pros:
- no need for a full server;
- convenient for simple scenarios;
- can be quickly launched.
Cons:
- you still need to write and maintain code;
- secrets need to be stored securely;
- you need to handle retry delivery yourself;
- you need to implement spam protection yourself;
- you need to maintain the logic for webhook/email/telegram yourself;
- when you have multiple sites, code duplication begins.
Serverless is a good option for developers, but not always the best option for businesses, agencies, and teams that need to quickly launch many sites.
Option 3: form backend / lead capture service
A form backend is an external service that accepts POST requests from the form and delivers the submission to the necessary channels.
The general principle:
Static Website → Form POST → Lead Capture Service → Telegram / Email / Webhook
Only the HTML/JS code of the form remains on the site. All backend logic is in the external service.
This approach is convenient if you need to:
- quickly connect a form;
- not write a backend;
- receive submissions in Telegram;
- send copies to Email;
- pass data to CRM via webhook;
- protect the form from spam;
- see the submission history;
- not lose submissions during temporary delivery errors.

Example of form integration
Below is an example of a simple form that sends a submission via JavaScript.
<form id="lead-form">
<input name="name" placeholder="Your name" required />
<input name="email" type="email" placeholder="Email" required />
<textarea name="message" placeholder="Message"></textarea>
<button type="submit">Send</button>
</form>
<script>
const form = document.querySelector('#lead-form')
form.addEventListener('submit', async (event) => {
event.preventDefault()
const formData = new FormData(form)
const payload = {
name: formData.get('name'),
email: formData.get('email'),
message: formData.get('message')
}
const response = await fetch('https://api.example.com/api/c1/sites/YOUR_SITE_KEY/submissions', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
})
if (!response.ok) {
alert('Failed to send submission. Please try again.')
return
}
form.reset()
alert('Submission sent.')
})
</script>
YOUR_SITE_KEY is the public key of the site. It is needed for the service to understand which site the submission relates to.
What should be in a good form backend
Simply accepting a POST request is not enough. For a production scenario, additional things are important.
1. Domain verification
The service must understand that submissions are indeed coming from your site.
For this, you can use domain verification through a DNS TXT record.
Example:
subscribe-verification=abc123
This reduces the risk that someone will connect a foreign domain or start sending junk on behalf of your site.
2. Captcha
A public form quickly starts receiving spam.
For simple forms, a built-in math captcha is often sufficient:
7 + 4 = ?
The user solves a simple problem, and the service checks the answer on the backend side.
Important:
- the challenge should have a TTL;
- one challenge cannot be reused;
- the verification must occur on the server;
- the answer cannot be trusted solely to frontend code.
3. Rate limiting
Even with captcha, it is worth limiting the frequency of submissions.
For example:
no more than 10 submissions per minute from one IP
Rate limiting protects against:
- bots;
- accidental resubmissions;
- simple flood spam;
- overloading notification channels.
4. Delivery to multiple channels
A good scenario is to send the submission to several places at once:
Telegram + Email + Webhook
For example:
- Telegram — to quickly see the submission;
- Email — to keep a copy;
- Webhook — to send data to CRM or internal system.
5. Retry on errors
Webhook or Email may temporarily not work.
For example:
- CRM returned
500; - SMTP server is temporarily unavailable;
- Telegram API responded with an error;
- the network timed out.
If the service simply tries once and forgets the submission — this is poor reliability.
A retry mechanism is needed:
1st attempt → error
2nd attempt in 30 seconds
3rd attempt in 2 minutes
4th attempt in 10 minutes
This way, submissions are not lost due to temporary failures.

When to use a ready-made form backend
A ready-made service is especially useful if:
- the site is static;
- you don't want to write a backend just for one form;
- you need to quickly launch a landing page;
- you need to receive submissions in Telegram;
- you need to connect a webhook to CRM;
- there are many sites;
- submissions cannot be lost;
- you need simple spam protection.
However, if the form is part of a complex product with a personal account, payments, custom business logic, and internal processes — it is better to create your own backend.
Conclusion
Adding a feedback form to a static site without a backend can be done in several ways:
mailto:— the simplest but unreliable option;- your own backend — flexible but expensive to maintain;
- serverless function — convenient for developers but requires code;
- form backend — a quick way to accept submissions without your own backend infrastructure.
For landing pages, static sites, promo pages, and MVPs, an external lead capture service is the best fit: the site sends the form to the API, and the service delivers the submission to Telegram, Email, or Webhook.
This way, you don't write a backend just for one form and still get reliable delivery, anti-spam, submission history, and integration control.
CTA
Want to accept submissions from your site without a backend?
Connect Form Hook: add the SDK, verify the domain, and receive leads in Telegram, Email, or Webhook.