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.
Prerequisites
- 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
Step 1: Configure Jekyll for Production
Ensure _config.yml sets the correct URL:
# _config.yml
url: "https://yourdomain.com"
baseurl: ""
plugins:
- jekyll-feed
- jekyll-sitemap
- jekyll-seo-tag
Add a Gemfile:
source "https://rubygems.org"
gem "jekyll", "~> 4.3"
group :jekyll_plugins do
gem "jekyll-feed"
gem "jekyll-sitemap"
gem "jekyll-seo-tag"
end
Step 2: Create the GitHub Actions Workflow
Create .github/workflows/deploy.yml:
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
This workflow uses the official upload-pages-artifact + deploy-pages pattern, the recommended approach since 2024 that avoids pushing to a gh-pages branch.
Step 3: Enable GitHub Pages in Repository Settings
- Go to Settings > Pages
- Under Source, select GitHub Actions (not a branch)
- Save
Push to main, the workflow runs automatically and your site deploys within ~2 minutes.
Step 4: Configure a Custom Domain
4a. Create the CNAME File
Add a CNAME file to the root of your repo (no extension, plain text):
yourdomain.com
Commit and push it. GitHub Pages reads this file to know which custom domain to serve.
4b. Set the Domain in GitHub Pages Settings
In Settings > Pages > Custom domain, enter yourdomain.com and save.
Step 5: Configure DNS (Cloudflare)
In the Cloudflare dashboard for your domain, add these records:
| Type | Name | Content | Proxy |
|---|---|---|---|
| A | @ | 185.199.108.153 | ✔ On |
| A | @ | 185.199.109.153 | ✔ On |
| A | @ | 185.199.110.153 | ✔ On |
| A | @ | 185.199.111.153 | ✔ On |
| CNAME | www | yourusername.github.io | ✔ On |
These are GitHub's current (2026) IP addresses. The old addresses from 2016 (
192.30.252.x) are deprecated.
Step 6: Enable HTTPS
Once DNS propagates (usually minutes with Cloudflare):
- 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)
Step 7: Cloudflare Performance Optimizations (Free Tier)
In the Cloudflare dashboard, enable:
- Speed > Optimization > Auto Minify: HTML, CSS, JS
- Caching > Configuration > Browser Cache TTL: 4 hours
- Speed > Optimization > Brotli: on
Verify Everything Works
# Check DNS resolves to GitHub Pages IPs
dig yourdomain.com A +short
# Verify HTTPS and response headers
curl -I https://yourdomain.com
# Look for: strict-transport-security, cf-ray (Cloudflare edge hit)
The Result
| Feature | Status |
|---|---|
| Unlimited plugins & custom gems | ✔ via GitHub Actions |
| Custom domain | ✔ CNAME file + DNS |
| HTTPS / TLS | ✔ Let's Encrypt via GitHub |
| Global CDN | ✔ Cloudflare |
| DDoS protection | ✔ Cloudflare |
| Deploy on push | ✔ Auto via Actions |
| Cost | $0 |
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.
Top comments (0)