GitHub Pages 404 Page
How to add a custom 404 page on GitHub Pages and troubleshoot common 404 errors for user/org sites and project sites.
On GitHub Pages, “404 page” usually means one of two problems:
- You want a custom 404 page so missing URLs show something helpful (not the default).
- 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 from404.mdusingpermalink: /404.html; other generators must output404.htmlat the published root. - Debug from Pages settings + build output: confirm the Pages source and verify
index.html+404.htmlexist 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 type | URL form | Public base path | Most common 404 pitfall |
|---|---|---|---|
| User/Org site | https://<user>.github.io/ | / | Publishing from the wrong branch/folder so index.html never reaches the published root |
| Project site | https://<user>.github.io/<repo>/ | /<repo>/ | Absolute links and asset paths (/assets/...) don’t include the repo base path |
| Custom domain | https://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 symptom | Most likely cause | First thing to check |
|---|---|---|---|
| Whole site | Every URL returns 404 | Pages source wrong / output folder wrong | Settings → Pages + published root contains index.html |
| One page | Only one path 404s | File not generated at that path | Build output: does the expected file exist? |
| CSS/JS/images | HTML loads, assets fail | Base path mismatch (/<repo>/) | Network tab: does the URL include the repo base path? |
| Custom domain | Domain shows default Pages 404 | DNS/custom domain not configured | Custom 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.htmlfile 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.mdwith YAML front matter andpermalink: /404.html. - This lets you write Markdown but still produce the correct output path (
404.htmlat 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.htmlat its root.
Hugo (example mental model):
- add a template at
layouts/404.html - run
hugo - verify
public/404.htmlexists 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:
- Wait for the Pages build to finish (or check GitHub Actions logs).
- 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/
- User/org site:
- 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:
- Build locally.
- Inspect the folder you configured Pages to publish.
- Ensure
index.htmlexists 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:
- Open DevTools → Network.
- Click a 404 asset request.
- If it’s missing
/<repo>/, you found the bug.
Generator-specific knobs:
- Jekyll:
_config.ymlbaseurl - 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 #
Make it actionable, not just cute
- explain what happened (“page not found”)
- offer the easiest next steps (homepage, popular pages, search)
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
Keep it lightweight
- a missing page should still load quickly and work without JavaScript
Validate with a fake URL every release
- a 10-second check prevents slow-moving “broken site” incidents
Document your publish source
- one line in your README (“publishes from
gh-pagesbranch” or “publishes from Actions to/”) prevents future 404 regressions
- one line in your README (“publishes from
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 #
- Publishing the 404 file to the wrong place —
404.htmlmust exist at the published root (the same place asindex.html). - Forgetting
permalink: /404.htmlin Jekyll — without it,404.mdmay not be served at/404.html. - Assuming
/is always the site root — project sites are under/<repo>/, which breaks absolute paths. - Testing only on a dev server — some dev servers do not mirror production 404 behavior.
- 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:
- You’re using the right URL (user/org vs project site).
- Pages is publishing from the source you expect (branch/folder or GitHub Actions).
- 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:
- publish a correct
404.htmlat the site root, and - 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 #
- GitHub Docs: Creating a custom 404 page for your GitHub Pages site
- GitHub Docs: Troubleshooting 404 errors for GitHub Pages sites
- GitHub Docs: Configuring a publishing source for your GitHub Pages site
- GitHub Docs: About GitHub Pages
- Hugo Docs: Custom 404 page
- Jekyll Docs: Permalinks
- RFC 9110: HTTP Semantics (404 Not Found)