Alex HerreraSoftware developer · Cybersecurity focus
ES
← Back to projects

Professional

Kaizen Apps | Corporate site

A bilingual site with the demonstrations under the same domain and a booking form that reaches a person.

  • Full stack
  • Security

1 / 9

Home page of kaizenapps.net in Spanish, with the language and theme switches in the header

The Spanish home page, with the language and theme switches in the header.

Role
Development
Period
2026-01 – present

Stack

  • Astro
  • TypeScript
  • React
  • Tailwind CSS
  • Cloudflare Turnstile
  • Resend
  • Render

Context

Kaizen Apps sells bespoke software, so its site is the first commercial contact. It is also where a prospect sees the product running before speaking to anyone.

The assignment was to rebuild it entirely:

  • the marketing pages, in Spanish and English;
  • the application demonstrations, under the same domain;
  • a booking channel that does not depend on someone checking a shared inbox.

Problem

The previous site had no search metadata. There were no per-page titles or descriptions, and no declared relationship between the Spanish and English versions. A search engine had no way of knowing they were the same page in two languages.

Each application the company sells sits at its own address, off the site. A prospect who wanted to see them running left the commercial site, and from outside had no way back and no way on to the next one.

Technical decisions

Routes are declared in a single file. Every page carries its identifier, its two routes and its metadata there. The menu and the language equivalence map used by the language switch both come from that same table. Adding a page means adding an entry, without walking through six files.

The site is static except where it cannot be. Only the booking needs a server, so only the booking is served as a server route, and the rest is generated at build time. That is why the site moved from one host to another by swapping the adapter alone, without touching the pages.

The eight pages that wrap a demonstration were eight copies of the same HTML. Each route now delegates to one shared function, so the floating menu and the frame policy are edited in a single file.

The anti-bot challenge is validated against Cloudflare from the server, not in the browser. The public response does not distinguish between an invalid token, a malformed field and a failure of the email service. The real code stays in the server log.

Architecture

The site has three classes of page. The marketing pages are static and bilingual, and they are generated from the route table. The demonstration wrappers are HTML responses that mount the corresponding application inside the site. The only server route is the booking one.

FormVerificationAPIEmail

The form obtains a Cloudflare Turnstile token and the server validates it against Cloudflare before looking at the rest of the submission. The browser does not decide whether the challenge was passed.

The type, presence and length of every field are verified on the server. A submission that fails those checks reaches neither the challenge verification nor the email.

The public response says the submission failed and nothing more. The real error code stays in the server log, where it gives no hints to anyone probing the form.

The HTML only includes the challenge's public key. The secret key and the email service key exist only in the server environment.

The only public server route, with four rules before a submission becomes an email.

Result

The site publishes in Spanish and English from a single route definition. Each page’s metadata and the correspondence between languages are declared where the page itself is declared.

The demonstrations sit under the same domain and share navigation, so a prospect can go through them without going back in the browser.

A meeting request arrives as a formatted email in the commercial inbox, and the sender gets the confirmation in the language they wrote in.

What I learned

On a bilingual site, the faults came from duplication, not from translation. While routes were spread across the menu, the language switch and every page, each new section cost three edits and produced at least one broken link. With a single table, language stopped being a special case.

The other lesson is commercial. The form is the one point on the site where a visitor leaves something behind. That is why it received most of the security work, and it was the only thing that justified having a server.