On GitHub Pages, “404 page” usually means one of two problems:

  1. You want a custom 404 page so missing URLs show something helpful (not the default).
  2. You are seeing unexpected 404 errors (your site, a page, or assets are missing in production).

This guide covers both: how to ship a correct 404.html for Pages, and how to diagnose the most common 404 failure patterns for user/org sites, project sites, and custom domains.

Key Takeaways #

  • Treat 404 as “file not found”: GitHub Pages is static hosting—if the published root doesn’t contain the file for a path, the server returns 404.
  • Know your base path: user/org sites are served at /; project sites are served under /<repo>/ (most asset 404s are base-path mistakes).
  • Ship a real 404.html: Jekyll can generate it from 404.md using permalink: /404.html; other generators must output 404.html at the published root.
  • Debug from Pages settings + build output: confirm the Pages source and verify index.html + 404.html exist where Pages publishes from.
  • Use authoritative docs: GitHub Docs describes custom 404 pages and common causes of Pages 404 errors; confirm generator behavior in official docs.

What is a GitHub Pages 404 Page? #

An HTTP 404 Not Found response means the server did not find a current representation for the requested target resource. RFC 9110 defines it as the origin server not finding a current representation for that target resource.

“The origin server did not find a current representation for the target resource…” — RFC 9110 (HTTP Semantics)

On GitHub Pages, a “404 page” is the content served when a visitor requests a URL that doesn’t exist in your published site. GitHub Docs supports a custom 404 page so you can keep your theme and give readers a clear next step (home, search, popular pages).

GitHub Pages supports creating a custom 404 page for your site. — GitHub Docs (Creating a custom 404 page), adapted

The practical implication is simple: GitHub Pages serves exactly what you publish. If your output folder is wrong, or your site assumes the wrong base path, you’ll see 404s even if everything works locally.

Know Your Pages URL (User/Org Site vs Project Site) #

Most “mystery 404s” are just the wrong URL.

Site typeURL formPublic base pathMost common 404 pitfall
User/Org sitehttps://<user>.github.io//Publishing from the wrong branch/folder so index.html never reaches the published root
Project sitehttps://<user>.github.io/<repo>//<repo>/Absolute links and asset paths (/assets/...) don’t include the repo base path
Custom domainhttps://example.com//DNS/custom domain not configured or certificate not provisioned yet

When validating a 404 page, always test a URL that matches the site type you are actually deploying.

The 4 Most Common 404 Patterns on GitHub Pages #

This table is a fast “symptom → cause → check” map.

What 404s?Typical symptomMost likely causeFirst thing to check
Whole siteEvery URL returns 404Pages source wrong / output folder wrongSettings → Pages + published root contains index.html
One pageOnly one path 404sFile not generated at that pathBuild output: does the expected file exist?
CSS/JS/imagesHTML loads, assets failBase path mismatch (/<repo>/)Network tab: does the URL include the repo base path?
Custom domainDomain shows default Pages 404DNS/custom domain not configuredCustom domain setting + DNS records + HTTPS status

Step-by-Step: Add a Custom 404 Page (Correctly) #

1) Confirm what Pages is publishing #

Go to Settings → Pages and confirm:

  • Deploy source: branch/folder or GitHub Actions
  • Published root: what folder ends up being served

If the file doesn’t exist at the published root, GitHub Pages cannot serve it.

2) Choose the right 404 approach for your generator #

There are two reliable approaches:

Option A — publish 404.html directly (works for any static site)

  • Put a 404.html file at the published root (or ensure your generator outputs it there).

Option B — Jekyll: use 404.md → output /404.html

  • GitHub Docs describes creating 404.md with YAML front matter and permalink: /404.html.
  • This lets you write Markdown but still produce the correct output path (404.html at the site root).

Example (404.md for Jekyll on Pages):

---
permalink: /404.html
---

# Page not found

Try the homepage, or search for the topic.

3) For non-Jekyll generators: make sure 404.html exists at build output root #

If you build with Hugo, Next.js static export, Astro, etc., the only rule that matters is:

  • after the build, the folder you publish must contain 404.html at its root.

Hugo (example mental model):

  • add a template at layouts/404.html
  • run hugo
  • verify public/404.html exists before publishing

Hugo’s documentation notes that GitHub Pages’ redirection to a 404 page is automatic and not configurable—you provide the 404.html, and the host serves it when needed.

4) Deploy and validate #

After you commit and push:

  1. Wait for the Pages build to finish (or check GitHub Actions logs).
  2. Visit a URL that does not exist, for example:
    • User/org site: https://<user>.github.io/this-page-does-not-exist/
    • Project site: https://<user>.github.io/<repo>/this-page-does-not-exist/
  3. Confirm your custom 404 page renders (not the default Pages 404).

Step-by-Step: Troubleshoot Unexpected 404 Errors (Fast Checklist) #

When a site fails, reduce the problem to: “what does Pages publish?” vs “what URL is the browser requesting?”

1) Confirm you’re using the correct URL #

Double-check the site type and URL form:

  • User/org site: https://<user>.github.io/
  • Project site: https://<user>.github.io/<repo>/

GitHub Docs calls out incorrect URLs and misconfigured sources as common causes of Pages 404 errors.

2) Confirm index.html exists at the published root #

If the entire site 404s, treat it as “nothing is being published” until proven otherwise:

  1. Build locally.
  2. Inspect the folder you configured Pages to publish.
  3. Ensure index.html exists at the root of that folder.

This single check catches most misconfigurations immediately.

3) Confirm the Pages source (branch/folder vs Actions) #

Mismatch examples:

  • Your generator builds into public/, but Pages is publishing /docs.
  • You switched to Actions-based deploy but Pages is still set to deploy from a branch folder.

Fix: align the publish configuration with the folder that actually contains your build output.

4) Fix base-path bugs (asset 404s) #

If HTML loads but assets 404, it’s usually base path:

  • project sites are served under /<repo>/
  • absolute asset URLs starting with / resolve to the domain root and break unless you configured a base path

Quick validation:

  1. Open DevTools → Network.
  2. Click a 404 asset request.
  3. If it’s missing /<repo>/, you found the bug.

Generator-specific knobs:

  • Jekyll: _config.yml baseurl
  • Hugo: baseURL (and whether you generate relative links)
  • Next.js static export: base path / asset prefix settings

5) Check case-sensitivity and “pretty URL” assumptions #

GitHub Pages uses a case-sensitive filesystem. About.html and about.html are different files.

Also, “pretty URLs” (/docs/) typically require docs/index.html. If you only have docs.html, the directory form may 404.

Best Practices for a Useful 404 Page #

  1. Make it actionable, not just cute

    • explain what happened (“page not found”)
    • offer the easiest next steps (homepage, popular pages, search)
  2. Make it base-path-safe

    • prefer relative links in the 404 page (especially for project sites)
    • avoid hard-coding a domain unless you have a custom domain and it’s stable
  3. Keep it lightweight

    • a missing page should still load quickly and work without JavaScript
  4. Validate with a fake URL every release

    • a 10-second check prevents slow-moving “broken site” incidents
  5. Document your publish source

    • one line in your README (“publishes from gh-pages branch” or “publishes from Actions to /”) prevents future 404 regressions

A practical 404 page checklist (so it actually helps) #

A good 404 page does two jobs at the same time: it reduces bounce (UX) and it reduces repeated support/debugging (ops). When you’re writing the page, use this simple checklist:

  • Say what happened in one sentence (don’t blame the user).
  • Offer 2–5 “escape hatches”: home, docs index, popular pages, and a search entry point.
  • Make links resilient: prefer relative URLs so the page works for both / and /<repo>/ deployments.
  • Capture signal: log 404 hits in your analytics (path, referrer) so you can fix broken internal links and catch external link rot.
  • Keep it accessible: a clear H1, readable contrast, and no “only works with JS” navigation.

If you regularly publish new pages or rename sections, treating 404 traffic as a feedback loop is one of the fastest ways to keep a GitHub Pages site feeling “alive” without adding a backend.

Common Mistakes #

  1. Publishing the 404 file to the wrong place404.html must exist at the published root (the same place as index.html).
  2. Forgetting permalink: /404.html in Jekyll — without it, 404.md may not be served at /404.html.
  3. Assuming / is always the site root — project sites are under /<repo>/, which breaks absolute paths.
  4. Testing only on a dev server — some dev servers do not mirror production 404 behavior.
  5. Debugging without checking build output — Pages problems are often visible by simply inspecting the built folder.

Frequently Asked Questions #

Do I need 404.html or can I use 404.md? #

Both can work. GitHub Docs describes a Jekyll-friendly approach: create 404.md and set permalink: /404.html so the final output is served as 404.html. If you build with another generator, the safest approach is to ensure your output folder contains a real 404.html at the published root.

Why does my entire GitHub Pages site show a 404? #

Start with three checks:

  1. You’re using the right URL (user/org vs project site).
  2. Pages is publishing from the source you expect (branch/folder or GitHub Actions).
  3. The published root actually contains index.html.

Why do my CSS/JS assets 404 but HTML pages load? #

That almost always means base path mismatch. For project sites, your site is served under /<repo>/, so absolute paths like /assets/app.css don’t include the repo prefix. Fix it by configuring your generator’s base path, using relative URLs, or adjusting your build for project-site deployments.

How do I test my 404 page locally? #

Build the site, then confirm the build output contains 404.html at its root. To simulate production, serve the output folder with a simple static server and request a non-existent path. This catches base-path and file-placement bugs that dev servers can hide.

Does GitHub Pages support “SPA refresh” routing? #

GitHub Pages doesn’t provide configurable server-side rewrites. If your SPA uses history-based routing, a hard refresh on a deep route will request a file that doesn’t exist and return 404. The safest fixes are hash-based routing or deploying to a host that supports rewrite rules.

Conclusion #

To ship a reliable GitHub Pages 404 page, focus on two fundamentals:

  1. publish a correct 404.html at the site root, and
  2. make your URLs/base path match the type of Pages site you are deploying.

When something 404s unexpectedly, the fastest diagnosis is to check Pages settings and then inspect the actual build output—static hosting problems are usually visible on disk.

References #

  1. GitHub Docs: Creating a custom 404 page for your GitHub Pages site
  2. GitHub Docs: Troubleshooting 404 errors for GitHub Pages sites
  3. GitHub Docs: Configuring a publishing source for your GitHub Pages site
  4. GitHub Docs: About GitHub Pages
  5. Hugo Docs: Custom 404 page
  6. Jekyll Docs: Permalinks
  7. RFC 9110: HTTP Semantics (404 Not Found)

Frequently Asked Questions

Do I need 404.html or can I use 404.md on GitHub Pages?

Both work. GitHub Docs shows a Jekyll-friendly option—add a 404.md with permalink /404.html so Pages serves it as /404.html. If you build the site yourself (Hugo, Next.js export, etc.), ensure the build output contains a real 404.html at the published root.

Why does my entire GitHub Pages site show a 404?

A site-wide 404 usually means Pages isn’t publishing what you think. Confirm the Pages source (branch/folder or GitHub Actions), verify the published root contains index.html, and check the Actions build logs if you deploy via workflow.

Why do CSS/JS files 404 but HTML pages load?

That pattern almost always points to a wrong base path. Project sites live under /<repo>/, so absolute asset paths like /assets/app.css break unless you set your generator’s base URL/base path correctly or use relative URLs.

How do I test my 404 page locally?

Build the site and confirm the output folder contains 404.html at its root. Then serve that output folder with a local static server and request a non-existent path to confirm the 404 page renders.

Does GitHub Pages support “SPA refresh” routing?

GitHub Pages is static hosting and does not provide configurable server-side rewrites. For SPAs, prefer hash-based routing or a host that supports rewrites; otherwise you’ll need a 404-to-index workaround and careful testing.