Panther Hook Blog
Lead Capture

Client-Side vs Server-Side Form Validation: Where Each Check Belongs

Design reliable lead form validation without confusing friendly browser feedback with a trusted security boundary.

TRUST BOUNDARYForm UIRequestServerVERIFY AGAIN

One field, two jobs

Browser validation should help somebody finish a form with fewer corrections. Server validation should decide whether an untrusted request is accepted at all. These jobs overlap in their rules, but they have very different security guarantees.

A browser can enforce a required field, show character limits and explain a malformed email immediately. The same request can also be sent by curl, an automated client or modified JavaScript. Your server therefore needs to enforce its own limits on every request, including those that bypass the interface.

Where each form rule belongs
CheckBrowserServer
Required nameImmediate guidanceReject missing required data
Email formatHelpful warningApply defined acceptance rules
Allowed field namesRender known inputsReject unexpected payload fields
Payload sizeImprove usabilityHard byte limit before heavy parsing
Anti-bot proofCollect tokenVerify token with trusted provider

Avoid accidental data changes

Validation and normalization are not interchangeable. Removing punctuation from someone’s surname or silently trimming a password-like field can change its meaning. Decide separately which fields you can safely trim, normalize, reject or preserve.

Also validate the shape of the entire payload. A field intended to be a short string should not accept a huge nested object. Set length, count and depth limits before any expensive processing. Generic regular expressions are not a substitute for field-specific rules.

A tiny server-side example

The example below demonstrates boundaries, not a complete production schema. A real submission handler still needs body-size limits, consent checks, anti-spam verification, authentication where applicable, and safe handling of stored personal data.

javascript
// Validate inputs again, even if the HTML form already did.
function validateLead(body) {
  if (!body || typeof body !== "object" || Array.isArray(body))
    return { ok: false, reason: "Invalid request" };
  const { email, message } = body;
  if (typeof email !== "string" || email.length > 254 || !email.includes("@"))
    return { ok: false, reason: "Check email" };
  if (typeof message !== "string" || message.length > 2000)
    return { ok: false, reason: "Check message" };
  return { ok: true };
}

Test failures as carefully as success

The useful outcome is predictable: clear guidance in the browser, the same acceptance rules on the server and no silent conversion of malformed requests into apparently valid leads.

  • Submit normally, then bypass the UI with a direct POST.
  • Test missing fields, extra fields, non-string values and unexpectedly large arrays.
  • Test multibyte Unicode characters: character count and byte size differ.
  • Check that errors identify the field without echoing sensitive submitted values.
  • Make sure accepted requests produce a durable submission identifier.
Read Panther Hook validation guidance