GitHub Pages

Web HostingStatic SitesJekyllGitHub ActionsCI/CDOpen SourceDocumentation

Web Hosting Platform

GitHub Pages

Overview

GitHub Pages is a static site hosting service that can be hosted directly from GitHub repositories. With Jekyll integration providing automatic Markdown to HTML conversion and blogging capabilities, it's widely used for open-source project documentation and portfolio sites. It maintains consistent demand through tight GitHub integration.

Details

Launched in 2008, GitHub Pages is one of GitHub's signature features beloved by many developers. Particularly popular for open-source project documentation, personal portfolio sites, and technical blogs. Through integration with Jekyll (Ruby-based static site generator), it automatically converts Markdown files to HTML and enables utilization of rich theme and plugin ecosystems. Starting in 2025, it transitioned to GitHub Actions-based build processes, enabling more flexible customization and use of newer Jekyll versions.

Advantages and Disadvantages

Advantages

  • Completely Free: Unlimited usage for public repositories
  • GitHub Integration: Unified repository management and hosting
  • Jekyll Integration: Automatic HTML generation from Markdown
  • Custom Domain Support: Configuration of custom domains
  • HTTPS Support: Automatic SSL certificate issuance
  • GitHub Actions: Advanced build process customization
  • Rich Themes: Numerous beautiful Jekyll themes available

Disadvantages

  • Static Sites Only: No server-side processing possible
  • Build Limitations: 10GB monthly bandwidth limit
  • Jekyll Dependency: GitHub Actions required for other SSGs
  • Performance: May be slower compared to dedicated CDNs
  • Feature Limitations: Constraints on advanced features and plugins

Reference Pages

Code Examples

Basic Setup and Project Configuration

# Repository creation with GitHub CLI
gh repo create my-website --public --clone
cd my-website

# Jekyll site initialization
jekyll new . --force
bundle install

# Enable GitHub Pages (Settings > Pages)
# Source: Deploy from a branch or GitHub Actions

# Initial commit
git add .
git commit -m "Initial Jekyll site"
git push origin main
# _config.yml - Jekyll basic configuration
title: My Awesome Website
description: A great site built with Jekyll and hosted on GitHub Pages
baseurl: "" # Repository name (e.g., "/my-website")
url: "https://username.github.io"

# GitHub Pages configuration
github_username: your-username
repository: your-username/my-website

# Jekyll configuration
markdown: kramdown
highlighter: rouge
theme: minima

# Plugin configuration
plugins:
  - jekyll-feed
  - jekyll-sitemap
  - jekyll-seo-tag
  - jekyll-redirect-from

# Excluded files
exclude:
  - Gemfile
  - Gemfile.lock
  - node_modules
  - vendor/bundle/
  - vendor/cache/
  - vendor/gems/
  - vendor/ruby/

Static Site Deployment

# .github/workflows/jekyll.yml - Jekyll GitHub Actions
name: Build and deploy Jekyll site to GitHub Pages

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

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

concurrency:
  group: "pages"
  cancel-in-progress: false

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        
      - name: Setup Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: '3.1'
          bundler-cache: true
          cache-version: 0
          
      - name: Setup Pages
        id: pages
        uses: actions/configure-pages@v4
        
      - name: Build with Jekyll
        run: bundle exec jekyll build --baseurl "${{ steps.pages.outputs.base_path }}"
        env:
          JEKYLL_ENV: production
          
      - name: Upload artifact
        uses: actions/upload-pages-artifact@v3
        with:
          path: ./_site

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

Framework Integration (Next.js, React, Vue)

# .github/workflows/nextjs.yml - Next.js GitHub Actions
name: Deploy Next.js site to Pages

on:
  push:
    branches: ["main"]
  workflow_dispatch:

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

concurrency:
  group: "pages"
  cancel-in-progress: false

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        
      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: "20"
          cache: npm
          
      - name: Setup Pages
        uses: actions/configure-pages@v4
        with:
          static_site_generator: next
          
      - name: Restore cache
        uses: actions/cache@v4
        with:
          path: |
            .next/cache
          key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}
          
      - name: Install dependencies
        run: npm ci
        
      - name: Build with Next.js
        run: npm run build
        
      - name: Upload artifact
        uses: actions/upload-pages-artifact@v3
        with:
          path: ./out

  deploy:
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    runs-on: ubuntu-latest
    needs: build
    steps:
      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v4
// next.config.js - GitHub Pages support
/** @type {import('next').NextConfig} */
const nextConfig = {
  output: 'export',
  trailingSlash: true,
  images: {
    unoptimized: true
  },
  basePath: process.env.NODE_ENV === 'production' ? '/repository-name' : '',
  assetPrefix: process.env.NODE_ENV === 'production' ? '/repository-name/' : '',
};

module.exports = nextConfig;

Custom Domains and SSL

# Custom domain configuration
echo "example.com" > CNAME
git add CNAME
git commit -m "Add custom domain"
git push origin main

# DNS configuration (CNAME record)
# example.com -> username.github.io
# www.example.com -> username.github.io
# _config.yml - Custom domain configuration
url: "https://example.com"
baseurl: ""

# Force HTTPS
enforce_ssl: true

# Custom domain plugin
plugins:
  - jekyll-redirect-from

# Redirect configuration
redirect_from:
  - /old-page/
  - /another-old-page/
<!-- 404.html - Custom 404 page -->
---
layout: default
permalink: /404.html
---

<div class="error-page">
  <h1>404 - Page Not Found</h1>
  <p>The page you're looking for doesn't exist.</p>
  <a href="{{ site.baseurl }}/" class="btn">Go Home</a>
</div>

<script>
  // Client-side routing for SPA
  (function() {
    var path = window.location.pathname;
    var base = '{{ site.baseurl }}';
    
    // Routing handling for GitHub Pages
    if (path !== base + '/404.html') {
      window.location.replace(base + '/#' + path);
    }
  })();
</script>

Serverless Functions and APIs

# .github/workflows/api-simulation.yml - Pseudo API creation
name: Generate API Data

on:
  push:
    branches: [ main ]
  schedule:
    - cron: '0 0 * * *'  # Daily execution

jobs:
  generate-data:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '18'
          
      - name: Generate API data
        run: |
          mkdir -p api
          
          # Generate user data
          cat > api/users.json << 'EOF'
          {
            "users": [
              {
                "id": 1,
                "name": "John Doe",
                "email": "[email protected]",
                "created_at": "${{ steps.date.outputs.date }}"
              }
            ],
            "total": 1,
            "generated_at": "${{ steps.date.outputs.date }}"
          }
          EOF
          
          # Generate statistics data
          node -e "
            const stats = {
              page_views: Math.floor(Math.random() * 10000),
              unique_visitors: Math.floor(Math.random() * 1000),
              last_updated: new Date().toISOString()
            };
            require('fs').writeFileSync('api/stats.json', JSON.stringify(stats, null, 2));
          "
      
      - name: Commit and push
        run: |
          git config --local user.email "[email protected]"
          git config --local user.name "GitHub Action"
          git add api/
          git diff --staged --quiet || git commit -m "Update API data"
          git push
// _includes/api-client.js - Static API client
class StaticAPI {
  constructor(baseUrl) {
    this.baseUrl = baseUrl || '';
  }
  
  async get(endpoint) {
    try {
      const response = await fetch(`${this.baseUrl}/api/${endpoint}.json`);
      if (!response.ok) throw new Error(`HTTP ${response.status}`);
      return await response.json();
    } catch (error) {
      console.error('API Error:', error);
      return null;
    }
  }
  
  // Search functionality (static database)
  async search(query, type = 'posts') {
    const data = await this.get(type);
    if (!data) return [];
    
    const searchTerm = query.toLowerCase();
    return data.filter(item => 
      item.title.toLowerCase().includes(searchTerm) ||
      item.content.toLowerCase().includes(searchTerm)
    );
  }
}

// Usage example
const api = new StaticAPI('{{ site.baseurl }}');

// Data fetching on page load
document.addEventListener('DOMContentLoaded', async () => {
  const users = await api.get('users');
  const stats = await api.get('stats');
  
  // Display data
  if (users) {
    document.getElementById('user-count').textContent = users.total;
  }
  
  if (stats) {
    document.getElementById('page-views').textContent = stats.page_views;
  }
});

CI/CD and Production Optimization

# .github/workflows/optimization.yml - Optimization workflow
name: Build and Optimize

on:
  push:
    branches: [ main ]

jobs:
  build-and-optimize:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '18'
          cache: 'npm'
          
      - name: Install dependencies
        run: npm ci
        
      - name: Build Jekyll site
        run: |
          bundle install
          bundle exec jekyll build
          
      - name: Optimize images
        run: |
          npm install -g imagemin-cli imagemin-webp imagemin-mozjpeg imagemin-pngquant
          imagemin "_site/**/*.{jpg,jpeg,png}" --out-dir="_site" --plugin=mozjpeg --plugin=pngquant
          
      - name: Minify HTML/CSS/JS
        run: |
          npm install -g html-minifier clean-css-cli uglify-js
          
          # HTML minification
          find _site -name "*.html" -exec html-minifier --collapse-whitespace --remove-comments {} \;
          
          # CSS minification
          find _site -name "*.css" -exec cleancss -o {} {} \;
          
          # JS minification
          find _site -name "*.js" -exec uglifyjs {} -o {} \;
          
      - name: Generate PWA files
        run: |
          cat > _site/manifest.json << 'EOF'
          {
            "name": "${{ github.repository }}",
            "short_name": "Site",
            "start_url": "{{ site.baseurl }}/",
            "display": "standalone",
            "background_color": "#ffffff",
            "theme_color": "#000000"
          }
          EOF
          
      - name: Upload artifact
        uses: actions/upload-pages-artifact@v3
        with:
          path: ./_site
# Gemfile - Jekyll plugin configuration
source "https://rubygems.org"

gem "jekyll", "~> 4.3.0"
gem "github-pages", group: :jekyll_plugins

group :jekyll_plugins do
  gem "jekyll-feed"
  gem "jekyll-sitemap"
  gem "jekyll-seo-tag"
  gem "jekyll-redirect-from"
  gem "jekyll-paginate"
  gem "jekyll-gist"
  gem "jekyll-github-metadata"
  gem "jekyll-include-cache"
  gem "jekyll-compress-images"
  gem "jekyll-minifier"
end

# Performance monitoring
gem "jekyll-analytics"

# Development
group :development do
  gem "webrick"
end
# Development and operations command collection
# Local development
bundle exec jekyll serve --livereload --port 4000

# Production build
JEKYLL_ENV=production bundle exec jekyll build

# Update dependencies
bundle update

# GitHub Pages compatibility check
github-pages health-check

# Site analysis
bundle exec jekyll doctor

# Preview (GitHub Pages environment)
act -W .github/workflows/pages.yml