21/02/2026 - Michau

How to Build a Secure Contact Form in Nuxt 4 with Zod and Rate Limiting

nuxtzodsecurityprismabackend
How to Build a Secure Contact Form in Nuxt 4 with Zod and Rate Limiting

A practical guide to building a secure contact form in Nuxt 4 using Zod for validation and server-side rate limiting to prevent spam and abuse.

How to Build a Secure Contact Form in Nuxt 4

Many developers underestimate the contact form. It is often implemented quickly, without properly considering security, validation, or protection against spam and abuse. Unfortunately, this is a common mistake that can lead to security vulnerabilities, application instability, or in the worst case, service disruptions caused by DDoS attacks or massive spam submissions.

In reality, a contact form is a true entry point into your system. If not properly secured, it can cause significant damage — both in terms of security and performance.

In this article, we’ll see how to build a production-ready secure contact form in Nuxt 4 using Zod for validation and server-side rate limiting.

  • Nuxt 4
  • Zod for server-side validation
  • Server-side rate limiting
  • Anti-spam honeypot
  • Prisma for data persistence

1. Creating the Server Endpoint

Thanks to Nuxt 4's server layer, we can handle APIs and backend logic within the same project, avoiding external services and maintaining full control over our security logic.

Create a file called server/api/contact.post.ts:

export default defineEventHandler(async (event) => {
  const body = await readBody(event)
})

Inside this file, we can read the form data and process it securely.

2. Server-Side Validation with Zod

Client-side validation is not enough. It's essential to implement server-side validation to ensure the data is correct and safe before storing it in the database.

Zod is a powerful and easy-to-use validation library. Here's how we can define a schema for our contact form:

import { z } from 'zod'

const schema = z.object({
  name: z.string().min(2),
  email: z.string().email(),
  message: z.string().min(10),
})

After defining the schema, we validate the incoming data and block any invalid requests. We can also return a clear error message to the client.

const result = schema.safeParse(body)

if (!result.success) {
  throw createError({
    statusCode: 400,
    statusMessage: 'Invalid payload',
  })
}

Robust server-side validation is essential. At this point, if the data is invalid, the request is blocked and the server returns a 400 error to the client.

3. Honeypot Protection

A simple and effective anti-bot technique is the "honeypot".

Add a hidden field to the form to ensure only real human interactions are processed.

<input type="text" name="website" class="hidden" />

If this field is filled, it likely indicates bot activity.

4. Implementing Rate Limiting

To prevent abuse and DDoS attacks, it's essential to implement server-side rate limiting. This limits the number of requests a single IP can make within a given time window.

In Nuxt 4, we can implement a custom rate limiter tailored to our use case, restricting requests from a single IP to a reasonable number.

Example:

rateLimitOrThrow({
  key: `contact:${ip}`,
  windowMs: 60000, // 1 minute
  max: 3,
})

This allows a maximum of 3 requests per minute per IP.

It’s a simple yet highly effective protection mechanism that helps prevent server overload and significantly reduces the risk of spam.

5. Saving Data with Prisma

Once the data has been validated and all necessary protections have been applied, we can safely store it in the database using Prisma.

await prisma.message.create({
  data: result.data,
})

At this stage, the data is secure and ready to be persisted in the database.

Conclusion

A contact form is not just HTML — it is an entry point into your system and should be treated accordingly.

Server-side validation using robust tools like Zod, anti-spam protection, and rate limiting are fundamental elements to ensure the security and stability of your production application.

Client-side validation improves user experience, but it cannot replace server-side validation, which is essential to protect your system from invalid or malicious data.

If you're building a project with Nuxt 4, this structure provides a solid, scalable, and secure foundation.