Home Blog How to Deploy a Jekyll Site to Cloudflare Pages (Step-by-Step Guide)
Tutorial

How to Deploy a Jekyll Site to Cloudflare Pages (Step-by-Step Guide)

Deploy your Jekyll site to Cloudflare Pages — the fastest global CDN for static sites. A complete guide covering setup, build config, custom domains, and redirects.

How to Deploy a Jekyll Site to Cloudflare Pages (Step-by-Step Guide)

Cloudflare Pages is one of the best places to host a Jekyll site in 2026. It is free for unlimited sites, has 300+ CDN locations worldwide, deploys automatically from GitHub or GitLab, and includes built-in DDoS protection from Cloudflare’s network — at no cost. Here is exactly how to set it up.

Why Cloudflare Pages for Jekyll

  • Free tier is genuinely unlimited — no bandwidth limits, unlimited sites, unlimited requests
  • 300+ CDN edge locations — faster global delivery than GitHub Pages or most competitors
  • Automatic HTTPS — SSL certificate provisioned automatically on every domain
  • Preview deployments — every pull request gets a unique preview URL
  • Built-in redirects and headers — via a _redirects file, no server config needed
  • Free custom domains — connect any domain you own at no charge

Prerequisites

  • A Jekyll site in a GitHub or GitLab repository
  • A Cloudflare account (free at cloudflare.com)
  • Your repository must have a Gemfile listing your gems

Step 1: Prepare your Jekyll project

Cloudflare Pages builds your site using bundle exec jekyll build. Make sure your repository has:

A Gemfile:

source "https://rubygems.org"

gem "jekyll", "~> 4.3"
gem "jekyll-feed"
gem "jekyll-seo-tag"
gem "jekyll-sitemap"
# add any other plugins you use

A Gemfile.lock committed to the repository. Run bundle install locally to generate it:

bundle install
git add Gemfile.lock
git commit -m "Add Gemfile.lock"
git push

Cloudflare Pages requires Gemfile.lock to reproduce your build environment.

Step 2: Connect your repository to Cloudflare Pages

  1. Log in to dash.cloudflare.com
  2. Select Workers & Pages from the left sidebar
  3. Click Create applicationPagesConnect to Git
  4. Authorise Cloudflare to access your GitHub or GitLab account
  5. Select your Jekyll repository and click Begin setup

Step 3: Configure the build settings

On the build configuration screen, set:

Setting Value
Framework preset Jekyll
Build command jekyll build
Build output directory _site

Cloudflare automatically detects Jekyll and pre-fills these values. Verify they are correct before continuing.

Setting environment variables

Click Environment variables (advanced) and add:

Variable Value
JEKYLL_ENV production
RUBY_VERSION 3.2.2

The JEKYLL_ENV=production variable enables production-only features in your site (analytics, optimisations). The RUBY_VERSION variable pins the Ruby version Cloudflare uses for the build.

To check which Ruby versions Cloudflare supports, refer to their build image documentation.

Step 4: Deploy

Click Save and Deploy. Cloudflare clones your repository, runs bundle exec jekyll build, and publishes the _site folder to its CDN. The first build typically takes 60–90 seconds.

You will see a live deployment URL in the format your-project.pages.dev — your site is immediately live on Cloudflare’s network.

Step 5: Add a custom domain

  1. In your Pages project, go to Custom domainsSet up a custom domain
  2. Enter your domain (e.g. jekyllhub.com)
  3. Cloudflare adds a DNS record automatically if your domain’s nameservers point to Cloudflare
  4. If your domain is registered elsewhere, you will need to add a CNAME record manually:
    • Name: @ (or www)
    • Target: your-project.pages.dev

SSL is provisioned automatically within a few minutes. Both www and root domain work.

Configuring redirects

Create a _redirects file in your Jekyll source root (not _site — Jekyll copies it automatically):

# Redirect www to non-www
https://www.jekyllhub.com/* https://jekyllhub.com/:splat 301

# Redirect old URLs
/old-post/ /new-post/ 301
/blog/category/themes/ /themes/ 301

# Custom 404
/* /404.html 404

Jekyll needs to copy this file to the build output. Add it to your _config.yml to ensure it is included:

include:
  - _redirects

Configuring custom headers

Create a _headers file in your source root for HTTP headers:

/*
  X-Frame-Options: DENY
  X-Content-Type-Options: nosniff
  Referrer-Policy: strict-origin-when-cross-origin
  Permissions-Policy: camera=(), microphone=(), geolocation=()

/assets/*
  Cache-Control: public, max-age=31536000, immutable

Add _headers to include in _config.yml the same way as _redirects.

Preview deployments

Every pull request to your repository automatically triggers a preview build at a unique URL (https://abc123.your-project.pages.dev). This lets you review changes before merging to main.

To disable preview deployments for a branch, go to your Pages project → DeploymentsBranch control and add branches to the exclusion list.

Using a GitHub Actions workflow (optional)

By default, Cloudflare Pages manages the build. If you need more control — custom Ruby gems, pre-build scripts, fetching from an API — you can trigger deployments from GitHub Actions using the Cloudflare Wrangler action:


# .github/workflows/deploy.yml
name: Deploy to Cloudflare Pages

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: ruby/setup-ruby@v1
        with:
          ruby-version: "3.2"
          bundler-cache: true

      - name: Build Jekyll site
        run: JEKYLL_ENV=production bundle exec jekyll build

      - name: Deploy to Cloudflare Pages
        uses: cloudflare/wrangler-action@v3
        with:
          apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
          command: pages deploy _site --project-name=your-project-name

Store CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID as secrets in your GitHub repository settings.

Troubleshooting common errors

Build fails with Could not find gem Your Gemfile.lock is missing or not committed. Run bundle install locally, commit Gemfile.lock, and push.

Build fails with ruby: command not found Set the RUBY_VERSION environment variable in your Pages project settings.

Custom domain shows a security warning The SSL certificate is still provisioning. Wait 5–10 minutes and refresh.

_redirects not working Ensure the file is in your Jekyll source root and listed under include: in _config.yml. Verify it was copied to _site/ in your build output.

Jekyll build succeeds but site looks broken Check your baseurl in _config.yml. If it is set to a subdirectory (e.g. /my-repo), remove or blank it for a root-domain deployment.

Cloudflare Pages is fast to set up, genuinely free at scale, and gives your Jekyll site the best global CDN coverage available. For most Jekyll projects, it is the best hosting choice in 2026.

Why Cloudflare Pages is the best Jekyll host in 2026

The hosting landscape for static sites has consolidated around a handful of strong options — Netlify, Vercel, GitHub Pages, and Cloudflare Pages. Each has genuine strengths, but Cloudflare Pages has pulled ahead for Jekyll specifically because of its combination of unlimited bandwidth on the free tier, the largest global CDN footprint of any provider, and zero cold start latency (every request is served from cache at an edge node near the visitor).

Netlify and Vercel both impose bandwidth limits on free tiers and charge for overages — Netlify at 100GB and Vercel at similar levels. For a Jekyll site that is growing its audience, hitting a bandwidth limit in the middle of a viral post is a painful and expensive experience. Cloudflare Pages has no bandwidth limit on any tier, making it genuinely free for sites of any traffic level as long as you do not need advanced features like custom redirects at scale or Workers integrations.

GitHub Pages is free and integrated with GitHub Actions, but it has important limitations: it does not support the full Jekyll plugin ecosystem (only the approved list), has slower build times, and the CDN coverage is less global than Cloudflare’s network of 300+ edge locations. For sites that need full plugin support or maximum performance, Cloudflare Pages is the stronger choice.

Configuring Cloudflare DNS for a custom domain

Adding a custom domain to a Cloudflare Pages site is uniquely smooth when your domain is already on Cloudflare DNS — which is common since Cloudflare offers free DNS with excellent performance. In the Cloudflare Pages dashboard, add your custom domain, and Cloudflare automatically creates the required DNS records and provisions an SSL certificate within minutes.

If your domain is registered elsewhere, you have two options: transfer the domain to Cloudflare Registrar (which offers registration at cost price with no markup) or point your nameservers to Cloudflare while keeping registration where it is. Both options give you full DNS management through Cloudflare’s dashboard and the integration with Pages custom domains.

One important detail: enable “Full (Strict)” SSL/TLS mode in Cloudflare’s settings after adding your custom domain. The default “Flexible” mode encrypts the browser-to-Cloudflare connection but not the Cloudflare-to-origin connection — for a static site on Pages this does not apply since there is no origin to connect to, but confirming the setting is in Full Strict mode ensures the security configuration is unambiguous.

Branch deployments for staging and preview

Cloudflare Pages deploys every branch in your repository as a separate preview URL — your main branch deploys to yoursite.pages.dev or your custom domain, while other branches get unique preview URLs like feature-branch.yoursite.pages.dev. This is valuable for reviewing content changes before they go live, testing theme modifications, or showing work to a client for approval.

Connect your staging branch (typically develop or staging) as a production alias in the Cloudflare Pages dashboard, pointing it to a staging subdomain like staging.yourdomain.com. This gives you a permanent staging environment that mirrors your production configuration, making it straightforward to test deployment changes without touching the live site.

For larger content operations where editors need to preview their work before publishing, configure your editorial workflow so content edits happen on a content branch that automatically deploys to a preview URL. Editors see the preview URL, approve, and the PR merge triggers a production deployment. This workflow keeps non-technical content editing clean and safe, with no risk of accidentally publishing to production before a review.

Performance tuning with Cloudflare caching rules

Cloudflare’s caching configuration provides performance control beyond what your Jekyll build output achieves alone. Custom caching rules in the Cloudflare dashboard let you set aggressive cache TTLs for static assets (CSS, JavaScript, images) and appropriate shorter TTLs for HTML pages.

Set cache TTL to one year (31,536,000 seconds) for versioned assets — files whose URLs include a hash or version number that changes when the content changes. Jekyll’s built-in asset pipeline does not automatically version files, but you can implement simple versioning by appending a version query string to asset URLs in your _config.yml and bumping it when assets change. Cloudflare caches query string variations separately, so main.css?v=2 and main.css?v=3 are cached as distinct resources.

HTML pages should have shorter cache TTLs — typically one hour to one day — so that content updates propagate to visitors promptly after deployment. Cloudflare Pages automatically purges the cache for changed files after each deployment, so the TTL primarily affects how long old content persists for visitors who happen to be on a Cloudflare edge that has not yet received the cache purge notification.

With these optimisations in place, a Jekyll site on Cloudflare Pages with a well-built theme consistently achieves Time to First Byte under 100 milliseconds globally — a performance level that few self-hosted or CMS-based sites can match. For readers from every continent, your site loads at effectively the same speed, because every visitor is served from an edge location within milliseconds of their position.

Share LinkedIn