on this page
You’ve pushed your project to GitHub, the build runs clean, and now you’re staring at a page full of logos: Vercel, Netlify, Cloudflare Pages, GitHub Pages. Each one promises to put your site on the internet for free, and each one’s homepage reads like it was written by people who have never met a beginner. So which button do you press?
Before you pick, it helps to know what you’re actually buying (even at the price of nothing). Once you see what a frontend host does under the hood, the four options stop looking interchangeable and start looking like what they are: the same basic job with different opinions bolted on.
What a frontend host actually does
Strip away the marketing and a frontend host does one boring, useful loop. It watches your GitHub repository. When you push, it pulls your latest code, installs your dependencies, and runs your build command. Your build spits out a folder of plain static files: HTML, CSS, JavaScript, images. The host grabs that folder, copies it onto a CDN (a Content Delivery Network, a fleet of servers spread around the world so your files sit close to whoever’s loading them), and hands you a URL with HTTPS already turned on.
That’s the whole trick. You write code and push it. The host turns your source into shippable files and serves them, fast and encrypted, without you renting a server or installing anything. If you want the longer version of this pipeline (DNS, builds, certificates, all of it), the localhost to production guide walks the full path. This guide is narrower: given that you understand the loop, which host should you hand your project to?
The important thing to hold onto is that word static. A frontend host serves files. It does not, by default, run a backend that talks to a database or holds a long-lived connection open. Some of these hosts can run small server-side functions on the side, but their core competency is shipping files to browsers, fast. Forget that and you’ll spend an afternoon wondering why your Node and Express server “isn’t deploying.”
The four beginner options, honestly
All four do the loop above. Where they differ is what they’re tuned for and the catch nobody mentions until you hit it.
The comparison, in one table
| Host | Free tier | Best for | Custom domains | Preview deploys | Main gotcha |
|---|---|---|---|---|---|
| GitHub Pages | Free, unlimited | Portfolios, docs, plain static sites | Yes, free HTTPS | No native PR previews | Static only, no server functions |
| Vercel | Hobby tier, personal use | Next.js apps | Yes, free HTTPS | Yes, per pull request | Commercial use needs a paid plan |
| Cloudflare Pages | Free, unmetered bandwidth | Astro and static sites, global speed | Yes, free HTTPS | Yes, per pull request | Dynamic features mean learning Workers |
| Netlify | Free, with build-minute limits | Sites needing redirects or forms | Yes, free HTTPS | Yes, per pull request | Free limits on builds and bandwidth |
The settings everyone gets wrong
Whichever host you pick, the first deploy asks the same two questions, and beginners trip on both. Getting these right is most of the battle.
The build command is what the host runs to turn your source into files. For most modern projects that’s npm run build (npm being the Node Package Manager, the tool that installs your libraries and runs the scripts defined in your package.json). Most hosts guess this correctly when they detect your framework, but if you’ve renamed your scripts, you set it yourself.
# What the host actually runs on its server
npm ci
npm run build
The output directory (Netlify calls it the publish directory) is the folder your build leaves behind, the one the host should actually serve. This is the single most common reason a deploy “succeeds” but shows a blank page or a 404: the host built your files, then served the wrong folder. The defaults depend on your tool:
Astro / Vite -> dist
Create React App -> build
Next.js -> handled by the adapter, you don't set this
For Astro and Vite projects the answer is dist. For an old Create React App project it’s build. For Next.js you generally don’t touch this at all, because Vercel (or the framework adapter on another host) wires it up for you. Set the wrong one and you’ll see a confidently green build followed by a page that isn’t there.
Environment variables and the prefix that bites
Environment variables are the configuration values you don’t hardcode: an API key, a backend URL, a feature flag. You set them once in the host’s dashboard, and the build reads them.
Here’s the gotcha that catches nearly everyone. Frontend build tools only expose a variable to the browser if its name starts with a specific prefix:
| Framework | Prefix | Example |
|---|---|---|
| Astro | PUBLIC_ | PUBLIC_API_URL |
| Vite | VITE_ | VITE_API_URL |
| Next.js | NEXT_PUBLIC_ | NEXT_PUBLIC_API_URL |
A variable named API_URL with no prefix stays invisible to your frontend code, which is why your fetch call suddenly points at undefined in production. Add the prefix and it works.
Custom domains and preview deploys
A custom domain turns your-project.pages.dev into yourname.dev. All four hosts let you add one, and all four issue the HTTPS certificate for free and renew it automatically, so you’re not buying or installing anything. You point your domain’s DNS at the host, wait a few minutes, and it’s live.
Preview deploys are the feature you’ll miss most if you skip it. When you open a pull request, Vercel, Netlify, and Cloudflare Pages each build that branch separately and give you a private URL to look at. You see the change running on the real internet before it touches your main site, which makes “can I see it?” a link instead of a screenshot. GitHub Pages doesn’t do this natively, which is one of the clearer reasons to reach for one of the others once your project grows past a single person.
Which one should I use?
Skipping the diplomacy, here are concrete picks.
Building an Astro site or a plain static site? Use Cloudflare Pages or GitHub Pages. Cloudflare if you want preview deploys and that unmetered bandwidth; GitHub Pages if you want the absolute shortest path and your code’s already there.
Building a Next.js app? Use Vercel. They make Next.js, every feature works on the first try, and you’ll spend zero time fighting configuration. You can host Next.js elsewhere, but on Vercel it just goes.
Need redirects or form handling without standing up a backend? Use Netlify. The redirect rules and built-in forms are the thing it does better than the others, and for a marketing site that’s exactly the friction you want gone.
If you genuinely don’t know yet, Cloudflare Pages is a safe default for most static and Astro projects, and nothing here locks you in. Moving a static site between hosts is an afternoon, not a migration.
Common mistakes
Most failed first deploys come down to one of these four, and now that you’ve read the rest of this guide, each one should look familiar.
Wrong output directory. The build goes green and the site is blank or 404s. You told the host to serve build when your tool wrote to dist, or the reverse. Match the directory to your framework and it appears.
Environment variables set locally but not on the host. Your .env file lives on your laptop and never gets pushed (rightly, it’s in .gitignore). The host has never seen those values. Add every variable your app needs in the host’s dashboard, with the correct prefix, then redeploy.
Expecting a static host to run a backend. You wrote a Node and Express server, or you’re connecting straight to PostgreSQL from the browser, and you’re trying to deploy it to a frontend host. That’s not what these do. Your frontend goes on Cloudflare Pages or Vercel; your server goes somewhere built to run it, like Railway or Render, and the two talk over HTTPS.
A build that passes locally but fails on the host. Almost always a different Node version. Your laptop runs Node 20, the host defaults to Node 18, and some package quietly disagrees. Pin the version so both machines match:
{
"engines": {
"node": "20.x"
}
}
Now go ship it
Pick the host that fits the project in front of you, set the build command and output directory, add your environment variables with the right prefix, and push. The first deploy that comes back green with a real URL is the moment the thing you built stops being a localhost secret and becomes something you can send to a stranger. That’s the whole point of all of this: not the perfect host, just the project that’s actually live.