Hosting your site with GitHub Pages

Hosting your site with GitHub Pages

Your agent just generated a nice little web application. It runs on your computer when you type npm run dev. Great. But how do you show it to your colleague, a client, or your mom?

This is where hosting comes in: putting your site on a server reachable from the Internet, with a real web address (a URL). And GitHub offers a free service for this, called GitHub Pages.

What is GitHub Pages, exactly?

Imagine that GitHub lends you a little piece of server, for free. You drop the files of your site onto it (HTML, CSS, images, JavaScript), and GitHub makes them available to everyone via a web address.

The default address looks like this:

https://your-username.github.io/project-name/

That’s already pretty good to get started. And if you want a real domain like mycoolproject.com, that’s also possible (we’ll get to that later).

Tip💡 Almost everything can be delegated to an agent

Most of the steps in this chapter can be performed directly by your coding agent (Claude Code, Cursor, Copilot CLI…) if it has access to the gh tool — the official GitHub command-line. Check with gh --version in a terminal, or ask your agent to install it and authenticate (gh auth login).

Whenever a step can be delegated, you’ll find a 🤖 callout with the right prompt to copy-paste. The manual instructions still apply — use whichever method speaks to you.

When to use GitHub Pages?

GitHub Pages is perfect for:

  • A landing page for your project or company
  • A personal portfolio
  • Documentation for a piece of software
  • A prototype to show off quickly
  • A static blog
  • A small web app (calculator, todo-list, game…)

However, GitHub Pages can only do one thing: serve files that don’t change on their own. This is called a static site. It’s perfect for everything above, but it’s not suitable if your app needs to:

  • Store data submitted by users (forms, accounts, comments…)
  • Run server-side code (payments, sending emails, heavy calculations…)
  • Hide secret API keys
NoteDon’t panic

If your project needs these features, other free services exist: Vercel, Netlify, Cloudflare Pages. We’ll mention them at the end of the chapter. But start with GitHub Pages: it’s the simplest.

First time: an ultra-simple site in 3 minutes

Let’s start with the most basic case: you have an index.html file in your repo, and you want to put it online.

Step 1: on GitHub, go to your repo.

Step 2: click Settings (in the top bar of the repo, far right).

Step 3: in the left menu, click Pages.

Step 4: under “Source”, choose Deploy from a branch. Under “Branch”, pick main and the / (root) folder. Click Save.

Step 5: wait 30 seconds to 2 minutes. Reload the page. You’ll see a green box appear:

✅ Your site is live at https://your-username.github.io/your-project/

Click the link. Your site is online. That’s it.

Tip🤖 Delegate to the agent

Rather than navigating through Settings, ask:

“Enable GitHub Pages on this repo, in ‘Deploy from a branch’ mode, on the main branch at the root.”

The agent will run something like:

gh api -X POST /repos/:owner/:repo/pages \
  -f "source[branch]=main" -f "source[path]=/"

And check the URL of your site with:

gh api /repos/:owner/:repo/pages --jq '.html_url'

Second case: a site that needs to be “built”

Modern frameworks (React, Vue, Astro, Next.js, Svelte…) don’t directly produce browser-readable HTML. They need a build step that transforms your code into static files ready to be served.

Concretely, you type npm run build and a folder appears (often dist/ or build/ or out/) containing the final site.

For these projects, we can’t just deploy the main branch directly — GitHub doesn’t know how to run npm run build. So we need to ask GitHub Actions (the automation service we saw in the previous chapter) to do the build for us, then hand the result to GitHub Pages.

Here’s the magic file to drop in your repo, at .github/workflows/deploy.yml:

name: Deploy to GitHub Pages

on:
  push:
    branches: [main]

permissions:
  contents: read
  pages: write
  id-token: write

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - run: npm ci
      - run: npm run build

      - uses: actions/upload-pages-artifact@v3
        with:
          path: ./dist  # ← adjust for your framework

  deploy:
    needs: build
    runs-on: ubuntu-latest
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    steps:
      - id: deployment
        uses: actions/deploy-pages@v4

A few things to note:

  • path: ./dist: that’s the folder produced by npm run build. Vite and Astro use dist, Next.js uses out, Gatsby uses public, and so on. When in doubt, run npm run build locally and check which folder appears.
  • Once the file is pushed to GitHub, go back to Settings → Pages and choose Source: GitHub Actions.
Tip🤖 Delegate to the agent

Rather than wrestling with YAML syntax, just say:

“Add a GitHub Actions workflow to deploy this project to GitHub Pages. Detect my framework on your own and configure the base path based on the repo name.”

The agent will:

  1. Read your package.json to identify the framework

  2. Create .github/workflows/deploy.yml with the right values

  3. Adjust vite.config.js (or equivalent) for the base path

  4. Enable “GitHub Actions” mode in Settings via:

    gh api -X PUT /repos/:owner/:repo/pages -f build_type=workflow
  5. Commit, push, and watch the deployment with gh run watch

Pitfall #1: broken paths

Here’s the problem that wastes the most time for beginners.

When your site runs locally, it’s served at the root: http://localhost:5173/. Your index.html loads its resources like this:

<link rel="stylesheet" href="/styles.css">
<script src="/app.js"></script>

The browser looks for these files at http://localhost:5173/styles.css. All good.

But on GitHub Pages, your site is not at the root of the domain — it’s in a sub-folder:

https://your-username.github.io/my-project/

So the browser looks for https://your-username.github.io/styles.css (at the root of the domain), instead of https://your-username.github.io/my-project/styles.css. Result: a blank page, no style, no JavaScript. The browser console is full of 404 errors.

The fix: tell your framework that the site will be served from the /my-project/ sub-folder. This is called configuring the base path.

With Vite (React, Vue, Svelte…), in vite.config.js:

export default defineConfig({
  base: '/my-project/',
})

With Astro, in astro.config.mjs:

export default defineConfig({
  base: '/my-project',
})

With Next.js in static export mode, in next.config.js:

module.exports = {
  output: 'export',
  basePath: '/my-project',
  images: { unoptimized: true },
}

Replace my-project with the exact name of your GitHub repo.

Pitfall #2: the refresh that breaks everything

If your site has multiple “pages” handled on the browser side (for example with React Router), you may have URLs like /about or /contact. Everything works fine as long as you navigate by clicking links. But if you refresh the page on /about, you get a 404.

Why? Because GitHub Pages looks for an about.html file that doesn’t exist. Your app only handles routing once it’s loaded.

The clever fix: create a 404.html file identical to index.html. GitHub Pages will serve 404.html for all unknown URLs, your app will start up normally, and its router will take over.

Add this step in your workflow, right after the build:

      - run: cp ./dist/index.html ./dist/404.html

Using your own domain name

Having your-username.github.io/my-project/ as an address is fine to start with. But for a real project, you’ll probably want something like mycoolproject.com.

Here’s how:

Step 1: buy the domain at a registrar (Namecheap, Cloudflare Registrar, Gandi, Google Domains… count $10-15 per year).

Step 2: in Settings → Pages on GitHub, in the “Custom domain” field, type your domain (e.g. mycoolproject.com) and click Save.

Step 3: at your registrar, configure the DNS records. DNS is the Internet’s phonebook: it maps a domain name to a server address.

If you want to use your primary domain (mycoolproject.com), add 4 A records pointing to GitHub’s servers:

A     @    185.199.108.153
A     @    185.199.109.153
A     @    185.199.110.153
A     @    185.199.111.153

If you want to use a subdomain (www.mycoolproject.com), add a CNAME record:

CNAME  www  your-username.github.io

Step 4: be patient. DNS propagation can take anywhere from a few minutes to several hours. Refresh the Settings → Pages page: you should see a green checkmark next to your domain.

Step 5: check the Enforce HTTPS box. GitHub will automatically generate an SSL certificate for your domain (that’s what gives the little padlock in the browser). It can take up to 30 minutes the first time.

Tip🤖 Delegate to the agent (partially)

The GitHub-side configuration can be done by the agent:

“Configure mycoolproject.com as a custom domain for GitHub Pages on this repo, and enable HTTPS.”

Underlying commands:

gh api -X PUT /repos/:owner/:repo/pages -f cname=mycoolproject.com
gh api -X PUT /repos/:owner/:repo/pages -F https_enforced=true

⚠️ However, the DNS configuration at your registrar remains manual: your agent doesn’t have access to your Namecheap or Gandi account. You have to go there yourself (unless you use Cloudflare, which has an API and a command-line tool — but that’s another story).

Warning: everything you deploy is public

GitHub Pages serves your files to anyone with the URL. Absolutely everyone can:

  • Read your HTML, CSS, JavaScript
  • Download your images
  • See the URLs of your API calls

This means NEVER put a secret in your code: no API key, no password, no token. Even if you put them in an environment variable at build time, they’ll end up in the final JavaScript, readable by anyone.

If your site needs to call an API with a secret key, you need to go through a middleman that keeps the key server-side. Free solutions exist (Cloudflare Workers, Vercel functions…), but that’s beyond the scope of this chapter.

Warning🤖 Be vigilant with agents

Agents don’t always have good security instincts. It has happened that an agent injects an API key into the client code “just to make it work”. Before each deployment, ask explicitly:

“Check that no API key, no secret, and no sensitive variable appears in the code that will be deployed. List all import.meta.env.VITE_* (or equivalent) and tell me if any shouldn’t be there.”

What about other solutions?

GitHub Pages is great to start with. But in some cases, its bigger siblings are more convenient:

  • Cloudflare Pages: free, faster, handles automatic previews on every pull request (very handy for testing a change before merging).
  • Netlify: pioneer of static deployment, great developer experience, generous free tier.
  • Vercel: ideal for Next.js projects (it’s the company that makes Next.js), very good for more dynamic apps.

All these services plug into your GitHub repo in a few clicks and deploy on every push, like GitHub Pages — but with extra features.

The typical vibe coder workflow

With an agent that has access to gh, the full flow fits in a few sentences:

  1. “Make me a landing page with Astro.”
  2. “Create a public GitHub repo and push the code.” → gh repo create + git push
  3. “Add the GitHub Pages deployment workflow, enable Pages in GitHub Actions mode, and watch the deployment until it’s live.” → workflow YAML + gh api to enable + gh run watch
  4. The agent gives you the URL. You click. It’s online.

From the initial description to a public site: about ten minutes, without ever leaving the chat with the agent. That’s vibe coding, deployed.

You can of course follow each step by hand — it’s even a good idea the first time, to understand what’s happening. But once you have the mechanics down, delegating is liberating.

What if it doesn’t work?

My site shows a 404 page

  • Check that Pages is properly enabled in Settings → Pages.
  • Look at the Actions tab: did your workflow run without errors?
  • If it’s a project site, check that the base path is properly configured.

The page loads without styles, broken

  • It’s almost always a base path misconfiguration. Open the browser’s developer tools (F12), “Console” tab: you’ll see 404 errors with the expected URLs. Compare with the actual URL of your site.

The deployment fails in GitHub Actions

  • Click the failing workflow in the Actions tab, then the failing step. Copy-paste the error message to your agent: it’s often a simple configuration issue to fix.

Everything looks right but the new content doesn’t show up

  • Browser cache is your enemy. Try in private browsing mode, or do a hard refresh (Ctrl+Shift+R on PC, Cmd+Shift+R on Mac).
Tip🤖 Let the agent diagnose

When something doesn’t work, agents are great at investigating on their own:

“My GitHub Pages deployment isn’t working. Look at the latest workflow, identify the error, and propose a fix.”

The agent will typically:

gh run list --workflow=deploy.yml --limit 1
gh run view <run-id> --log-failed
gh api /repos/:owner/:repo/pages

Then analyze the logs and fix the issue (often a misconfigured build path, a forgotten base path, or a missing permission).


Next chapter: Security.