A contact form is not reliable because its submit button responds. Its input handling, delivery configuration, abuse controls and user-facing success state must agree about whether a message was received.

This guide is part of the NexisHub Developer Tutorials desk. It connects practical engineering, research, and product decisions to the wider NexisHub platform.

The operating idea

NexisHub's public Contact form deliberately disables submission unless `NEXT_PUBLIC_FORMS_ENABLED` is true. The server route separately returns a 503 when `FORM_WEBHOOK_URL` is absent. These are distinct safeguards: the browser flag shapes the user experience; the server configuration controls whether the message can actually be forwarded.

A visitor should never see a success message for an undelivered message. The operator also needs a real response channel when the form is disabled. NexisHub currently publishes a direct owner-supplied email, while operational delivery and the legal-notice process remain independently unverified.

Editorial boundary

NexisHub separates verified platform documentation, repeatable observation, and inference. No optimization can guarantee selection or citation by an external system.

Source inspection: what the handler really does

The reviewed `/api/contact` route parses a JSON payload and checks input lengths. It requires a trimmed name of at most 100 characters, a plausible email of at most 200 characters, and a message between 10 and 3000 characters. Ordinary enquiries must use an allowed topic. Studio enquiries have additional required company, service and timeline fields, with bounded optional URLs and risk-context values.

The handler checks an unused `companyWebsite` honeypot field and returns a successful-looking response for that field. This may reduce basic bot noise, but a honeypot cannot replace request-rate limiting, abuse monitoring, server authorization where needed or evidence that delivery happened. The current source does not by itself establish those protections.

If the configured webhook is unavailable, the route returns 503. An upstream non-OK response becomes 502; successful forwarding returns `{ ok: true }`. That response proves the downstream endpoint accepted the HTTP request, not that a human received, read or acted on the message. The UI distinguishes sending, sent and error states and provides a disabled-form notice.

Threat-model a contact form before turning it on

An attacker may send oversized payloads, repeated requests, invalid enum values or content designed to attack the receiving webhook or its downstream consumers. The server must validate every field regardless of browser controls. Rate limiting, abuse detection, downstream sanitization and stable error handling should be reviewed against realistic traffic and cost constraints.

Never expose `FORM_WEBHOOK_TOKEN` to the client. The handler reads its token from the server environment and sends it only in the outbound request to the configured endpoint. The service also needs a controlled provider allowlist, delivery retry policy and privacy review so webhook failures do not silently lose messages.

In testing, use a local mock endpoint or designated test inbox. Do not submit personal information to an unknown live endpoint, and do not enable Production delivery as a side effect of inspecting UI behaviour.

A finite acceptance matrix

Check an empty payload, malformed email, message shorter than 10 characters, invalid topic, oversized message, valid ordinary enquiry, valid Studio enquiry, missing webhook configuration, upstream 4xx/5xx, network timeout and repeated submission. Assertions must include both HTTP status and the UI message, not only one side.

When delivery is enabled, verify with a designated test payload that the destination receives the correct fields exactly once, that duplicate retries are handled safely, and that failures produce a comprehensible response. Add a privacy notice that correctly describes the configured processors, retention and support channel. If those operational facts are missing, keep the form disabled and provide a working alternative contact path.

Core principles

  1. Server validates independentlyBrowser required fields and disabled states are not a security boundary.
  2. Keep secrets on the serverWebhook tokens belong in server-only environment configuration.
  3. Never invent successful deliveryDistinguish HTTP acceptance by a provider from confirmed delivery and human response.
  4. Fail clearlyA disabled or misconfigured channel should surface an understandable alternative.

A practical implementation workflow

Apply the work in a controlled sequence. Keep a baseline, name an owner, and define the evidence that will show whether each step was completed.

  1. 1. Inventory submitted dataName each accepted field, allowed value, length, purpose and downstream processor.
  2. 2. Test validationCover invalid JSON, missing required values, long strings and unexpected enum values.
  3. 3. Simulate provider failureTest missing configuration, provider rejection and timeout without contacting Production.
  4. 4. Check abuse and privacy controlsReview rate limits, secret scope, logging redaction, retention and user notice.
  5. 5. Enable only with acceptance evidenceConfirm a controlled end-to-end test reaches the intended inbox and can be answered.

Common mistakes

Trusting disabled browser UI

An attacker can call API routes without using the site's form.

Success on webhook acceptance

A 2xx provider response is not proof that a real operator can receive or handle messages.

Calling a honeypot complete security

It may catch simple bots but does not address sustained abuse or downstream injection.

How to measure it responsibly

Measure request rejection by cause, successful forwards, downstream delivery failures and human response times only after permitted collection is established.

Record the exact test endpoint, payload classification, expected HTTP status and observed outcome. Avoid storing actual visitor messages in debug logs.

Evidence rule

Keep observed outputs, diagnostic scores, inferred causes, and business outcomes in separate fields. A modelled score is not a citation, and correlation is not proof of cause.

What comes next

A mature contact channel needs operational ownership, failure recovery and tested escalation paths as much as it needs validation.

The durable response is to build pages that are accessible, semantically explicit, useful outside their original layout, and backed by evidence a reader can inspect.

Key takeaways

01Treat public forms as server API surfaces.

02Keep delivery secrets off the client.

03Separate validation, forwarding and confirmed reception.

04Test negative paths with fixtures.

05Do not enable a channel whose owner cannot reliably receive it.

Frequently asked questions

Is client-side validation enough?

No. Every input must be validated at the server boundary because API calls can bypass the browser.

Does a honeypot prevent all spam?

No. It is one friction mechanism and requires complementary abuse controls.

Should an unconfigured form still accept messages?

No. It should fail clearly and provide a verified alternative contact method until delivery is ready.

References and further reading

  1. OWASP Input Validation Cheat Sheet
  2. OWASP API Security Top 10
  3. Next.js Route Handlers
Nexis Studio

Turn a useful idea into a maintainable product.

Nexis Studio helps organisations move from problem definition to product design, engineering, launch, and long-term support with clear technical and commercial boundaries.

Explore Nexis Studio

Continue the cluster

Related NexisHub guides

Developer TutorialsBuilding a Structured Blog with Next.js and TypeScriptDeveloper TutorialsAdding JSON-LD Metadata to a Next.js WebsiteDeveloper TutorialsBuilding a Citation Export Endpoint for Research Publications