This portfolio is intentionally small, but the deployment path is the same one I would use for a production marketing site, documentation surface, or lightweight product front door. The goal is a fast static-first experience with just enough server runtime for real interactions, without carrying a traditional server or a heavy application framework.
Application stack
The site is built with Astro 5, TypeScript, and Tailwind CSS 4. Content for projects, profile details, and blog posts lives in JSON under src/data, which keeps the public pages easy to update without adding a CMS dependency before one is needed.
Astro handles the page structure and routing, Tailwind provides the design system, and the Cloudflare adapter compiles the output for Cloudflare's edge runtime. The project is configured with output: 'server', so it can serve static assets efficiently while still supporting runtime routes like the contact endpoint.
Cloudflare runtime
The deployment target is Cloudflare Workers with static assets bound through Cloudflare Assets. The Worker entry is generated into dist/_worker.js/index.js, and wrangler.jsonc points Cloudflare at that Worker plus the built dist directory for the asset binding.
That gives the site a single edge deployment unit: static pages, images, styles, scripts, and API routes ship together. The same Cloudflare layer also provides observability, runtime environment variables, request metadata such as CF-Connecting-IP, and the network security controls that sit in front of the application.
GitHub automatic builds
The build flow is connected to GitHub so the repository is the source of truth. Pushes to the production branch trigger production builds, while non-production branches can create isolated branch builds for review before merge.
That branch build model is useful for portfolio work because content, styling, and infrastructure changes can be checked against a real Cloudflare-hosted preview rather than only a local dev server. It also keeps rollbacks simple: the deployed artifact maps back to a Git commit.
Contact form and Turnstile
The contact form posts to /api/contact, an Astro API route running on the Cloudflare runtime. Before any email is sent, the endpoint validates the submitted name, email, and message, then verifies the cf-turnstile-response token against Cloudflare Turnstile's site verification API.
Turnstile keeps the form usable without adding a visible challenge by default, while still giving the server a proof that the request passed Cloudflare's abuse checks. The secret key stays in the Cloudflare environment, and the public site key is exposed as a Wrangler variable for rendering the widget.
Email handling
Once a submission passes validation and Turnstile verification, the Worker sends the message through the configured email provider using environment-backed credentials. The email includes the sender's address as reply_to, so replies can go straight back to the person who submitted the form.
At the domain layer, Cloudflare Email Routing can handle inbound aliases for the domain, while the contact form handles structured outbound notifications from the site. Keeping those concerns separate makes the setup easier to reason about: Cloudflare owns DNS and inbound routing, and the application endpoint owns validated form delivery.
Domain management and security
The rest of the stack benefits from Cloudflare's standard domain management: DNS, managed TLS, HTTPS enforcement, proxying, caching, and security controls all sit in front of the Worker. That means the site gets a hardened edge by default before application-specific protections like Turnstile are even considered.
The result is a compact build with a practical operational footprint: code in GitHub, automatic Cloudflare builds, branch previews for review, edge-hosted assets and API routes, protected form submissions, and domain-level security managed from one platform.