Programming Beginner 7 min

How to Deploy a Static Site to GitHub Pages

GitHub Pages is free static hosting built directly into GitHub. There is no server to provision, no bill to pay for small traffic, and deployments happen automatically on every push. It handles HTML, CSS, JavaScript, and pre-built frameworks — anything that does not need server-side code at runtime.

Step-by-step

  1. 1

    Push your site to a GitHub repository

    Your source code must be in a GitHub repository. If deploying a pre-built folder (e.g. dist/), either commit the build output to a dedicated branch or use GitHub Actions to build it automatically (covered later).

    bash
    git init
    git add .
    git commit -m "Initial commit"
    git remote add origin https://github.com/your-user/your-repo.git
    git push -u origin main
  2. 2

    Enable Pages in repository settings

    Go to Settings → Pages in your repository. Under Source, choose Deploy from a branch, then select the branch (typically main) and the folder (/ (root) or /docs). Click Save. GitHub starts a deployment immediately and shows the live URL — usually https://your-user.github.io/your-repo/ — within about 60 seconds.

    bash
    # After enabling, verify the deployment status:
    gh run list --workflow pages-build-deployment
  3. 3

    Prevent unwanted Jekyll processing

    By default, GitHub Pages pipes your site through Jekyll, which skips any file or folder whose name starts with an underscore (_app, _next, __mocks__). If your framework outputs files like these, add a .nojekyll file to the root of your deployed branch.

    bash
    # In the root of your repo (or dist/ before deploying):
    touch .nojekyll
    git add .nojekyll
    git commit -m "Disable Jekyll processing"
    git push
  4. 4

    Deploy a built framework with GitHub Actions

    For Vite, Next.js static export, or any framework that needs a build step, use GitHub Actions to build and publish instead of committing the dist/ folder. Create .github/workflows/deploy.yml — this is the recommended approach for any non-trivial site.

    yaml
    name: Deploy to GitHub Pages
    
    on:
      push:
        branches: [main]
    
    permissions:
      contents: read
      pages: write
      id-token: write
    
    jobs:
      deploy:
        environment:
          name: github-pages
          url: ${{ steps.deploy.outputs.page_url }}
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
    
          - name: Set up Node
            uses: actions/setup-node@v4
            with:
              node-version: 20
              cache: npm
    
          - run: npm ci
          - run: npm run build          # outputs to dist/
    
          - uses: actions/upload-pages-artifact@v3
            with:
              path: dist
    
          - id: deploy
            uses: actions/deploy-pages@v4
  5. 5

    Switch the Pages source to GitHub Actions

    When using the workflow above, go back to Settings → Pages → Source and switch from "Deploy from a branch" to GitHub Actions. Otherwise GitHub deploys both your branch and the workflow, which causes conflicts.

    bash
    # Confirm the deployment URL once the workflow finishes:
    gh run list --limit 5
  6. 6

    Add a custom domain

    To serve the site from www.example.com instead of the default *.github.io URL: add a CNAME file containing your domain to the root of the deployed branch, then create a CNAME DNS record pointing to your-user.github.io. HTTPS is provisioned automatically via Let's Encrypt — it typically takes a few minutes to activate.

    bash
    # Create the CNAME file
    echo 'www.example.com' > CNAME
    git add CNAME && git commit -m "Add custom domain" && git push
    
    # DNS record to add with your registrar:
    # Type: CNAME
    # Name: www
    # Value: your-user.github.io
  7. 7

    Know the platform limits

    GitHub Pages is generous for personal projects but not designed for high-traffic production apps. Keep these limits in mind: the published site must be 1 GB or less; GitHub recommends staying under a soft bandwidth limit of 100 GB/month; and there is a 10 builds/hour limit per repository. No server-side code, no databases — pure static only.

    bash
    # Check your site's published size locally:
    du -sh dist/
    # Anything over ~500 MB is a red flag for a static site.

Tips & gotchas

  • For a personal portfolio at <code>username.github.io</code>, create a repo named exactly <code>username.github.io</code> — GitHub auto-serves it from the root without a sub-path.
  • If your single-page app shows 404 on deep links, add a <code>404.html</code> that redirects to <code>index.html</code> — GitHub Pages serves it for missing paths.
  • Cache-busting is free: GitHub Pages serves files with a strong ETag. Force a re-fetch by appending a query string or using content-hashed filenames (which Vite does automatically).
  • The <code>gh-pages</code> npm package (<code>npm install --save-dev gh-pages</code>) automates pushing a <code>dist/</code> folder to the <code>gh-pages</code> branch without the Actions workflow.

Wrapping up

GitHub Pages covers the vast majority of static hosting needs at zero cost. For simple HTML, enable it in Settings in two clicks. For a framework like Vite or Next.js, the Actions workflow above gives you automated CI/CD with no third-party account required. Add a custom domain and HTTPS is handled for you. The only real constraint is the static-only requirement — the moment you need a database or server-side logic, graduate to a platform like Vercel, Render, or a VPS.

#Git #GitHub #Deployment
Back to all guides

Need Help With Your Project?

Book a free 30-minute consultation to discuss your technical challenges and explore solutions together.