I've wanted my own little corner of the internet for ages. Somewhere that's just mine, shows off the bits I've built and lets someone get in touch without any hassle. I finally sat down and built it. I had so much fun doing it that I figured I'd write up how the whole thing fits together.
I work with Cloudflare a lot and I genuinely love it, so hosting it there was never really a question. The part I was excited about was Astro. I'd been keen to give it a proper go for ages and a portfolio felt like the perfect excuse.
The stack
The site runs on Astro 6 with TypeScript and Tailwind CSS 4. Astro does the pages and the routing, Tailwind does the styling and the Cloudflare adapter gets it all ready for the edge. I've got it set to output: 'server', so most of the site is static and quick but I can still run a bit of server code where I actually need it, like the contact form.
All the content for my projects, my profile and these blog posts lives in plain JSON files under src/data. No CMS, no database, nothing extra to log into. When I want to add a project I open a file, add an object and push. That's it. For a site this size a whole content system would just be another thing to maintain for no real payoff. I can always add one later if I ever actually need it.
Running it on Cloudflare
This is the bit I reckon Cloudflare is perfect for. The adapter takes my site and compiles it into two parts. One is a small worker that handles the pages that have to run per request, like the contact endpoint. The other is a folder of static assets, so the pages, images, styles and scripts that can just be served straight off the edge. My wrangler.jsonc points Cloudflare at the adapter's server entrypoint and hands it that assets folder. The whole lot ships as one deployment.
Because it's all on Cloudflare I get a heap of things for free without wiring anything up. Observability, environment variables for my secrets, the real visitor IP on CF-Connecting-IP and all the network level security sitting in front of the worker before my code even runs.
Push to deploy
The whole thing is hooked up to GitHub so my repo is the source of truth. When I push to my production branch Cloudflare builds and deploys it for me. Push a feature branch and I get its own isolated preview build to check before I merge. That preview part is genuinely handy for a portfolio, because I can look at a content or styling change on a real Cloudflare URL instead of just trusting my local dev server. Rolling back is easy too, since every deploy maps straight back to a commit.
The contact form
The contact form posts to /api/contact, which is an Astro API route running on Cloudflare. Before it sends anything it checks the name, email and message are all there and sensible, then it verifies the cf-turnstile-response token against Cloudflare Turnstile. Turnstile is lovely because it keeps the bots out without making real people click on traffic lights all day. The secret key lives in the Cloudflare environment and only the public site key ever makes it near the browser.
One quick heads up if you're on Astro 6 like me. The old Astro.locals.runtime.env is gone, so the way you grab your bindings and secrets has moved. You now pull them in with import { env } from 'cloudflare:workers' instead. It caught me out for a second when I upgraded, so hopefully this saves you the head scratch.
Once a message is through validation and Turnstile the worker fires it off through my email provider. I set reply_to to whoever filled in the form so I can just hit reply and go straight back to them.
Security and the domain
The rest of it leans on Cloudflare doing what Cloudflare does. DNS, managed TLS, forcing HTTPS, caching and the security controls all sit in front of the worker, so the site is locked down at the edge before I've even thought about my own app level protections like Turnstile. For the tiny bit of effort it takes to set up, you get a lot back.
The result
I was really happy with how it turned out! It's quick, it costs me next to nothing and there's almost nothing to maintain. If you're thinking of building your own, my one bit of advice would be to lean on your host's building blocks instead of reaching for a big framework and a server you have to babysit. Astro and Cloudflare gave me a proper site with a real little backend for about the price of a plain static page. Happy coding!