GitHub Pages — The Custom Domain That Went to the Wrong Server
A GitHub Pages custom domain pointed to the wrong server after a CNAME file was accidentally deleted.
20+ years shipping production infrastructure and CI/CD at scale. Written from production experience, not tutorials.
- ✓Git installed (v2.30+), GitHub account, basic knowledge of Git (commit, push, branch), familiarity with HTML/CSS, optional: a static site generator (Jekyll, Hugo, etc.) if using one.
1. Push your site to a repo. 2. Go to Settings → Pages. 3. Select source branch (gh-pages or main) and folder (/root or /docs). 4. (Optional) Add a custom domain via CNAME record. GitHub serves your site automatically.
GitHub Pages is like having a free web host built into every GitHub repo. You push your website files to a special branch, and GitHub automatically serves them to visitors. It's like dropping PDFs into a Dropbox folder and getting a URL to share — but for websites. No server setup, no hosting bills, no deployment scripts needed.
| Chrome | Firefox | Safari | Edge |
|---|---|---|---|
| ✓ | ✓ | ✓ | ✓ |
GitHub Pages is the simplest way to host a static site: push code, get a URL. It powers documentation sites, personal portfolios, project landing pages, and even full static blogs (via Jekyll). The simplicity is the killer feature: there's no server to manage, no CI/CD pipeline to configure, no hosting bill. But simplicity has edge cases: the CNAME file, 404 handling, and custom domain DNS propagation. This guide covers setup, the custom domain incident that took down a product launch, and how to avoid the most common Pages pitfalls.
What Is GitHub Pages and Why Use It?
GitHub Pages is a static site hosting service that takes HTML, CSS, and JavaScript files directly from a repository on GitHub, optionally runs the files through a build process, and publishes a website. It's free, integrates with your existing Git workflow, and supports custom domains and HTTPS. For production use, it's ideal for documentation sites, project landing pages, personal portfolios, and even small business sites. The key advantage is zero server management: you push code, and GitHub handles deployment. However, it's not a dynamic hosting platform — no server-side processing, databases, or backend logic. If you need those, look elsewhere. For static content, it's a no-brainer.
Setting Up Your Repository for GitHub Pages
To host a site, you need a repository. For a user or organization site, name the repo <username>.github.io. For project sites, any name works, and the site will be at <username>.github.io/<repo-name>. Enable Pages in the repo Settings > Pages. Choose the source branch (usually main or gh-pages) and optionally a /docs folder. For modern static site generators (Jekyll, Hugo, etc.), GitHub Pages can build your site automatically. For plain HTML, just push your files. Important: if you use a custom domain, add a CNAME file in the root of your publishing source with your domain (e.g., example.com). Also configure your DNS provider with a CNAME record pointing to <username>.github.io.
main or gh-pages, GitHub Pages won't deploy. Stick to the defaults or configure explicitly in Settings.main but forgetting to set the Pages source to main. The site stays on the old version. Always double-check the source branch after initial setup.Using a Static Site Generator with GitHub Pages
GitHub Pages natively supports Jekyll, but you can use any static site generator (Hugo, Next.js, Astro) by building locally and pushing the output, or using GitHub Actions to build and deploy. For Jekyll, just push your source files; GitHub builds them automatically. For others, you need a workflow. The advantage of a generator: templating, Markdown support, asset pipeline, and SEO optimization. For production, I recommend using a CI/CD pipeline to ensure builds are reproducible and tested. Avoid committing the built _site folder — let the CI generate it. This prevents merge conflicts and keeps the repo clean.
_site folder. Switching to CI-only builds eliminated that class of error.Custom Domains and HTTPS
GitHub Pages supports custom domains with automatic HTTPS via Let's Encrypt. To set up, add a CNAME file to your repository's root (or configure in Settings > Pages > Custom domain). Then update your DNS: for apex domains (e.g., example.com), add an A record pointing to GitHub's IPs (185.199.108.153, 185.199.109.153, 185.199.110.153, 185.199.111.153). For subdomains (e.g., www.example.com), add a CNAME record pointing to <username>.github.io. After configuration, GitHub provisions an SSL certificate automatically. This can take up to an hour. If you use a CDN like Cloudflare, disable the proxy (orange cloud) for the DNS record, or use a full strict SSL mode.
Enforcing HTTPS and Redirects
GitHub Pages automatically redirects HTTP to HTTPS if you have a custom domain and HTTPS is enabled. You can enforce HTTPS in Settings > Pages > Enforce HTTPS. This is critical for production: without it, users may access your site over insecure HTTP. Additionally, you can set up redirects using a _redirects file (Netlify-style) or a staticwebapp.config.json for Azure Static Web Apps, but GitHub Pages has limited redirect support. For advanced redirects, use a meta refresh in HTML or a JavaScript redirect. For SEO, prefer server-side redirects; since GitHub Pages doesn't support them natively, consider using a service like Cloudflare Workers or Netlify instead.
Using GitHub Actions for Advanced Deployments
GitHub Actions gives you full control over the build and deploy process. You can run tests, lint, build, and then deploy. The official actions/deploy-pages action handles deployment to the github-pages environment. For production, always pin action versions to a specific commit SHA to avoid supply chain attacks. Also, use environment secrets for API keys or tokens. A common pattern: build on push to main, deploy to a staging environment on a develop branch, and promote to production via a workflow dispatch. This mirrors a real CI/CD pipeline.
actions/checkout@a81bbbf8298c0fa03ea29cdc473d45769f953675) to prevent malicious updates.workflow_dispatch for manual production deployments to control timing.Handling 404 Pages and SEO
GitHub Pages automatically serves a 404 page if a file is not found. You can customize it by adding a 404.html file to the root of your publishing source. For SEO, ensure your 404 page is helpful and includes a link to the homepage. Also, create a sitemap.xml and robots.txt to guide search engines. For single-page applications (SPAs), you need to handle client-side routing: create a 404.html that redirects to index.html with the requested path as a hash or query parameter. This is a common pitfall — without it, direct links to SPA routes return 404.
Performance Optimization and Caching
GitHub Pages serves static files with strong caching headers (Cache-Control: max-age=600 for HTML, 1 year for assets with hashed filenames). For production, optimize images (use WebP, compress), minify CSS/JS, and enable gzip compression (GitHub Pages does this automatically). Use a CDN like Cloudflare in front of GitHub Pages for additional caching and DDoS protection. However, be aware that GitHub Pages has a bandwidth limit of 100 GB per month (for free accounts). For high-traffic sites, consider a dedicated CDN or static hosting provider. Also, use lazy loading for images and defer non-critical scripts.
Troubleshooting Common GitHub Pages Issues
Common issues: site not updating (check Actions tab for build failures), custom domain not resolving (DNS propagation can take 24h, verify A records), HTTPS not provisioning (ensure DNS is correct and not proxied through Cloudflare), 404 errors (check file paths and case sensitivity — GitHub Pages is case-sensitive), and build failures (check your static site generator's logs). For Jekyll, ensure your Gemfile and _config.yml are correct. For Actions, review the workflow logs. If your site is stuck on an old version, trigger a manual redeploy via the Actions tab or push an empty commit.
Image.jpg but the file is image.jpg, you'll get a 404. Always use consistent casing.When Not to Use GitHub Pages
GitHub Pages is not suitable for every project. Avoid it if you need server-side processing (PHP, Node.js, Python), a database, user authentication, or real-time features. Also, if you require custom redirects (301/302), advanced caching rules, or a Web Application Firewall (WAF), look elsewhere. For e-commerce, membership sites, or any site handling sensitive data, GitHub Pages lacks the necessary security controls. Finally, if your site exceeds 1 GB in size or 100 GB monthly bandwidth, you'll hit limits. For those cases, consider Vercel, Netlify, AWS S3 + CloudFront, or a VPS.
Custom Domain Resolved to Wrong Server During Product Launch
git log --all --full-history -- CNAME. 2. Found the merge commit that deleted it. 3. Restored the CNAME file: git revert <merge-sha> (or manually recreated it). 4. Waited 5 minutes for GitHub to detect the CNAME file and re-enable the custom domain. 5. Added a CI check: test -f CNAME && echo 'CNAME exists' || exit 1. 6. Added a post-deploy check that fetches the custom domain URL and verifies HTTP 200.- CNAME file must exist in the repo for custom domains to work.
- A deleted CNAME file causes the custom domain to 404.
- Add CI checks that verify CNAME exists.
- Monitor the custom domain URL with a health check — don't rely on the github.io preview URL.
<username>.github.io repo — serves automatically from main branch<username>.github.io, add CNAME file to repo| File | Command / Code | Purpose |
|---|---|---|
| .github | name: Deploy to GitHub Pages | What Is GitHub Pages and Why Use It? |
| terminal | git init my-site | Setting Up Your Repository for GitHub Pages |
| .github | name: Deploy Hugo site to Pages | Using a Static Site Generator with GitHub Pages |
| DNS Records | example.com. A 185.199.108.153 | Custom Domains and HTTPS |
| redirect.html | Enforcing HTTPS and Redirects | |
| .github | name: Deploy to Staging | Using GitHub Actions for Advanced Deployments |
| 404.html | Handling 404 Pages and SEO | |
| index.html | Performance Optimization and Caching | |
| terminal | git commit --allow-empty -m "Trigger redeploy" | Troubleshooting Common GitHub Pages Issues |
| comparison.txt | GitHub Pages: | When Not to Use GitHub Pages |
Key takeaways
Frequently Asked Questions
20+ years shipping production infrastructure and CI/CD at scale. Written from production experience, not tutorials.
That's Git. Mark it forged?
4 min read · try the examples if you haven't