GitHub Pages will serve a fast, HTTPS static site for free, and fast static HTML is genuinely good for search. What it will not do is issue a 301, set a header, or serve a custom 410 — and those gaps are the ceiling on what you can fix.
Static sites have a real SEO advantage: they are fast, and every piece of markup is fully rendered before a crawler arrives. GitHub Pages delivers that with a CDN and a certificate at no cost, which is a strong starting position.
The limits are structural rather than incidental. They are worth understanding before you build a content site there and discover them during a migration.
Table of contents
- Get the fundamentals in place first
- The duplicate content problem specific to GitHub Pages
- What you cannot do, and why each one matters
- Getting new pages crawled
- Speed, which you mostly already have
- When to move off, and to what
- How this fits the rest of the stack
- FAQ
Get the fundamentals in place first
Most static-site SEO problems are missing markup rather than platform limits. Every page needs:
- A unique
<title>that describes that page, not the site. - A
<meta name="description">written for a human, not stuffed with keywords. - A
<link rel="canonical">with the absolute URL, which matters more here than usual — see the next section. - Open Graph and Twitter card tags, which decide what a shared link looks like.
- One
<h1>, then a heading structure that reflects the document rather than the design.
If you use Jekyll, the jekyll-seo-tag plugin generates most of this from your front matter and is supported on GitHub Pages without custom build configuration. Other generators have equivalents. Either way, verify by viewing source on a built page rather than trusting the plugin ran.
Add a sitemap.xml and a robots.txt referencing it. Nearly every static site generator produces a sitemap with one plugin or one config line, and there is no reason not to.
The duplicate content problem specific to GitHub Pages
This one is particular to the platform and catches people. A project site is reachable at both username.github.io/repo-name/ and, once configured, your custom domain. Both serve identical content.
Search engines treat those as separate URLs with duplicate content, and they will pick which one to index on your behalf. Usually not the one you wanted.
Fix it with absolute canonical tags pointing at the custom domain:
<link rel="canonical" href="https://example.com/getting-started/" />
Absolute, not relative. A relative canonical resolves against whichever hostname served the page, which makes it useless for exactly this problem.
The stronger fix would be a 301 from the github.io address to the custom domain, and that is precisely what the platform does not offer. Canonicals are a hint that search engines usually honour; a redirect is an instruction. You are stuck with the hint.
What you cannot do, and why each one matters
The list of structural limitations:
- No server-side redirects. No 301s. Every URL you have ever published is permanent, or it 404s. Restructuring a site means losing whatever authority the old URLs had.
- No custom response headers. No
Cache-Controltuning, noContent-Security-Policy, noX-Robots-Tagto noindex a section without touching its markup. - No custom 404 status handling beyond the page itself. You get a
404.html, but you cannot serve a 410 Gone for genuinely removed content, which is the signal that gets a URL dropped rather than recrawled. - No server-side rendering or edge logic. Fine for content sites; a hard stop for anything needing per-request behaviour.
- Soft build limits. Sites are intended to be modest in size and build frequency.
The redirect gap is the one that hurts. The usual workaround is an HTML page with a <meta http-equiv="refresh"> and a canonical tag pointing at the new location:
<meta http-equiv="refresh" content="0; url=https://example.com/new-path/" />
<link rel="canonical" href="https://example.com/new-path/" />
Search engines do follow these and generally pass authority, but a meta refresh is slower for users, weaker as a signal than a 301, and something you have to generate for every moved URL by hand.
Getting new pages crawled
Static sites publish instantly and then wait to be noticed. Two things speed that up.
Submit the sitemap in Google Search Console and Bing Webmaster Tools once. That is the baseline and it is a five-minute job most people skip.
Then submit new URLs directly. IndexNow is a single HTTP request per batch and is honoured by Bing and its partners:
curl -X POST https://api.indexnow.org/indexnow \
-H "Content-Type: application/json" \
-d '{
"host": "example.com",
"key": "YOUR_KEY",
"keyLocation": "https://example.com/YOUR_KEY.txt",
"urlList": ["https://example.com/new-post/"]
}'
The key is a file you host at the root containing the key itself, which proves you control the domain. Wire this into your build workflow so publishing a page submits it automatically — it is a handful of lines in a CI job and it removes a manual step that would otherwise be forgotten.
Speed, which you mostly already have
Static HTML from a CDN starts well ahead of most sites. The remaining wins are in your own markup:
- Always set
widthandheighton images. Missing dimensions cause layout shift, which is a measured ranking signal and an easy fix. - Compress and resize images before committing them. There is no image optimisation service in front of a static host — a 4MB photo is served as 4MB.
- Self-host fonts or accept the third-party connection. Font loading is often the largest remaining blocker on an otherwise fast page.
- Ship less JavaScript. A content site that needs a framework at runtime usually does not.
Because you cannot set Cache-Control, you take whatever caching the platform provides. For a mostly-static content site that is fine; for a site with large assets you want to control, it is a real limitation.
When to move off, and to what
GitHub Pages is a good fit for documentation, a personal site, a project page — content that rarely moves and does not need per-request behaviour. Plenty of sites should just stay there.
The signals to move are specific: you need real 301s because URLs change, you need headers for security policy or caching, you need to noindex a section without editing every page, or the site has grown past what the platform is intended for.
At that point you want a static host that keeps the build-from-repository workflow and adds server-level control. On RunxBuild, static sites build from a repository with custom domains, configurable headers, redirects, rewrites and SPA fallback, and 120GB of bandwidth included before overage at $0.10/GB — so the migration is a config file rather than a rearchitecture, and the redirects you could not write become a list you can.
How this fits the rest of the stack
GitHub Pages gives you speed and HTTPS for free, and those are real SEO assets. What it withholds is server-level control: no 301s, no headers, no 410s, and a duplicate github.io address you can only paper over with canonical tags. Fix the markup fundamentals first, since most problems live there. When the redirects start mattering, the RunxBuild hosting calculator shows what a static site with headers and redirects included actually costs.
Useful related references:
- Static Pages: The Three Things They Actually Are, and the Three Things the Top Pages Aren’t Telling You
- Static Pages in 2026: The Cheap, Fast, Boring Choice That Quietly Outperforms
- The n8n HTML Node: Extracting Data From Pages Without Writing a Scraper
- Deploying from GitHub on RunxBuild
FAQ
Is GitHub Pages good for SEO?
Yes for the basics — static HTML served fast over HTTPS from a CDN is a strong foundation. The limits are structural: no server-side redirects, no custom headers, and no control over response codes beyond a 404 page.
How do I add a sitemap to GitHub Pages?
Generate one with your static site generator — jekyll-sitemap for Jekyll, a built-in integration for most others — so it is produced on every build. Reference it from robots.txt and submit it once in Google Search Console and Bing Webmaster Tools.
Can I do 301 redirects on GitHub Pages?
No. The platform serves static files and cannot issue redirect status codes. The workaround is an HTML page with a <meta http-equiv="refresh"> plus a canonical tag to the new URL, which search engines generally honour but which is weaker and slower than a real 301.
Why is my github.io URL indexed instead of my custom domain?
Both addresses serve identical content and search engines pick one. Add an absolute <link rel="canonical"> pointing at the custom domain on every page — relative canonicals resolve against whichever host served the page, so they do not help here.
When should I move off GitHub Pages?
When you need real 301 redirects for changed URLs, custom response headers for caching or security policy, an X-Robots-Tag to noindex a section, or the site has outgrown the platform’s intended scale. Until then it is a reasonable place for documentation and project sites.