I watched a friend generate a genuinely nice portfolio in Claude in about ten minutes. Then the momentum vanished: "wait, how do I actually host this?" We spent longer arguing about Vercel vs Netlify and DNS than it took to build the site. Minutes to create, an afternoon to deploy β that asymmetry is the whole reason this exists.
So I built MakeMySiteLive: you upload a ZIP (or paste a single HTML file), and you get a live HTTPS URL. No repo, no build step, no server. Here's how it's put together and the parts that were more annoying than I expected.
The shape of it
Three pieces with a hard trust boundary between them:
- Web (Next.js) β dashboard + marketing. It's a BFF: no database access, no business logic.
- Control plane (Fastify + Postgres/Prisma) β all business logic, the only thing that touches the DB.
- Site-server β serves the actual published sites. Holds no local state, so it scales horizontally.
That last property was a deliberate constraint, and it shaped everything.
Stateless serving
The site-server can run on any number of nodes, and any node can serve any site. Files live in Cloudflare R2; password-gate sessions live in Redis; nothing is on local disk. Resolving a request is just: map the Host header to a site (cached in Redis on a short TTL), then stream the file from R2.
const info = await resolveInfo(host); // Redis cache, 300s TTL
const file = await storage.getFile(`${info.folderPath}${path}`); // R2
// SPA fallback to index.html, branding badge injected for free-tier sites
The payoff: deploys are immutable versioned writes, and a rollback is just flipping which version is "active" in one transaction β no files move.
Custom domains + automatic TLS
This was the real time sink. Customers point a CNAME at us and HTTPS "just works," including cert issuance and renewal. That's Cloudflare for SaaS custom hostnames doing the heavy lifting β you register the hostname via their API, then poll for SSL status. Making that flow reliable took longer than the entire upload pipeline.
Single-file "microsites"
Plenty of people don't have a ZIP β they have one HTML file. So microsites: paste/upload a single .html, get a live page. These store the HTML inline on the version row and serve it directly, no R2 round-trip.
The interesting decision: I originally gave microsites the same version history + rollback as full sites. Shipped it, then tore it out. For a one-off landing page, "v1 / v2 / roll back" is just noise β so now a republish simply overwrites the single version. Less surface, less confusion. A good reminder that "consistent with the rest of the system" isn't always the right call.
Letting an AI agent deploy
Since AI usually generated the page, making someone copy it into a dashboard felt backwards. So there's a hosted MCP (Model Context Protocol) server β an agent like Claude connects once (OAuth, scoped to your account) and gets tools to create and deploy sites from the chat.
reg(
"create_microsite",
"Create and publish a microsite from one standalone HTML string.",
{ subdomain: z.string(), name: z.string(), html: z.string() },
async (args) => ok(await createAndDeployMicrosite(userId, args)),
);
"Publish this as a site" β live URL, no dashboard. If you want the setup details, I wrote it up here: connecting the MCP server to Claude.
Hosting arbitrary HTML is a phishing magnet
This is the part you can't hand-wave. Free subdomains + arbitrary user HTML is exactly what abuse loves. So every upload runs through:
1. Unzip β require index.html at root, reject "../" path traversal, strip macOS cruft
2. Scan β ClamAV over TCP + blocked extensions + phishing/eval heuristics
3. Publish β write a new immutable version, flip "active" in one transaction
It's not perfect, but "instant and frictionless" can't mean "open relay for phishing kits," so this layer has to be real from day one.
What I'd tell past-me
- Decide the statelessness constraint up front. Retrofitting "no local state" is miserable; designing for it is free.
- Don't mirror your own abstractions reflexively β microsites didn't need full versioning just because full sites had it.
- For a solo dev selling worldwide, offload tax/VAT/GST to a merchant of record early. I used one and it saved me a category of work I had zero interest in.
If you want to poke at it, the first site is free, no card: makemysitelive.com.
Question I'm genuinely chewing on: for static hosting like this, is per-site/usage pricing better than a flat monthly subscription? Subscription is cleaner MRR, but per-site feels more honest for someone who just wants one page online. Curious how others have landed on this.
Top comments (3)
The deployment friction point is very real. Agents can create small sites quickly, but the handoff dies when hosting, DNS, and build assumptions enter the flow. An MCP publishing path is interesting because it turns deploy into an explicit tool call with a narrow surface instead of a vague instruction to the user.
Exactly, you said it better than I did. The narrow tool surface is the whole point. Instead of the agent telling the user to go make a repo and set DNS, it makes one deploy call and gets a live URL back that it can check and share, so the loop actually closes inside the chat.
A thing I did not expect while building it. Agents call the tools far more reliably when each one has an obvious name and a tiny input list, like a create microsite tool that only takes a subdomain, a name and the html. Keeping the surface narrow helped the model get it right as much as it helped safety, since each tool is also scoped to the user through OAuth.
Thanks for the really thoughtful read.
The tiny input list point is underrated. Agents do much better when a tool name almost contains the workflow: create microsite, deploy static page, verify URL. Once the tool surface gets broad, the model starts doing product management inside the tool call. I also like the OAuth scoping angle because it makes the simple path the safer path.