You open the server, hit the URL of a deleted product, and instead of your site's 404 page, you see Apache's default page: "Not Found. The requested URL was not found on this server." Or worse, the site returns a 200 code for every wrong URL and Google indexes thousands of duplicate pages. The problem is usually in the ErrorDocument setting, not in the error file itself.
Before anything else, let's clear one thing up: a custom error page isn't just about appearance. If the status code that comes with it is wrong, no matter what you do, you'll get the opposite result. This article is exactly about that boundary.
Why a custom error page without the correct status code is useless
When a user reaches a URL that doesn't exist, the server must write HTTP/1.1 404 Not Found in the response header. If that code is 200, both the browser and Google's crawler think the page is healthy. You'll see the result in Search Console: tens of thousands of "indexed pages" that none of them have real content and eat up your site's crawl budget.
The right way is to introduce the error file with the ErrorDocument directive, not with a redirect. You'll see the difference between the two below.
The difference between ErrorDocument and Redirect 404
This is where people make a mistake: in .htaccess they write Redirect 404 /404.html and think they're done. This directive creates a 302 redirect; meaning the server first sends a 302 code, then the browser goes to fetch /404.html with a 200 code. Google interprets the wrong URL as "temporarily moved" and keeps it in the indexing queue. The sign of this is that in the coverage report, your 404 URLs appear under the "Redirected" category, not "Not found."
The most correct form is this line:
ErrorDocument 404 /errors/404.html
ErrorDocument 500 /errors/500.html
ErrorDocument 403 /errors/403.html
The path must start from the domain root and begin with a slash. If you write ErrorDocument 404 errors/404.html without the leading slash, Apache interprets it as relative and the result differs depending on the depth of the requested URL. This small mistake causes the error page to work only at the site root and the default page to return again on deeper paths.
Content that brings the user back, not a page that abandons them
A good 404 page does three things and nothing more. First, it says in one sentence what happened. Second, it puts three or four real paths in front of them. Third, it offers a way to search or contact. The rest is decoration.
- The page title should be exactly what the user expects: "The page you were looking for was not found."
- Links to the homepage, main categories, and if you have a store, the shopping cart.
- A simple search form connected to the site's internal results.
- If the URL looks like a deleted product, a link to the closest available product.
What actually works in practice is a smart suggestion based on the URL. If the requested URL is very similar to one of the existing paths, suggest that one. A small PHP script with similar_text() or levenshtein() over the list of site URLs, in most cases, gets the user to the right destination. The cost is that you have to keep the URL list somewhere and keep it updated; it's worth it for sites under a few thousand pages, not for small sites.
Don't underestimate the 500 page
The 500 page is fundamentally different from 404: the user isn't at fault, you are. Here you shouldn't show any technical details. The message Fatal error: Uncaught Error: Call to undefined function on the page both damages technical credibility and opens a path for abuse.
In php.ini or with ini_set, set these two values:
display_errors = Off
log_errors = On
error_log = /home/USERNAME/logs/php_errors.log
Replace the log path with your own hosting's actual path. If you're not sure which php.ini directive takes effect on your hosting, the php.ini directives reference lists each directive with its default value and practical effect.
A point that's rarely mentioned: ErrorDocument 500 only works when Apache itself generates the error. If PHP dies with a fatal error and sends incomplete output, Apache may treat it as a healthy response and your custom page won't be displayed. To cover this case, you must also register an error handler at the script level or use auto_prepend_file.
Configuring in htaccess or in the hosting panel?
Both ways exist and choosing between them depends on your situation.
| Method | Advantage | Limitation |
|---|---|---|
ErrorDocument in .htaccess | Fast, portable with the file, no admin access needed | Must be repeated for each domain separately; doesn't work if AllowOverride is restricted |
| Configuration in VirtualHost | At the server level, across all domains at once | Requires root access or server management panel |
| Configuration in hosting panel | Without touching files, suitable for non-technical users | Less flexible, sometimes only accepts a fixed path |
If you're working on Linux hosting and have access to the files, I'd choose .htaccess. The reason is simple: with one file you can keep the configuration in git and move it between test and production environments. If you have dozens of domains on one server and want them all to have a shared error page, a dedicated server with configuration at the VirtualHost level is less of a headache.
A practical limitation I run into a lot: when the site is behind a CDN, your custom error page may never reach the user. Some CDNs replace 404 and 500 responses with their own page. If after correct configuration you still see a strange page, first check the response header with curl -I https://example.com/not-found and see what code your server is sending.
Verifying that it actually works
Once you've written the configuration, don't guess. Test it:
curl -sI https://example.com/this-page-does-not-exist | head -n 1
curl -s https://example.com/this-page-does-not-exist | grep -i "page you were looking for"
The first line should return HTTP/2 404. If you see HTTP/2 200, it means you have a redirect or rewrite somewhere that changes the code. The most common culprit is a rule in .htaccess that sends everything to index.php, and that one returns a 200 code without checking. If you use a framework, this is the default behavior of some routers and must be fixed in the code itself, not in htaccess.
To see what the server is saying at the network layer and that the problem isn't DNS, the DNS and network lookup tool is the fastest way. And if you want to make sure the 500 error is coming from a PHP extension and not the config, compare the list of active extensions with the PHP extensions check guide.
Three mistakes that keep repeating
- Linking from the 404 page to the 404 page. If your site menu links to URLs that are themselves 404s, the user falls into a loop. Remove the menu on the error page or keep only healthy links.
- Putting
noindexon the 500 page. The 500 page shouldn't be indexed, but this is done with the status code, not with a meta tag. If the 500 code is correct, Google won't index it on its own. - Forgetting subdomains.
ErrorDocumentin the root.htaccessdoesn't affectblog.example.com. Each subdomain needs its own file.
One point about the 404 page and SEO: if you've deliberately deleted a page and know it has backlinks, a 404 is wrong. There you should give a 301. A 404 is for URLs that truly shouldn't exist.
Frequently asked questions
Does a custom error page affect SEO ranking?
The page itself has no direct effect; what has an effect is the status code. If your 404 page returns a 404 code, Google removes it from the index and there's no problem. If it returns a 200 code, contentless pages accumulate in the index and crawl budget is wasted.
Why do I still see Apache's default page after configuring ErrorDocument?
There are three common reasons: the file path doesn't start with a slash, the error file wasn't uploaded to the right path, or AllowOverride in Apache settings doesn't allow reading ErrorDocument from .htaccess. With a simple test file and curl -I, you can rule out all three in a few minutes.
How do I build a 500 page when the site itself won't come up?
The error file must be pure HTML with no dependencies; not PHP, not a database call, not an external CSS file. If your 500 page depends on the very thing that's broken, it will never be displayed. Create a simple HTML file with inline styling and introduce that.
Can I have a different error page for each domain?
Yes. ErrorDocument in .htaccess applies to each domain separately, so you just need to place the error file specific to that domain in its root. Just remember that subdomains with separate DocumentRoots need separate files.
Right now, open a non-existent URL in your browser and check the response header with curl -I. If the first number isn't 404, stop everything else and fix that first.