GitHub

How to Host a Website on GitHub

How to Host a Website on GitHub

To host a site on GitHub you need a repository with an entry file such as index.html in it, plus a publishing source picked in the repository settings.

GitHub Pages serves HTML, CSS and JavaScript files from that repository at a github.io address or a custom domain, and it costs nothing on public repositories.

Visibility is where people get caught out. A Pages site is reachable by anyone on the internet even when its repository is private (GitHub Docs, 2026), so API keys and unfinished drafts need to leave the repository before the first publish.

Which type of GitHub Pages site fits the project?

Pick a user site when the page is your main presence online, and a project site for everything else. The repository name and the final URL both depend on that choice, so make it before creating any files.

Under the hood, GitHub Pages works as a static hosting service built into GitHub, pulling HTML, CSS and JavaScript out of a repository and turning it into a website (GitHub Docs, 2026).

Site typeRepository nameDefault URLAllowed
User siteusername.github.iohttps://username.github.io1 per account
Organization siteorganization.github.iohttps://organization.github.io1 per account
Project siteAny repository namehttps://username.github.io/repository-name1 per repository

GitHub Docs (2026) caps user and organization sites at one per account, so a second portfolio or a client demo ends up in a project site under its own path.

If the site should load from the bare username.github.io address, build the user site. If that slot is already taken, or the site belongs to a single codebase, a project site is the better fit.

That extra path segment has a cost, though. It is why build tools such as Vite need a base path set before assets load correctly.

Whichever type you pick, the content stays static. The same documentation states that GitHub Pages does not support server-side languages such as PHP, Ruby or Python.

How to create the repository and add the site files

YouTube player

Create a repository with the right name, then push an entry file to the top level of the publishing source. That’s all there is to it.

Why is GitHub the heart of open source?

Uncover GitHub statistics: developer community growth, repository trends, collaboration patterns, and the platform that powers modern software development.

Explore GitHub Data →
  1. Select the plus menu in the top-right corner of GitHub, then New repository.
  2. Pick the owner account and type the name. For a user site that is username.github.io, in lowercase even if your username has capitals.
  3. Set visibility to public on a Free plan. Private repositories can publish a site only on GitHub Pro, Team or Enterprise plans.
  4. Switch Add README on and click Create repository.
  5. Add index.html to the top level of the source folder and commit it.

GitHub Pages accepts index.html, index.md or README.md as the entry file (GitHub Docs, 2026). If the source is the /docs folder on main, the file belongs in /docs, not at the repository root.

Never filled in the repository form before? A short guide to creating a repository in GitHub covers each field.

Same warning as at the top, because it’s easy to forget: strip API keys, drafts and anything internal before publishing. The site is public no matter what the repository’s visibility says.

Uploading files in the browser

Open the repository, choose Add file, then Upload files, and drag in the contents of the site folder. Drag the contents, not the folder itself, or index.html ends up one level too deep.

Write a commit message such as “Add initial site files” and commit to the main branch.

Looking to sharpen your Git skills? Branching, merging, rebasing, and everything else you need - including git rebase, git stash, and commit workflows - is on one page in the Git Cheat Sheet.

Pushing files with Git or GitHub Desktop

Clone the repository, copy the site files into the clone, then commit and push.

  • git clone https://github.com/username/username.github.io.git
  • git add .
  • git commit -m "Add initial site files"
  • git push origin main

GitHub Desktop does the same with buttons. Commit in the left panel, then push origin. If a push gets rejected, the walkthrough on how to push to GitHub lists the usual causes.

How to set the publishing source in repository settings

Publish from a branch when the site is plain HTML or Jekyll, and switch to GitHub Actions the moment it needs a build step. GitHub’s documentation recommends the branch route for anyone who does not need control over the build (GitHub Docs, 2026).

Deploying from a branch is the quick option. Setup is two dropdowns and a save button, and Jekyll builds the site with no configuration. The catch is that any other generator needs its build output committed along with a .nojekyll file, and repositories containing symbolic links can’t use this route at all.

GitHub Actions takes more effort up front. It runs any build, whether that’s Vite, Hugo, a Next.js static export or something else, and it keeps compiled files out of the repository. The soft limit of 10 builds per hour does not apply to custom workflows either. On the downside you have a workflow file to write and maintain, and the deploy step only runs on pushes to the default branch. Honestly, once a site has any build step at all, I’d take the extra file over committing compiled output.

▶ GIT EXPLAINED

If you commit, branch, or merge code every day, it helps to know how Git came to be. This video covers how Linus Torvalds created Git in just 10 days after a license dispute and explains how its data model actually works.

Linus Torvalds Built Git in Days Out of Spite
Built in 10 Days Out of Spite. Git and its data model explained in plain language.

For the branch route, open Settings, then Pages, and find Build and deployment. Under Source choose Deploy from a branch, select the branch, pick / (root) or /docs, and save.

The source can be any Git branch in the repository, which is why many projects keep compiled output on a dedicated gh-pages branch. On a branch deployment GitHub Pages runs Jekyll by default, so anything else needs an empty .nojekyll file at the root of the source.

Choosing Actions hands the build to a GitHub Actions workflow. The workflow templates GitHub suggests run these steps in order:

  • Trigger on a push to the default branch, or manually
  • Check out the repository with actions/checkout
  • Build the static files, if the site needs it
  • Upload them with actions/upload-pages-artifact
  • Deploy with actions/deploy-pages

Remove the /docs folder after selecting it and the build errors out with a missing folder message.

And a branch deployment only triggers automatically when someone with admin permissions and a verified email address has pushed to the source. Every Pages deployment runs as a workflow, even from a branch, so the Actions tab is where build errors show up.

How long until the site goes live, and how to check it

GitHub Docs (2026) puts publishing at up to 10 minutes after a push. If the site still shows nothing after an hour, the same documentation treats it as a build problem rather than a delay.

  • The Visit site button under Settings, then Pages, appears once the first deployment finishes
  • The Actions tab shows the Pages run in green or red, with logs for whichever step failed
  • On a project site the URL ends with the repository name, and it won’t answer at the bare username.github.io address
  • A hard refresh in the browser comes before concluding the push failed

Automated commits are a quieter trap. Commits pushed by a workflow using the default GITHUB_TOKEN do not trigger a Pages build, so the live site can sit stale while the repository looks current.

Wait ten minutes, then read the Actions log. Refreshing harder will not fix a failed build.

How to connect a custom domain to GitHub Pages

Add the domain under Settings, then Pages, then create matching DNS records at your registrar. Apex domains get A records and www gets a CNAME record. Order matters, and the repository comes first.

DomainRecord typePoints to
Apex (example.com)A4 GitHub Pages IPv4 addresses
Apex (example.com)ALIAS or ANAMEusername.github.io
www.example.comCNAMEusername.github.io

Claim the domain in the repository before touching DNS. A record that points at GitHub for a domain no Pages site has claimed lets someone else host content on one of your subdomains (GitHub Docs, 2026).

Verifying the domain in your account settings stops other GitHub users from using it with their own repositories.

Changes take up to 24 hours to propagate. Run a lookup before assuming anything is broken.

Apex domain setup

The A records for the apex go to these addresses.

  • 185.199.108.153
  • 185.199.109.153
  • 185.199.110.153
  • 185.199.111.153

Delete any default record your DNS provider added before saving these. AAAA records for the IPv6 addresses in GitHub’s documentation are optional, but keep the A records alongside them because IPv6 adoption is still uneven.

Check the result with dig example.com +noall +answer -t A and compare the answers to the four addresses above. Windows has no dig, so use Resolve-DnsName in PowerShell.

www subdomain setup

A www subdomain needs one CNAME record pointing to username.github.io. Leave the repository name off the target, and never use the unique pages.github.io address shown on the settings screen.

Set up both the apex and www. GitHub redirects between them automatically, so whichever one you save as the custom domain becomes the main address and the other forwards to it.

With a branch deployment, saving the domain commits a CNAME file to the root of the source branch. Actions deployments create no CNAME file, and an existing one is ignored.

One site gets one domain. A second apex domain, or blog.example.com next to example.com, is not supported (www is the single exception). Skip wildcard DNS records too, because they leave deeper subdomains open to takeover even after verification.

How to enforce HTTPS on a GitHub Pages site

Tick Enforce HTTPS under Settings, then Pages, once a check mark appears beside the custom domain. A plain github.io address is already served over HTTPS, so the checkbox matters for custom domains.

Saving a custom domain starts an automatic DNS check. If it passes, GitHub requests a TLS certificate from Let’s Encrypt and installs it on the servers that handle Pages traffic.

The wait is documented two ways. GitHub’s custom domain troubleshooting page says HTTPS can take up to an hour, while its domain management page says Enforce HTTPS can take up to 24 hours to become available.

GitHub does not explain the gap. The 24 hour figure matches the DNS propagation limit, so treat one hour as the best case.

When the checkbox stays greyed out, look at the DNS records first.

  • Any additional A, AAAA, ALIAS or ANAME record on the apex, or a stray CNAME on www, can block certificate generation
  • Records that do not point at the GitHub Pages addresses will do the same
  • If you use CAA records, at least one must carry letsencrypt.org
  • A subdomain aimed at the apex breaks HTTPS enforcement, and the subdomain may never reach the site

If provisioning stalls several minutes after saving, click Remove next to the domain, retype it and save again. That cancels the job and restarts it.

Mixed content is the other usual snag. Search the source for http:// and switch every stylesheet, script and image link to https://.

Keep passwords and card numbers off the site anyway. GitHub states that Pages should not carry sensitive transactions.

What are the limits of GitHub Pages, and when does it stop fitting?

A site fits while it stays within GitHub’s documented usage limits. Past them, GitHub may stop serving it (GitHub Docs, 2026).

A published site tops out at 1 GB, and that is also the recommended size of the source repository. Bandwidth has a soft limit of 100 GB per month, builds have a soft limit of 10 per hour, and a deployment times out after 10 minutes.

Exceed the soft limits and the typical outcome is an email from GitHub Support suggesting a CDN in front of the site, GitHub releases for large files, or a different host.

Logins, databases and form handling don’t fit, since all of them need an outside service. Secrets in client-side code are a bad idea here too. So are sensitive transactions, and so is traffic that regularly approaches 100 GB a month.

A static page cannot hide a key it sends to the browser. Keeping an API key out of the repository is only half the job, because the call itself needs a proxy or serverless function.

Cost is a smaller factor. GitHub Actions is free for public repositories, while private and internal ones are billed past the monthly free minutes (GitHub Docs, 2026).

On a Free plan the repository has to be public, so scan the commit history for anything sensitive before you switch the repository to public.

For documentation, portfolios and project sites, stay on GitHub Pages. Move to Netlify or Cloudflare Pages when the site needs server functions, form handling or branch previews.

The Vite deployment guide notes that Netlify creates Preview Deployments for branch pushes and pull requests, while Cloudflare Pages does so for branch pushes unless its branch build controls turn them off. GitHub’s own Pages workflow template skips the deploy step on pull requests.

How to fix a site that is not updating, shows a 404, or fails to build

404 errors and missing files

  • The entry file name is case sensitive, so Index.html fails where index.html works
  • The file has to sit at the top of the source folder, or at the top of the uploaded artifact for Actions deployments
  • DNS records need to point at GitHub, and the CNAME target is username.github.io without the repository name
  • Flipping a repository between public and private changes the site URL, and links break until the next rebuild
  • A site on a private repository goes 404 when the subscription ends. Renewing redeploys it, and making the repository public keeps it free

The guides disagree on one point. GitHub’s 404 page says the publishing branch must be main or the default branch, while the publishing source page allows any branch.

If a gh-pages site returns 404 and everything else checks out, test from the default branch.

A site sitting at the wrong path usually traces back to the repository name. Renaming the repository changes a project site URL, and a user site must match username.github.io exactly.

If the custom domain vanishes after a deploy, the cause is often a force push. Many static site generators do this, and it overwrites the CNAME file GitHub committed when you saved the domain. Pull that commit before building locally so the file ships with the build (GitHub Docs, 2026).

The idfkit documentation repository hit this in March 2026. Its deploy step recreated the gh-pages branch on every run and dropped the CNAME file, until a pull request added the domain to the deploy action’s cname setting (GitHub pull request, idfkit-docs).

Failed builds and Actions errors

Read the failed run’s log before changing anything. The step name usually points straight at the cause.

  • A custom workflow needs pages: write and id-token: write permissions, as the sample in the Vite documentation (v8.3.1) shows
  • Jekyll errors come from bad front matter or configuration, and GitHub keeps a separate page listing each build error
  • A failed run can be re-run from the Actions tab without a new commit
  • GitHub creates a github-pages environment automatically and recommends a deployment protection rule so only the default branch can deploy to it

Blank pages on React and Vite projects

A blank page on a React or Vite project is almost always a base path problem. The built HTML requests assets from the domain root, but a project site lives under its repository name.

Deploy targetVite base value
username.github.io (user site)'/'
Custom domain such as www.example.com'/'
username.github.io/repository-name'/repository-name/'

Set the publishing source to GitHub Actions as well, since Vite needs a build step before anything can be deployed (Vite documentation, v8.3.1).

Client-side routing adds one more failure. A hard refresh on a deep link returns a 404 because static hosting has no rewrite rules, so hash-based routing is the quickest fix.

GitHub Pages FAQ

How do I unpublish a GitHub Pages site?

Open the repository’s main page, click the menu beside the “Your site is live at” message, and choose Unpublish site. That removes the current deployment while leaving repository content and settings untouched (GitHub Docs, 2026).

To bring the site back, create a new deployment. Trigger a workflow run if you publish with GitHub Actions, or set the publishing source again and commit to it if you publish from a branch.

Can GitHub Pages show a custom 404 page?

Yes. Create a file named 404.html in the publishing source and it replaces the default error page for missing paths.

A Markdown file works too, as 404.md with permalink: /404.html in its front matter.

Is an online store allowed on GitHub Pages?

No. GitHub’s usage rules bar Pages from running an online business, an e-commerce site or a SaaS product.

The same page says Pages should not handle passwords or credit card numbers. Open source project pages, blogs and resumes are the intended uses.

What does a 429 error from a GitHub Pages site mean?

A 429 status means the request triggered rate limiting. GitHub applies these limits to keep service consistent across all Pages sites, and says they are not meant to interfere with legitimate use.

The response carries an HTML body explaining the block.

Does GitHub Pages log visitor information?

Yes. GitHub logs and stores each visitor’s IP address for security purposes, whether or not the visitor is signed in to GitHub.

That is separate from any analytics script you add to the site yourself.

Keeping a GitHub Pages Site Stable After Launch

A GitHub Pages site stays stable when you do things in a fixed order. Verify the custom domain first, enforce HTTPS once DNS resolves, and move the build into a GitHub Actions workflow last.

Verification leads because a disabled site with live DNS records lets another account host content on your subdomains. If the domain is unverified, remove the records the moment the site is unpublished (GitHub Docs, 2026).

Debug by layer. A content push is overdue after 10 minutes, a DNS change only after 24 hours, so neither problem gets diagnosed on the other’s clock.

Day-to-day work then reduces to updating the GitHub repository that feeds the site.

This order reflects GitHub’s documentation as of October 2026. A change to the soft usage limits or to the plan rules for private repositories would shift it.

Bogdan Sandu

Stay sharp. Ship better code.

Every week: one curated article, one tool worth knowing, one tip you can use tomorrow. No noise, no padding.