| |

GitHub 15 ๐ŸŒ GitHub Pages: Hosting Static Sites

โฑ๏ธ Estimated read: 10 min | |

GitHub Pages turns a repository into a static website. It is not a general-purpose web host. It serves HTML, CSS, JavaScript, and assets directly from a branch or a GitHub Actions artifact, with no server-side execution, no database, and no dynamic request handling. For documentation, project landing pages, and personal blogs, it is free, integrated with the repository, and requires no infrastructure. The exam tests two things: how to configure the publishing source, and how to attach a custom domain without breaking HTTPS.

Key point: GitHub Pages can publish from a branch (/ or /docs) or from a GitHub Actions workflow. If you need a custom build process, use Actions. If you are serving pre-built static files, publishing from a branch is simpler. Custom domains require a CNAME file (branch publishing) or a DNS record (Actions publishing) and an HTTPS certificate provisioned by GitHub.


Why GitHub Pages exists

The hosting problem. A documentation site or project page does not need a server. It needs files served over HTTP. GitHub Pages provides that for free: the repository already contains the source, and Pages serves the output. There is no separate deployment pipeline, no FTP, no bucket policy. A push to the configured branch is the deployment .

The build problem. Static site generators (Hugo, Jekyll, Vite, Astro) produce a directory of files. If those files are committed to a branch, Pages can serve them directly. If they are generated during CI, an Actions workflow can build them and deploy the artifact to Pages without ever committing the output. The choice depends on whether the generated files should be versioned or ephemeral .

The custom domain problem. A github.io URL is functional but not professional. A custom domain maps docs.example.com or example.com to the Pages site. The mapping requires DNS records and a verification step on GitHub. Once verified, GitHub provisions a free TLS certificate through Let’s Encrypt and serves the site over HTTPS .

The security problem. A custom domain that is configured in DNS but not verified on GitHub is vulnerable to domain takeover. If the Pages site is disabled and the DNS record remains, an attacker can claim the subdomain and serve content under the organization’s name. Verification (via a TXT record) and removing stale DNS records are the defenses .

The limits problem. GitHub Pages is not for commerce. It is not for SaaS. It has soft bandwidth limits (100 GB/month), soft build limits (10 per hour for branch publishing), and a 1 GB published site size limit. Deployments time out after 10 minutes. Exceeding these limits results in a polite email, not immediate termination, but the site may not be served reliably .


a. Configuring the publishing source

The publishing source is configured in the repository settings under Settings โ†’ Pages. The “Source” dropdown offers two paths: Deploy from a branch and GitHub Actions.

For branch publishing, the source branch can be any branch, and the folder can be / (root) or /docs. When a push lands on the configured branch and folder, Pages builds and publishes the site. The default is the root of the default branch. For a simple HTML site, the root is sufficient. For a project that keeps documentation alongside code, /docs is the common choice .

For Actions publishing, the source is set to GitHub Actions. This removes the branch-based build trigger entirely. The workflow must check out the repository, build the site (if needed), upload the artifact with actions/upload-pages-artifact, and deploy with actions/deploy-pages. The workflow needs permissions: pages: write and id-token: write to function .

A critical caveat for branch publishing: commits pushed by a GitHub Actions workflow using GITHUB_TOKEN do not trigger a Pages build. If a workflow generates files and pushes them to the source branch, the site will not update. The fix is to use a personal access token for the push, or to switch to Actions publishing where the workflow deploys directly .


b. Publishing with GitHub Actions

The Actions workflow pattern for Pages is standardized. The sequence is: checkout, build, configure-pages, upload-pages-artifact, deploy-pages. The configure-pages action sets up the Pages environment. The upload-pages-artifact action packages the output directory. The deploy-pages action publishes it .

name: Deploy to Pages
on:
  push:
    branches: ['main']
  workflow_dispatch:

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

concurrency:
  group: 'pages'
  cancel-in-progress: true

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    steps:
      - uses: actions/checkout@v4
      - name: Build
        run: npm ci && npm run build
      - uses: actions/configure-pages@v5
      - uses: actions/upload-pages-artifact@v3
        with:
          path: './dist'
      - id: deployment
        uses: actions/deploy-pages@v4

The path in the upload step must point to the directory containing the final static files (e.g., dist, build, _site). If the site is already pre-built and committed, the path can be ./ or ./docs. The environment block creates a deployment record that GitHub links from the repository sidebar .

The concurrency block prevents two deployments from running simultaneously. Without it, a rapid sequence of pushes can cause the second deployment to fail because the first is still in progress. cancel-in-progress: true cancels the older run, but for production sites, false allows the running deployment to finish .


c. Custom domains and HTTPS

A custom domain can be a subdomain (docs.example.com) or an apex domain (example.com). The DNS configuration differs. For a subdomain, a CNAME record points to <username>.github.io. For an apex domain, A records point to GitHub’s IP addresses: 185.199.108.153, 185.199.109.153, 185.199.110.153, 185.199.111.153 (and AAAA records for IPv6) .

For branch publishing, a CNAME file at the root of the source branch (or in the /docs folder if that is the source) records the custom domain. This file is served by Pages and tells GitHub which domain the site expects. For Actions publishing, no CNAME file is created or required. The custom domain is configured in the Pages settings, and the DNS records are the only external configuration .

After DNS propagation (up to 24 hours), GitHub verifies the domain and provisions a TLS certificate. The “Enforce HTTPS” checkbox becomes available. Enabling it redirects all HTTP traffic to HTTPS. If the certificate is not yet issued, the checkbox is disabled. The verification step is separate from the DNS configuration: a TXT record proves ownership and prevents domain takeover. Without it, a dangling DNS record can be claimed by another GitHub user .


Complete Example Session

# ============================================
# PART 1: ENABLE PAGES FROM A BRANCH
# ============================================

# Settings โ†’ Pages
# Source: Deploy from a branch
# Branch: main
# Folder: /docs
# Click Save
# ============================================
# PART 2: PUSH CONTENT TO THE SOURCE
# ============================================

mkdir -p docs
echo "<h1>Hello Pages</h1>" > docs/index.html
git add docs/index.html
git commit -m "Add docs index"
git push origin main
# Pages builds and serves the site at https://user.github.io/repo/
# ============================================
# PART 3: CONFIGURE CUSTOM DOMAIN (BRANCH)
# ============================================

echo "docs.example.com" > docs/CNAME
git add docs/CNAME
git commit -m "Add custom domain"
git push origin main
# The CNAME file is served by Pages.
# ============================================
# PART 4: CONFIGURE DNS FOR SUBDOMAIN
# ============================================

# At the DNS provider:
# Type: CNAME
# Name: docs
# Value: username.github.io
# ============================================
# PART 5: ENABLE HTTPS
# ============================================

# Settings โ†’ Pages โ†’ Custom domain: docs.example.com
# Wait for DNS propagation (up to 24h)
# Check "Enforce HTTPS" once the certificate is provisioned
# ============================================
# PART 6: SWITCH TO ACTIONS PUBLISHING
# ============================================

# Settings โ†’ Pages โ†’ Source: GitHub Actions
# Create .github/workflows/pages.yml
name: Deploy
on:
  push:
    branches: ['main']
permissions:
  contents: read
  pages: write
  id-token: write
jobs:
  deploy:
    runs-on: ubuntu-latest
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    steps:
      - uses: actions/checkout@v4
      - uses: actions/configure-pages@v5
      - uses: actions/upload-pages-artifact@v3
        with:
          path: './docs'
      - id: deployment
        uses: actions/deploy-pages@v4
# ============================================
# PART 7: VERIFY THE DEPLOYMENT
# ============================================

# Actions tab โ†’ Deploy workflow โ†’ completed
# Repository sidebar โ†’ Environments โ†’ github-pages
# The site URL is displayed.
# ============================================
# PART 8: APEX DOMAIN DNS
# ============================================

# Type: A
# Name: @
# Value: 185.199.108.153
# (repeat for .109, .110, .111)
# Optionally add AAAA records for IPv6

The eight parts trace both publishing paths: branch publishing with a CNAME file, and Actions publishing with a workflow. The custom domain configuration is the same in the Pages settings, but the file requirement differs.


Quick Reference

Publishing Source

SourceConfigurationTrigger
Branch (/)Settings โ†’ Pages โ†’ Deploy from branchPush to branch root
Branch (/docs)Settings โ†’ Pages โ†’ Deploy from branchPush to docs/
GitHub ActionsSettings โ†’ Pages โ†’ GitHub ActionsWorkflow deploy-pages

Custom Domain DNS

Domain TypeRecordValue
Subdomain (docs.example.com)CNAME<username>.github.io
Apex (example.com)A185.199.108.153, 185.199.109.153, 185.199.110.153, 185.199.111.153
Apex (IPv6)AAAA2606:50c0:8000::153 โ€ฆ

Limits

LimitValue
Site size1 GB
Bandwidth100 GB/month (soft)
Builds per hour10 (branch publishing)
Deployment timeout10 minutes
Source repo size1 GB (recommended)

Best Practices

โœ… Do This:

permissions:
  pages: write
  id-token: write
  • โœ… Required for actions/deploy-pages
# Verify custom domain before configuring DNS
  • โœ… Prevents domain takeover
# Use /docs for documentation alongside code
  • โœ… Keeps source and output in one place

โŒ Don’t Do This:

# Push to source branch from a GITHUB_TOKEN workflow
  • โŒ Does not trigger a Pages build
# Configure DNS before adding domain in GitHub
  • โŒ Domain takeover risk
# Use Pages for e-commerce or SaaS
  • โŒ Violates usage limits

Common Pitfalls

PitfallWhy It HappensFix
Site not updatingPush from GITHUB_TOKEN workflowUse PAT or Actions publishing
Custom domain 404CNAME missing from source folderAdd CNAME to /docs or root
HTTPS not availableCertificate not yet provisionedWait for DNS propagation; check verification
Actions deploy failsMissing pages: write permissionAdd permissions block
Site exceeds 1 GBLarge assets committedUse Git LFS or external CDN

Real-World Examples

1. Branch Publishing

Settings โ†’ Pages โ†’ Deploy from branch โ†’ main /docs

2. Actions Publishing

uses: actions/deploy-pages@v4

3. CNAME File

docs.example.com

4. Subdomain DNS

CNAME docs โ†’ username.github.io

5. Apex A Records

A @ โ†’ 185.199.108.153

6. Verify DNS

dig docs.example.com CNAME +short

7. Enforce HTTPS

Settings โ†’ Pages โ†’ Enforce HTTPS

8. Workflow Permissions

permissions:
  pages: write
  id-token: write

9. Upload Artifact

uses: actions/upload-pages-artifact@v3
with:
  path: './dist'

10. Environment URL

environment:
  name: github-pages

Visual

Publishing Paths

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  BRANCH PUBLISHING                           โ”‚
โ”‚                                              โ”‚
โ”‚  push to main/docs โ†’ Pages build โ†’ serve     โ”‚
โ”‚  CNAME file required for custom domain       โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                   โ”‚
                   โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  ACTIONS PUBLISHING                          โ”‚
โ”‚                                              โ”‚
โ”‚  workflow โ†’ build โ†’ upload โ†’ deploy          โ”‚
โ”‚  No CNAME file; domain set in settings       โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Both paths produce a site. The difference is who builds it and where the output lives.

Custom Domain Flow

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Add domain in Settings โ†’ Pages              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                   โ”‚
                   โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Configure DNS (CNAME or A records)          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                   โ”‚
                   โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  GitHub verifies + provisions TLS            โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                   โ”‚
                   โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Enable "Enforce HTTPS"                      โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

DNS without verification is a takeover risk. Verification proves ownership.


Summary

ItemValue
Branch source folders/ or /docs
Actions sourceactions/deploy-pages
Custom domain file (branch)CNAME in source folder
Subdomain DNSCNAME to <user>.github.io
Apex DNSA records to GitHub IPs
HTTPSEnforced in Pages settings
Site size limit1 GB
Bandwidth limit100 GB/month (soft)
Build limit10/hour (branch)

Key takeaways:

  • Pages serves static files only. No server-side code, no database, no dynamic rendering .
  • Two publishing paths exist. Branch publishing is simpler; Actions publishing is required for custom builds or when symbolic links are present .
  • Custom domains require DNS and verification. The CNAME file is for branch publishing only. Actions publishing ignores it .
  • HTTPS is automatic but not immediate. GitHub provisions the certificate after DNS verification. The “Enforce HTTPS” checkbox appears when it is ready.
  • Pages has limits. It is not for commerce or SaaS. The 1 GB site size and 100 GB bandwidth limits are soft but enforced .

Remember: GitHub Pages is a static host tied to a repository. The source is either a branch folder or an Actions workflow. A custom domain maps a DNS record to the Pages service, and a CNAME file (for branch publishing) tells GitHub which domain to expect. Configure the domain in GitHub first, then DNS. Verify ownership. Enable HTTPS. The site is served from GitHub’s infrastructure, but the repository remains the source of truth.

Was this helpful?


Stop using slow, ad-bloated tool sites! ๐Ÿคฎ

๐Ÿ”Ž Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
โœ… Finance (Mortgage, Interest, Inflation)
โœ… Tech (Base64, JSON, Dev Suite, IP)
โœ… Health (BMI, BMR, TDEE)
โœ… Productivity (Timer, Workspace, QR)

โšก๏ธ Fast & Private
๐Ÿ”’ No data leaves your device
๐Ÿ’Ž 100% Free

๐Ÿ”— Use it now: https://tools.kandz.me
๐Ÿ”– Bookmark itโ€”youโ€™ll need it later!