GitHub Pages vs Cloudflare - Which is Better?

GitHub Pages and Cloudflare Pages are two of the most popular ways to host a static website for free. Both deploy straight from a Git repository, serve your site over free HTTPS, and cost nothing for a typical blog, portfolio or documentation site.
But GitHub Pages is a publishing feature attached to your repository, while Cloudflare Pages runs on an edge network spread across more than 330 cities and adds redirects, custom headers, preview deployments and serverless functions on top. So which one should you host your site on? Let's find out.
Cloudflare Pages is the better host for most static sites. It has the larger edge network, no bandwidth cap for static files, and gives you preview deployments, real 301 redirects, custom headers and Brotli compression, none of which GitHub Pages offers. GitHub Pages is still a fine pick for an open-source project's docs or a personal Jekyll site in a public repository, where you may not want a second account just for hosting. If you're starting fresh on Cloudflare, use Workers with static assets, which Cloudflare now recommends over Pages for new projects.
In this article, we will compare GitHub Pages and Cloudflare Pages on global server response time, browser caching, build limits, redirects and headers, custom domains, and pricing. I'll also cover the popular middle path of putting Cloudflare's proxy in front of a GitHub Pages site, because Cloudflare doesn't cache your HTML in that setup by default.
Since April 2025, Cloudflare has recommended Workers with static assets for new projects instead of Pages. For serving a static site, the two work the same way: free and unlimited static requests, the same file limits, and the same _headers and _redirects files. Their build and preview features differ, so I'll stick to Pages for those parts.
How We Compared Them
For this comparison, we didn't run a fresh benchmark. Every limit, price and feature below comes from GitHub's and Cloudflare's own documentation, and the caching and compression details come from the response headers both platforms send.
Here's what we looked at:
- Documentation: GitHub Docs for Pages limits, HTTPS and custom domains, and Cloudflare's docs for Pages limits, caching, headers, redirects and Functions pricing.
- Network size: Cloudflare's network page and Fastly's network map, since GitHub Pages is served through Fastly.
- Response headers:
cache-control,content-encodingandalt-svcon public sites hosted ongithub.ioandpages.dev.
What we didn't test is TTFB from multiple locations or Core Web Vitals. Network size tells you which platform is likely to be closer to your visitors, but only a test from those locations proves it. I'll show you how to run that test on your own site in the server response time section.
GitHub Pages Overview
GitHub Pages turns a repository into a website. You get one user or organization site per account (username.github.io) and one project site per repository, served at username.github.io/repository-name/ unless you add a custom domain.
There are two ways to publish:
- From a branch: GitHub builds the root or
/docsfolder with Jekyll by default. An empty.nojekyllfile turns that build off if your site is plain HTML. - With GitHub Actions: any other static site generator (Hugo, Astro, Eleventy, Docusaurus) builds in a workflow. GitHub's docs now call this the recommended approach.
Pages is free on GitHub Free, but the repository has to be public. Publishing from a private repository needs a paid plan such as GitHub Pro or Team, and restricting who can view the site needs GitHub Enterprise Cloud.
GitHub's usage limits are modest but fine for most personal sites:
- Published site size: 1 GB, which is also the recommended size for the source repository.
- Bandwidth: a soft limit of 100 GB per month.
- Builds: a soft limit of 10 per hour, which doesn't apply when you deploy with a custom GitHub Actions workflow.
- Deployment timeout: 10 minutes.
The same page says GitHub Pages isn't meant to be free hosting for an online business, an ecommerce store or a commercial SaaS product. A portfolio, a blog or your project's documentation is fine; a store is not.
Behind the scenes, GitHub Pages is served through Fastly's CDN, which caches successful responses at its edge. GitHub described this setup in a 2015 engineering post (updated in 2021), and Fastly lists Pages on its GitHub customer page.
What you don't get is server-side code, custom HTTP headers, server-side redirects, or preview deployments for pull requests.
Cloudflare Pages Overview
Cloudflare Pages is Cloudflare's platform for static sites and full-stack apps. You can connect a GitHub or GitLab repository and let Cloudflare build it on every push, or build anywhere you like and push the output with the Wrangler CLI (Direct Upload). Direct Upload doesn't care whether your source lives in a public repository, a private one, or no repository at all.
The free plan limits, from Cloudflare's Pages limits page:
- Bandwidth and requests: free and unlimited for static assets.
- Builds: 500 per month, one at a time, with a 20-minute timeout.
- Files: 20,000 per site, up to 25 MiB each.
- Custom domains: 100 per project.
- Projects: 100 per account.
- Preview deployments: unlimited.
Pages Functions (server-side code that runs on Cloudflare Workers) are billed as Workers requests. The free plan shares 100,000 requests per day between Functions and Workers, and the Workers Paid plan starts at $5 per month with 10 million requests included.
The biggest difference is the network: Cloudflare lists data centers in more than 330 cities, and it says about 95% of the world's internet-connected population is within 50 ms of one.
We've compared Cloudflare Pages with Netlify and Vercel before, and the Vite + React app from my Vite performance case study is deployed on Cloudflare Pages too. As for the Workers recommendation, Cloudflare's April 2025 announcement says Pages will keep being supported, but new features and optimizations now go to Workers.
Worldwide Server Response Time
For a static site, server response time (TTFB) mostly comes down to two things: how far the visitor is from the nearest edge server, and whether that server already has the file in its cache. There's no database or backend code to wait for, so the network does most of the work.
GitHub Pages answers from a Fastly POP (point of presence). If that POP has a cached copy, the visitor gets it right away. If it doesn't (a cache miss), Fastly first fetches the file from GitHub's servers. Fastly doesn't publish a POP count; its network map describes a strategy of fewer but more powerful POPs, with 655 Tbps of connected capacity.
Cloudflare Pages answers from the Cloudflare data center closest to the visitor. Pages assets are served through Tiered Cache and stay cached in each data center for up to a week, or until your next deployment, though Cloudflare notes they can be evicted at any time.
Here's a quick illustration of how a request reaches each platform.

Both paths are short when the file is already cached at the edge. The difference is how many edge locations each network has near your visitors, and where the request goes when there's a miss.
On paper, Cloudflare has the shorter path for more of the world. Where both networks have an edge server close to the visitor, the difference should be small. The gap is most likely to show up in countries where Cloudflare has a nearby city and Fastly doesn't. If you want to compare the two CDNs themselves, our best CDN provider ranking covers both Cloudflare and Fastly.
One Redditor who moved a Hugo site from GitHub Pages (deployed through GitHub Actions) to Cloudflare said latency from their location averaged about 25 ms on GitHub and about 12 ms on Cloudflare. That's roughly half, but it's one site measured from one place. A visitor in another country could see a bigger gap or none at all, depending on which edge servers sit near them.
A site's traffic also affects how often visitors get a cached copy. An edge server only caches a file after someone requests it there, so a low-traffic site gets more cache misses than a popular one on both platforms. The first visitor from a quiet region waits for the trip to the origin.
Since TTFB delays everything that comes after it (I've covered this in our guide on reducing server response time), this is the part I'd test before deciding.
How to Test It on Your Own Site
- Deploy the same build to both platforms, for example
username.github.io/my-site/andmy-site.pages.dev. - Run the SpeedVitals TTFB Test on the same page on each URL. It measures TTFB from 40 locations in one go.
- Run each URL at least twice and compare the later runs. The first run warms the edge caches, so it mostly measures cache misses.
- Compare the regional averages and the Cache Status column for the countries your visitors come from.
If most of your traffic comes from one region, give that region's numbers more weight than the global average.
Winner: Cloudflare Pages, based on network size. It only becomes a measured result once you've tested from your own audience's regions.
Browser Caching, Compression and HTTP/3
This is where GitHub Pages falls behind, and the CDN has nothing to do with it.
GitHub Pages sends cache-control: max-age=600 with its responses (HTML, CSS, JavaScript and images alike), and you can't change it. Browsers can reuse a file for 10 minutes, after which they have to check with the server again. That's reasonable for HTML, but a fingerprinted file like main.4f9c2a.js never changes, so the browser could safely keep it for a year. On GitHub Pages, a returning visitor has to revalidate it every 10 minutes.
Cloudflare Pages takes the opposite default: public, max-age=0, must-revalidate, with an ETag on every response. The browser checks with Cloudflare on every request (a quick 304 when nothing changed), so a new deployment shows up immediately. And unlike GitHub Pages, you can override it with a _headers file in your build output:
/assets/*
Cache-Control: public, max-age=31536000, immutable
Only use this for files with a content hash in their name. Vite and Docusaurus put them in /assets/, and Astro uses /_astro/. Never set a long cache on HTML, or visitors will keep seeing the old page after you deploy.
Compression is the second difference. Cloudflare's docs say Pages serves Brotli where the browser supports it, while the GitHub Pages sites we checked returned gzip even when the request offered Brotli. Brotli had the best compression ratio of the three algorithms in our ZSTD vs Brotli vs GZip comparison, so this is a small but free win for Cloudflare. You can check any URL with our Brotli Test.
Similarly, pages.dev responses advertise HTTP/3 through an alt-svc header, while the GitHub Pages responses we checked didn't. Our HTTP/3 Checker shows this for any site.
Winner: Cloudflare Pages.
Impact on Core Web Vitals
Of the three Core Web Vitals, hosting mostly affects Largest Contentful Paint (LCP). The browser can't render anything until the HTML arrives, so a lower TTFB moves LCP earlier on the first visit, and longer browser caching helps on repeat visits. Google rates an LCP of 2.5 seconds or less as good at the 75th percentile, according to web.dev.
INP and CLS come from your front-end code (JavaScript, fonts, images without dimensions), and switching hosts won't change them. If a static site fails Core Web Vitals on GitHub Pages, check its field data and find out whether the LCP image, render-blocking CSS or JavaScript is the bigger problem before you blame the host.
Feature and Developer Experience Comparison
Builds and Static Site Generators
GitHub Pages builds Jekyll for you with zero setup. For any other generator, you write (or copy) a GitHub Actions workflow, and the runners are free for public repositories. It's a bit more work up front, but you control the exact Node, Hugo or Ruby version.
Cloudflare Pages has framework presets for the popular generators: you pick one, confirm the build command and output folder, and it builds on every push. The free plan allows 500 builds per month and one build at a time, so a team pushing many commits a day can end up waiting in a queue. If that becomes a problem, you can build in GitHub Actions and deploy the output with wrangler pages deploy.
Preview Deployments
Every branch and pull request on Cloudflare Pages gets its own preview URL (plus a branch-name.project.pages.dev alias) that updates with each commit. Previews are unlimited and public by default, and you can restrict them to your Cloudflare account with an access policy.
GitHub Pages has one live site per repository. The official actions/deploy-pages action has a preview option for pull requests, but GitHub marks it as alpha and not available to the public. Until that changes, you'd need a third-party service for previews.
Redirects and Custom Headers
For most site owners, this is the biggest gap between the two. GitHub Pages doesn't read _redirects, _headers or .htaccess files. The usual workaround is the jekyll-redirect-from plugin, which creates an HTML page that redirects in the browser instead of returning a real 301 status code. There's also no way to send security headers like Content-Security-Policy or Strict-Transport-Security, apart from the few that work as a <meta> tag.
Cloudflare Pages reads both files from your build output. _redirects supports 2,000 static and 100 dynamic rules with 301, 302, 303, 307 and 308 status codes, and _headers supports up to 100 rules:
/old-post/ /blog/new-post/ 301
/docs/* /guide/:splat 301
The default status code is 302 (temporary), so add 301 to every rule that moves a page permanently. Then confirm the status codes with our Redirect Tracer.
Custom Domains and HTTPS
Both platforms give you a free certificate. GitHub Pages issues one from Let's Encrypt once your DNS points to GitHub, either with A and AAAA records on the apex domain or a CNAME for www (GitHub recommends using www). If your domain has CAA records, one of them must allow letsencrypt.org.
Cloudflare Pages is just as easy for a subdomain: add it in the dashboard, then point a CNAME at project.pages.dev. An apex domain like example.com is different, because it has to be a zone on your Cloudflare account, which means moving your nameservers to Cloudflare. If you'd rather keep DNS at your registrar and serve the bare domain, GitHub Pages is simpler.
Server-Side Code
GitHub Pages serves files and nothing else. Cloudflare Pages Functions let you add server-side code, such as a contact form handler or a redirect based on the visitor's country, without leaving the platform. The free plan includes 100,000 requests per day across Functions and Workers, and requests for static files don't count toward it.
Analytics
Cloudflare Web Analytics is free, and you can turn it on for a Pages project from the Metrics tab without touching your code. GitHub Pages has nothing built in, so you add your own analytics script. For Core Web Vitals from real visitors, you'll need a RUM tool such as SpeedVitals RUM on either platform.
Limits and Pricing
The table below lists the free plan limits from each platform's documentation:
| GitHub Pages | Cloudflare Pages (Free) | |
|---|---|---|
| Price | Free (public repositories on GitHub Free) | Free |
| Site size | 1 GB | 20,000 files, 25 MiB per file |
| Bandwidth | 100 GB/month (soft limit) | Unlimited for static files |
| Builds | 10 per hour (soft limit, branch builds) | 500 per month, 1 at a time |
| Timeout | 10 minutes per deployment | 20 minutes per build |
| Sites | 1 user site + 1 per repository | 100 projects per account |
| Server-side code | None | Functions, 100,000 requests/day |
| Commercial use | Not for online businesses, stores or SaaS | No such restriction in the Pages docs |
GitHub's limits are soft: if a site goes well past them, GitHub says it may stop serving the site or send the owner an email, but it doesn't give a hard cutoff.
The one place where GitHub Pages is more generous is file count. A large documentation site with tens of thousands of HTML files can fit under GitHub's 1 GB limit but go over Cloudflare's 20,000-file free limit (paid plans allow up to 100,000).
Putting Cloudflare in Front of GitHub Pages
A lot of site owners keep their site on GitHub Pages and route their domain through Cloudflare's proxy (the orange cloud in Cloudflare's DNS settings), expecting Cloudflare's network to speed it up. It can, but not with the default settings.
According to its docs, Cloudflare doesn't cache HTML by default. So every page request goes from the visitor to Cloudflare, then to Fastly, and on a Fastly cache miss to GitHub's servers. Cloudflare becomes an extra hop in front of Fastly. Static files such as CSS, JavaScript and images do get cached at Cloudflare's edge.

To make the proxy worth it for speed, you need a Cache Rule that makes HTML eligible for caching, and then you have to purge Cloudflare's cache after every deploy (a GitHub Actions step that calls Cloudflare's purge API works). Cloudflare Pages handles this for you, since its cached assets only live until the next deployment.
The SSL/TLS encryption mode is the other trap:
- Flexible: Cloudflare connects to GitHub over plain HTTP, GitHub's "Enforce HTTPS" setting redirects it back to HTTPS, and the site ends up in a redirect loop. Don't use it.
- Full: encrypts the connection to GitHub without validating GitHub's certificate. This is the forgiving option.
- Full (strict): validates GitHub's certificate, so the site breaks if that certificate can't be issued or renewed.
Certificate issuance is where the proxy causes trouble. GitHub's docs list DNS records that don't point to GitHub Pages' IP addresses as one reason a certificate can't be generated, and a proxied record resolves to Cloudflare's addresses instead. Another person on Reddit, in a thread about GitHub Pages certificate renewal failing behind Cloudflare's proxy, said the only fix that worked for them was moving the site to Cloudflare Pages.
Not everyone runs into this, and the encryption mode is a likely reason: Full doesn't check GitHub's certificate at all, while Full (strict) does. If you run this setup, check the HTTPS section of your repository's Pages settings after any DNS change, and don't switch to Full (strict) unless GitHub's certificate is in place.
Cloudflare's redirect and header rules do fill two of GitHub Pages' biggest gaps, so the proxy has its uses. But if you want Cloudflare's edge serving your HTML, moving the site to Cloudflare Pages takes less upkeep than maintaining cache rules and purges.
Feature Comparison Table
| Feature | GitHub Pages | Cloudflare Pages |
|---|---|---|
| Free plan | ✅ | ✅ |
| Edge network | Fastly (POP count not published) | 330+ cities |
| Static bandwidth | 100 GB/month soft limit | ✅ Unlimited |
| Static site generators | Jekyll built in, others via Actions | Framework presets or any build command |
| Private source repo on a free plan | ❌ | ✅ (Direct Upload) |
| Preview deployments | ❌ | ✅ Unlimited |
| Server-side redirects (301) | ❌ | ✅ _redirects |
| Custom headers | ❌ | ✅ _headers |
| Browser cache control | ❌ Fixed at 10 minutes | ✅ Configurable |
| Brotli compression | ❌ gzip in our checks | ✅ |
| HTTP/3 | ❌ Not advertised in our checks | ✅ |
| Server-side code | ❌ | ✅ Functions |
| Built-in analytics | ❌ | ✅ Web Analytics |
| Apex domain without moving DNS | ✅ | ❌ |
| Commercial sites | ❌ | ✅ |
Moving From GitHub Pages to Cloudflare Pages
If you decide to switch, follow this order so visitors never land on a broken page:
- Create the project. In the Cloudflare dashboard, go to Workers & Pages, connect your repository, and set the build command and output folder (
_sitefor Jekyll,publicfor Hugo,distfor Astro and Vite,buildfor Docusaurus). - Fix the base path. A GitHub project site lives under
/repository-name/, whileproject.pages.devserves from the root. Updatebaseurlin Jekyll,baseURLin Hugo,basein Astro or Vite, orbaseUrlin Docusaurus. - Move your redirects from jekyll-redirect-from into a
_redirectsfile with301status codes. - Test the
pages.devURL before touching DNS: the 404 page, redirects, forms, and TTFB from your main regions. - Add the custom domain in the Pages dashboard first, then update DNS. Cloudflare's docs warn that a CNAME pointed at Pages before the domain is added can return a 522 error.
- Unpublish the GitHub Pages site once the new one is live, so the old
github.iocopy doesn't stay online next to it.
Best Use Cases for GitHub Pages
Open-source project documentation. The docs live next to the code, contributors already have access, and nobody needs a second account to fix a typo.
Personal Jekyll blogs and portfolios. You push Markdown and GitHub builds the site without a workflow file.
Small sites that never move URLs. If you'll never rename a page or need a security header, the missing redirects and headers won't hurt you.
Apex domains with DNS you don't want to move. GitHub Pages works with A and AAAA records at any DNS provider.
Best Use Cases for Cloudflare Pages
Sites with visitors in many countries. More than 330 cities give your visitors a better chance of having an edge server nearby.
Sites that might get a traffic spike. Static requests are unlimited, so a front-page moment on Hacker News won't run into a 100 GB soft limit.
Sites that change URLs. Real 301 redirects matter when you restructure a site or rename posts.
Teams that review changes before shipping. Preview URLs for every branch let reviewers check a change on a real URL.
Commercial sites. GitHub rules out stores and business sites on Pages, and Cloudflare's Pages docs have no such restriction.
Which is a Better Choice?
Summing up, here's your best choice:
- Open-source docs or a personal Jekyll site in a public repo? → GitHub Pages
- Visitors spread across many countries? → Cloudflare Pages, then confirm with a TTFB test
- Need 301 redirects, security headers or long browser caching? → Cloudflare Pages
- Serving the apex domain without moving DNS to Cloudflare? → GitHub Pages
- More than 20,000 files on a free plan? → GitHub Pages
- Selling something? → Cloudflare Pages
- Starting a new project on Cloudflare today? → Workers with static assets
Conclusion
For most static sites, Cloudflare Pages is the better host. It has the larger network on paper, no bandwidth cap for static files, and the redirects, headers, previews and caching control that GitHub Pages doesn't offer at all.
GitHub Pages still makes sense for simple sites. For an open-source project's docs or a personal Jekyll blog in a public repository, skipping a second account and any build setup is worth more than a 301 redirect.
The only case where I'd put Cloudflare's proxy in front of GitHub Pages is when you need Cloudflare's security or redirect rules and can't move the site yet. If speed is the goal, move the site instead.
Before you switch, deploy the same build to both platforms and run the TTFB Test from the regions your visitors come from. If the numbers are close for your audience, let the features decide.
