GitGitHub Pages

GitHub Pages

GitHub Pages is free static hosting for any repo. Push HTML, CSS, and JS (or let a build step produce them) and GitHub serves them from a .github.io URL — or your own custom domain — with automatic HTTPS. It is how millions of project sites, blogs, portfolios, and documentation pages are hosted, and it costs nothing for personal use.

What you can host
  • Static websites — plain HTML, or output of any static site generator (Jekyll, Hugo, Astro, Next.js export, Vite, etc.).

  • Project documentation rendered from Markdown.

  • Personal portfolios and blogs.

  • Single-page apps (React/Vue/Svelte build output).

  • API documentation generated from OpenAPI/Swagger.

What you cannot host: anything that needs a server (PHP, Node, Rails, Python). Pages is static-only.

URL patterns

Repo name

Type

URL

jsmith.github.io

User/org site

https://jsmith.github.io

jsmith/blog

Project site

https://jsmith.github.io/blog

acme/docs

Org project site

https://acme.github.io/docs

jsmith.github.io/anything

Subpath on user site

https://jsmith.github.io/anything

You get one site per user (username.github.io) and as many project sites as you have repos.

Enabling Pages
  • Go to the repo on GitHub. Open Settings → Pages.

  • Under Source, pick a deployment method (see the three options below).

  • Save. Within a minute or two, your site is live at the URL shown at the top of the Pages settings.

The three deployment options
1. Deploy from a branch

Simplest path. Pick a branch (commonly main or gh-pages) and a folder (root / or /docs). GitHub runs Jekyll on it (or just serves files as-is if _config.yml is absent) and publishes the result.

The classic gh-pages branch flow

Bash
# Build your site into ./dist
npm run build

# Push dist/ to a gh-pages branch
npx gh-pages -d dist

# Or manually:
git checkout --orphan gh-pages
git --work-tree dist add --all
git --work-tree dist commit -m "Deploy"
git push origin HEAD:gh-pages --force
git checkout main
2. GitHub Actions (recommended for everything modern)

Build your site in CI and publish the output. Works with any static generator. Settings → Pages → Source → GitHub Actions.

.github/workflows/pages.yml

Text
name: Deploy site
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 }
      - run: npm ci
      - run: npm run build
      - uses: actions/upload-pages-artifact@v3
        with: { path: ./dist }

  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
3. Custom (advanced)

Configure your own workflow that uploads to the Pages artifact endpoint. Same as option 2, just hand-rolled.

Jekyll — the historical default

Drop Markdown files and a _config.yml into your repo, push, and Jekyll builds it into HTML automatically. No build config needed. This is why many older project sites are Jekyll — it was the path of least resistance for years.

A minimal _config.yml

Text
title: My Project
description: A few words about it
theme: jekyll-theme-cayman
markdown: kramdown

Test Jekyll locally before pushing

Bash
gem install bundler jekyll
bundle init
echo 'gem "github-pages", group: :jekyll_plugins' >> Gemfile
bundle install
bundle exec jekyll serve   # http://localhost:4000
Custom domains and HTTPS
  • Buy a domain wherever you like (Namecheap, Cloudflare Registrar, etc.).

  • Add a CNAME file at the repo root containing just the bare domain (example.com).

  • At your DNS provider, add an A record for the apex pointing to GitHub Pages IPs (185.199.108.153, 185.199.109.153, 185.199.110.153, 185.199.111.153), or an ALIAS/ANAME if your DNS supports it.

  • For www.example.com, add a CNAME record pointing to jsmith.github.io.

  • Wait a few minutes, then in Settings → Pages, GitHub provisions a Let's Encrypt certificate. Check Enforce HTTPS.

CNAME file at repo root

Text
example.com
Limits

Resource

Limit

Repo size

1 GB recommended (10 GB hard cap)

Site size

1 GB

Bandwidth

100 GB/month soft limit

Builds

10 per hour for branch-based deploys

Custom domains

No HTTPS for unverified domains

File types served

Static files only — no server-side execution

These are generous for personal projects. If you blow past 100 GB/month you should be using Netlify, Vercel, or Cloudflare Pages instead.

Pages for any modern stack
  • Next.jsnext build && next export (or output: "export" in next.config.js), then publish out/.

  • Astronpm run build, publish dist/.

  • Hugohugo, publish public/.

  • Vitevite build, publish dist/. Set base: "/repo-name/" for project sites.

  • MkDocs / Docusaurus / Sphinx — all have first-class GitHub Pages publishers.

The decision tree

Pages vs Netlify vs Vercel

Text
Need server functions / SSR / Edge?    -> Vercel / Netlify / Cloudflare
Need a custom build process?           -> Pages with Actions (free)
Just static HTML?                      -> Pages with branch deploy
Documentation for an open-source repo? -> Pages (it's the convention)
Production site with > 100 GB traffic? -> Move to a CDN-backed host
Pages is the easiest deploy in software
Push to main, wait 60 seconds, site is live, HTTPS included, no credit card, no signup with another vendor. For any static site, it is the lowest-friction option in existence. Use it for portfolios, docs, blog posts, project landing pages.
Tip
For project sites under `username.github.io/repo-name`, remember to set the base path in your build config — Vite, Next.js, and Astro all need to know they are hosted at a subpath, or your asset URLs will 404.