GitHub Pages turns a repository into a website. Push HTML to a branch, switch Pages on in the repository settings, and a few moments later the content is live on a github.io address with a certificate already in place. There is no server to configure and no bill, which makes it the fastest route from nothing to a public URL that exists.
It is also a static host with firm limits, and knowing them at the start is worth more than discovering them later. This covers the working setup, the custom domain, and the specific point where a project stops fitting.
Table of contents
- The five-minute version
- The two publishing sources, and which to choose
- Bypassing Jekyll, and other small traps
- Adding a custom domain
- The limits, stated plainly
- What to do when you outgrow it
- How this fits the rest of the stack
- FAQ
The five-minute version
For a personal site, the repository name is the configuration. Create a repository named yourusername.github.io, and its contents become your site at that address.
- Create the repository with that exact name, matching your username.
- Add an
index.htmlat the root, or aREADME.mdif you want the default theme to render it. - Open Settings, then Pages in the sidebar.
- Under Build and deployment, set the source to Deploy from a branch, and pick your default branch with the root folder.
- Wait. The first publish can take several minutes, which is long enough that people assume it has failed.
For a project site the pattern is the same but the URL is different: the site appears at yourusername.github.io/repository-name/, which matters more than it sounds. Every absolute path in your HTML now needs the repository name in front of it, and this is the single most common reason a site works locally and shows unstyled text once published.
The two publishing sources, and which to choose
There are two ways Pages builds a site, and the choice determines what you can do.
Deploy from a branch. GitHub serves files from a branch directly. If a _config.yml is present it runs Jekyll first, which is why a plain markdown file becomes a themed page without you doing anything. It is the simplest option and the right one for a site that is already HTML or already Jekyll.
GitHub Actions. A workflow builds the site and uploads the result. This is what you want for anything with a build step — a modern static site generator, a framework producing a static export, a Tailwind build. You get the full workflow log when a build fails, which is worth a great deal compared with a site that silently does not update.
If you are using any generator other than Jekyll, choose Actions. Committing a built dist folder to a branch works, and it turns every content change into a diff full of compiled output nobody can review.
Bypassing Jekyll, and other small traps
A few behaviours cause a disproportionate share of the confusion.
Files and folders starting with an underscore are ignored by Jekyll processing. If your build output contains a _next or _assets directory, those files silently do not publish. The fix is a .nojekyll file at the root of the published output, which turns Jekyll off entirely and serves your files as they are.
There is no server-side anything. No PHP, no Node process, no database, no environment variables at runtime, no redirects file, no custom headers. A 404 page works because it is a file called 404.html; a redirect requires a meta refresh or a small script.
Client-side routing needs a workaround. A single-page application will 404 on a deep link refresh, because there is no rewrite rule to send unknown paths to index.html. The usual hack is copying index.html to 404.html, which works and is exactly as unpleasant as it sounds.
Anything you commit is public. On a free account, Pages sites are served from public repositories. An API key in your built JavaScript is on the internet, in the repository, and in the git history.
Adding a custom domain
This is the step most worth doing, because a github.io address is not yours in any lasting sense.
For an apex domain, create four A records at your DNS provider pointing at the GitHub Pages addresses listed in their documentation, or an ALIAS/ANAME record if your provider supports one. For a www subdomain, a single CNAME record pointing at yourusername.github.io is enough — and the subdomain version is more robust, because CNAME records survive an address change and hard-coded A records do not.
Then enter the domain in Settings → Pages → Custom domain. GitHub writes a CNAME file into the repository, which is worth knowing about because a build process that wipes the publish directory will delete it and your domain will quietly detach.
Once DNS resolves, tick Enforce HTTPS. The certificate is issued automatically and takes a little while. If the option is greyed out, DNS has not propagated yet; the answer is to wait rather than to change anything.
The limits, stated plainly
GitHub publishes soft limits for Pages, and they are generous for what the service is:
- Repositories are recommended to stay under 1GB, and published sites under 1GB.
- Bandwidth has a soft limit of 100GB per month.
- Builds are limited to roughly ten per hour.
- Sites are intended for static content, and the terms exclude running a business that is primarily commercial transactions.
In practice most people never approach any of these. The build limit is the one that bites first, usually when somebody wires up a workflow that redeploys on every push to a busy repository.
The real limit is not in that list. It is that the site cannot do anything on the server. The moment you need a contact form that actually sends, a login, a database, a scheduled job, an API key kept out of the browser, or a redirect rule, you are past what a static file host can do — and you will reach one of those far sooner than you will reach 100GB of bandwidth.
What to do when you outgrow it
The transition is less dramatic than people expect, because the static part of the site does not need to change.
The usual shapes:
- You need forms, auth, or a database. Add a small backend service alongside the static site and call it from the front end. The static site keeps working exactly as it did.
- You need redirects, custom headers, or SPA routing. These are static-hosting features that Pages simply does not offer, so the move is to a static host that has them, and it is a build-configuration change rather than a rewrite.
- The repository must be private. A private repository publishing a public site is normal on most static hosts and is a paid feature on Pages.
- Builds are slow or the limit is in the way. A host that builds from the repository on push, with a real build log, removes the workflow you had to write yourself.
None of these require abandoning the workflow you like. Connecting a repository, building on push, and getting a live route is the same mental model — with somewhere for the server-side half to live.
How this fits the rest of the stack
The honest position on GitHub Pages is that it is very good at exactly one thing and makes no pretence of doing the rest. Static files, a certificate, a custom domain, no cost. When a project stays inside that description it is hard to beat.
When it does not, the question becomes what the pieces cost — and it is usually less than people assume, which is why it is worth checking rather than guessing. The RunxBuild hosting calculator prices a static site, a backend service, a database and bandwidth as separate lines. RunxBuild builds static sites from a repository with custom domains, headers, redirects, rewrites and SPA fallback included, and 120GB of bandwidth before overage at $0.10/GB — which covers the four things on the list above that Pages cannot do, without changing how you work.
Useful related references:
- GitHub Pages and SEO
- Deploying a static HTML website for free
- Static sites on RunxBuild
- Custom domains for static sites
- Connecting a GitHub repository
FAQ
How do I start with GitHub Pages?
Create a repository named after your username followed by github.io, add an index.html, then enable Pages in Settings with your default branch as the source. The site appears at that address within a few minutes. Project sites use any repository name and publish under a subpath.
Is GitHub Pages free?
Yes, for public repositories, including a certificate and a custom domain. Publishing from a private repository requires a paid GitHub plan. Bandwidth has a soft limit of 100GB a month and sites are expected to stay under 1GB.
Why is my GitHub Pages site not showing CSS?
Almost always because a project site publishes under a subpath and the stylesheet is linked with an absolute path. Either use relative paths or set the base URL to include the repository name. The second common cause is a build output folder starting with an underscore, which Jekyll ignores unless a .nojekyll file is present.
Can GitHub Pages run a backend?
No. It serves static files only, so there is no PHP, no Node process, no database and no server-side environment variables. Forms, authentication and scheduled jobs all need something running elsewhere, which is the usual reason people move on.
How do I add a custom domain to GitHub Pages?
Point a CNAME record for the www subdomain at your github.io address, or apex A records at the addresses in GitHub’s documentation, then enter the domain under Settings and Pages. Enable Enforce HTTPS once DNS resolves and the certificate has been issued.