Tools: Deploy Jekyll to GitHub Pages in 2026: GitHub Actions, Custom Domain & Cloudflare

Tools: Deploy Jekyll to GitHub Pages in 2026: GitHub Actions, Custom Domain & Cloudflare

Prerequisites

Step 1: Configure Jekyll for Production

Step 2: Create the GitHub Actions Workflow

Step 3: Enable GitHub Pages in Repository Settings

Step 4: Configure a Custom Domain

4a. Create the CNAME File

4b. Set the Domain in GitHub Pages Settings

Step 5: Configure DNS (Cloudflare)

Step 6: Enable HTTPS

Step 7: Cloudflare Performance Optimizations (Free Tier)

Verify Everything Works

The Result GitHub Pages remains the best free static site host for developers in 2026. The catch: the built-in Jekyll builder is limited to a small set of allowed gems. The solution is a GitHub Actions workflow that builds Jekyll with full plugin support and deploys the output to GitHub Pages. This guide walks through the complete setup, from a blank repo to a custom domain served over HTTPS via Cloudflare. Ensure _config.yml sets the correct URL: Create .github/workflows/deploy.yml: This workflow uses the official upload-pages-artifact + deploy-pages pattern, the recommended approach since 2024 that avoids pushing to a gh-pages branch. Push to main, the workflow runs automatically and your site deploys within ~2 minutes. Add a CNAME file to the root of your repo (no extension, plain text): Commit and push it. GitHub Pages reads this file to know which custom domain to serve. In Settings > Pages > Custom domain, enter yourdomain.com and save. In the Cloudflare dashboard for your domain, add these records: These are GitHub's current (2026) IP addresses. The old addresses from 2016 (192.30.252.x) are deprecated. Once DNS propagates (usually minutes with Cloudflare): In the Cloudflare dashboard, enable: Your Jekyll site is now fully production-grade: CI/CD deploys on every push, TLS is enforced, and Cloudflare caches assets at 300+ edge nodes worldwide. Templates let you quickly answer FAQs or store snippets for re-use. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse

Command

Copy

# _config.yml url: "https://yourdomain.com" baseurl: "" plugins: - jekyll-feed - jekyll-sitemap - jekyll-seo-tag # _config.yml url: "https://yourdomain.com" baseurl: "" plugins: - jekyll-feed - jekyll-sitemap - jekyll-seo-tag # _config.yml url: "https://yourdomain.com" baseurl: "" plugins: - jekyll-feed - jekyll-sitemap - jekyll-seo-tag source "https://rubygems.org" gem "jekyll", "~> 4.3" group :jekyll_plugins do gem "jekyll-feed" gem "jekyll-sitemap" gem "jekyll-seo-tag" end source "https://rubygems.org" gem "jekyll", "~> 4.3" group :jekyll_plugins do gem "jekyll-feed" gem "jekyll-sitemap" gem "jekyll-seo-tag" end source "https://rubygems.org" gem "jekyll", "~> 4.3" group :jekyll_plugins do gem "jekyll-feed" gem "jekyll-sitemap" gem "jekyll-seo-tag" end name: Deploy Jekyll to GitHub 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: - uses: actions/checkout@v4 - uses: ruby/setup-ruby@v1 with: ruby-version: '3.3' bundler-cache: true # Caches gems for faster builds - name: Configure GitHub Pages uses: actions/configure-pages@v5 - name: Build with Jekyll run: bundle exec jekyll build --destination ./_site env: JEKYLL_ENV: production - name: Upload artifact uses: actions/upload-pages-artifact@v3 deploy: needs: build runs-on: ubuntu-latest environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} steps: - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4 name: Deploy Jekyll to GitHub 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: - uses: actions/checkout@v4 - uses: ruby/setup-ruby@v1 with: ruby-version: '3.3' bundler-cache: true # Caches gems for faster builds - name: Configure GitHub Pages uses: actions/configure-pages@v5 - name: Build with Jekyll run: bundle exec jekyll build --destination ./_site env: JEKYLL_ENV: production - name: Upload artifact uses: actions/upload-pages-artifact@v3 deploy: needs: build runs-on: ubuntu-latest environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} steps: - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4 name: Deploy Jekyll to GitHub 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: - uses: actions/checkout@v4 - uses: ruby/setup-ruby@v1 with: ruby-version: '3.3' bundler-cache: true # Caches gems for faster builds - name: Configure GitHub Pages uses: actions/configure-pages@v5 - name: Build with Jekyll run: bundle exec jekyll build --destination ./_site env: JEKYLL_ENV: production - name: Upload artifact uses: actions/upload-pages-artifact@v3 deploy: needs: build runs-on: ubuntu-latest environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} steps: - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4 yourdomain.com yourdomain.com yourdomain.com # Check DNS resolves to GitHub Pages IPs dig yourdomain.com A +short # Verify HTTPS and response headers -weight: 500;">curl -I https://yourdomain.com # Look for: strict-transport-security, cf-ray (Cloudflare edge hit) # Check DNS resolves to GitHub Pages IPs dig yourdomain.com A +short # Verify HTTPS and response headers -weight: 500;">curl -I https://yourdomain.com # Look for: strict-transport-security, cf-ray (Cloudflare edge hit) # Check DNS resolves to GitHub Pages IPs dig yourdomain.com A +short # Verify HTTPS and response headers -weight: 500;">curl -I https://yourdomain.com # Look for: strict-transport-security, cf-ray (Cloudflare edge hit) - A GitHub account with a repository for your Jekyll site - A custom domain (optional but recommended) - A Cloudflare account (free tier is sufficient) - Ruby ≥ 3.1 and Jekyll ≥ 4.3 installed locally for testing - Go to Settings > Pages - Under Source, select GitHub Actions (not a branch) - In Settings > Pages, check Enforce HTTPS ✔ - In Cloudflare SSL/TLS, set mode to Full (not Full Strict) - Add a Cloudflare Redirect Rule: www.yourdomain.com → https://yourdomain.com (301) - Speed > Optimization > Auto Minify: HTML, CSS, JS - Caching > Configuration > Browser Cache TTL: 4 hours - Speed > Optimization > Brotli: on