Home >Web >.htaccess and Apache >Error pages, file types and encoding

Error pages, file types and encoding

A custom 404 page instead of a terse server message, the right type for new file formats and the encoding of text files. How to set them in .htaccess and why an error page given as a full address does not report the error to search engines.

Custom error pages: ErrorDocument

ErrorDocument 404 /404.php
ErrorDocument 403 /403.html
ErrorDocument 503 "The site is under maintenance, please try again in a moment."

What follows the error code decides the behavior:

  • a path with a slash at the beginning – Apache serves the page instead of the error and the status code stays 404 (or 403). This is the right variant;
  • a full address (https://www.example.com/404.html) – Apache redirects the browser with the 302 code. A search engine never learns about the 404 error, it sees a redirect and then a page with the code 200;
  • anything else is printed as text. ErrorDocument 404 404.html (without the slash) thus shows only the text “404.html”. Apache sends the text with the ISO-8859-1 encoding, so characters outside it (for example letters with diacritics from other languages) would be displayed incorrectly.

When the page given in ErrorDocument is missing, the visitor gets the default Apache message with a note that another 404 error occurred while looking for the error page.

A 404 page in PHP

Apache calls the script with the code 404, $_SERVER['REQUEST_URI'] contains the original address the visitor was looking for and $_SERVER['REDIRECT_STATUS'] the error code:

<?php
http_response_code(404);
$address = $_SERVER['REQUEST_URI'];
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="robots" content="noindex">
<title>Page not found</title>
</head>
<body>
<h1>Page not found</h1>
<p>The address <?php echo htmlspecialchars($address, ENT_QUOTES, 'UTF-8'); ?> does not exist on this site.</p>
<p><a href="/">Go to the home page</a></p>
</body>
</html>

http_response_code() only confirms the 404 code – the same page can then also be used from the code of the site itself. The address from the request must be printed through htmlspecialchars(), otherwise HTML and JavaScript could be slipped into the page (see XSS and safe output).

ErrorDocument applies only to errors returned by Apache (a file that does not exist, a denied folder). When the site sends all addresses to index.php and the script itself responds with 404 or 500, ErrorDocument is not used – the script must print the error page. A fatal PHP error before the first output returns the code 500 with a blank page when displaying errors is turned off (as it should be on a live site). If the script has already printed something, the code even stays 200 – the headers have already been sent. How to catch PHP errors and write them to a log is described in Error messages on a live site.

File type: AddType

Apache determines the file type in the Content-Type header from the extension using its own list. Older versions do not know newer formats and send the file without a type. The browser then guesses the type, and with the X-Content-Type-Options: nosniff header it may not use the file at all. The AddType directive adds the type:

AddType image/avif .avif
AddType application/manifest+json .webmanifest
AddType text/javascript .mjs

Apache 2.4.41 from 2019 knows none of these three extensions, Apache 2.4.68 knows .avif and .mjs, but still not .webmanifest (as of October 2026). Which type the server sends is shown by curl -I or the Network tab in the developer tools.

The browser displays PDF files and images. If they should be downloaded instead, the Content-Disposition header helps:

<FilesMatch "\.(pdf|zip)$">
  Header set Content-Disposition attachment
</FilesMatch>

Encoding: AddDefaultCharset and AddCharset

AddDefaultCharset UTF-8
AddCharset UTF-8 .css .js .txt

AddDefaultCharset adds the encoding to HTML and text files (the types text/html and text/plain) that do not have one specified. AddCharset adds it to files with the given extensions – useful for styles and scripts with characters outside ASCII. It has no effect on pages from PHP, PHP sends the encoding itself (the default_charset setting, UTF-8 by default).

The default file of a folder: DirectoryIndex

DirectoryIndex index.php index.html

For the address of a folder (/, /articles/), Apache serves the first file from the list that exists in the folder. The order matters: when both files are in the folder, index.php wins.

Related articles:
Error messages on a live site
Security headers and HTTPS
XSS and safe output
mod_rewrite and clean URLs