Fixing Apache errors: 403, 404, 500, 502, 503 and startup failures
The page a visitor sees ("Forbidden", "Internal Server Error") rarely says why. The error log almost always does, and since httpd 2.4 most messages carry an AH number you can search for. This page lists the messages you are most likely to meet, what each one means, and the fix. Every message quoted here was checked against the httpd 2.4 source code or reproduced on a test server running 2.4.69, the current release as of November 2026; paths and numbers in your log will differ.
Find and read the error log
| System | Main error log | Service name |
|---|---|---|
| Debian, Ubuntu | /var/log/apache2/error.log | apache2 |
| RHEL, AlmaLinux, Rocky, Fedora | /var/log/httpd/error_log | httpd |
Virtual hosts often have their own ErrorLog; apachectl -S shows which file each site uses. Watch the log while you reproduce the problem:
sudo tail -f /var/log/apache2/error.log # RHEL: /var/log/httpd/error_log
sudo journalctl -u apache2 -n 50 --no-pager # RHEL: -u httpd; shows errors when the service will not start
A typical line looks like this:
[Wed Nov 04 02:10:28.799290 2026] [authz_core:error] [pid 2212119:tid 2212122] [client 203.0.113.7:45168] AH01630: client denied by server configuration: /var/www/example/private/
Read it left to right: time, module:level, process and thread, client address, then the message with its AH code. Some messages, such as "File does not exist", are logged at info level in 2.4 and stay hidden under the default LogLevel warn. To see them temporarily, set LogLevel warn core:info in the virtual host and reload. More in logging.
Always test after editing and before reloading:
sudo apachectl configtest
Syntax OK
sudo systemctl reload apache2 # RHEL/Fedora: sudo systemctl reload httpd
Startup problems
AH00558: could not reliably determine the server's name
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 127.0.1.1. Set the 'ServerName' directive globally to suppress this message
A warning, not an error: Apache still starts. It appears when there is no ServerName at server level. Set one:
# Debian/Ubuntu
echo "ServerName www.example.com" | sudo tee /etc/apache2/conf-available/servername.conf
sudo a2enconf servername
# RHEL/Fedora: add "ServerName www.example.com:80" to /etc/httpd/conf/httpd.conf
sudo apachectl configtest
Address already in use
(98)Address already in use: AH00072: make_sock: could not bind to address 0.0.0.0:80
no listening sockets available, shutting down
AH00015: Unable to open logs
Another program already listens on that port: a second Apache instance, nginx, a container publishing port 80, or an old httpd that did not stop. The last line is a side effect; ignore it until the port is free. Find the owner:
sudo ss -ltnp 'sport = :80'
Stop or reconfigure that program, or change Apache's Listen line (/etc/apache2/ports.conf on Debian, /etc/httpd/conf/httpd.conf on RHEL). Two identical Listen lines give a different, clearer error at configtest: "Cannot define multiple Listeners on the same IP:port".
SELinux refuses a new port (RHEL, Fedora)
If httpd fails to start after you add a port such as Listen 9876, and /var/log/audit/audit.log shows avc: denied { name_bind }, the port is not labelled for web servers. List and add it:
sudo semanage port -l | grep -w http_port_t
sudo semanage port -a -t http_port_t -p tcp 9876
403 Forbidden
Four different causes, each with its own message.
AH01630: client denied by server configuration: /path- A
Requirerule refused the request. Most often the site lives outside/var/www: both Debian and RHEL ship<Directory /> Require all deniedand only grant/var/www(Debian also/usr/share). Add a block for your directory:
Also look for old<Directory "/srv/example/public"> Require all granted </Directory>Order deny,allow/Deny from alllines (2.2 syntax) mixed in. See access control. AH01276: Cannot serve directory /path/: No matching DirectoryIndex (index.html) found, and server-generated directory index forbidden by Options directive- A directory URL with no index file, and listings are off. Upload
index.html(orindex.phpand add it toDirectoryIndex). TurningOptions +Indexeson is rarely what you want. AH00035: access to /page denied (filesystem path '/path') because search permissions are missing on a component of the path- The Apache user (
www-dataon Debian,apacheon RHEL) cannot enter one of the parent directories. A related message isAH00529: … pcfg_openfile: unable to check htaccess file, ensure it is readable and that '/path/' is executable. Show every directory's permissions on the way down:
Home directories are often mode 700 or 750, which blocks the web server.namei -l /srv/example/public/index.html sudo chmod o+x /srv /srv/example # directories need x to be entered sudo chmod o+r /srv/example/public/index.html - Permissions look right, but still 403 (RHEL, Fedora)
- SELinux. Files created or moved outside
/var/wwwget the wrong label, and files moved withmvfrom a home directory keepuser_home_t. Check and fix:
For directories the application must write to (uploads, cache), usels -Z /srv/example/public/index.html sudo ausearch -m AVC -ts recent sudo semanage fcontext -a -t httpd_sys_content_t "/srv/example(/.*)?" sudo restorecon -R -v /srv/examplehttpd_sys_rw_content_ton just those paths. Do not switch SELinux off to "fix" this.
404 Not Found
With core:info logging you will see AH00128: File does not exist: /var/www/html/missing.html. Compare that path with where the file really is. Typical causes:
- The request went to a different virtual host than you think, so a different
DocumentRootapplied. Check withapachectl -S, and make sure theServerNameorServerAliasmatches the host name in the URL (see virtual hosts). - Case:
Photo.JPGandphoto.jpgare different files on Linux. - A rewrite rule maps to the wrong place; debug with
LogLevel rewrite:trace3(mod_rewrite). - An
Aliaswithout a matching<Directory>grant gives 403, not 404, so check which code you actually get.
500 Internal Server Error
/var/www/html/.htaccess: Invalid command 'RewriteEngine', perhaps misspelled or defined by a module not included in the server configuration- The .htaccess uses a module that is not loaded. On Debian/Ubuntu:
sudo a2enmod rewriteand reload. The same pattern applies toHeader(headers),Expires…(expires) and others. If the message ends in "not allowed here" instead, the module is loaded butAllowOverrideforbids that directive in .htaccess. AH00124: Request exceeded the limit of 10 internal redirects due to probable configuration error- A rewrite loop: a rule rewrites to a URL that matches the same rule again. Add a condition that skips existing files (
RewriteCond %{REQUEST_FILENAME} !-f) or use the[END]flag. For a front controller,FallbackResource /index.phpis simpler. End of script output before headers: script.cgi- A CGI script died or printed nothing before its headers. Run it by hand as the web user, check the
#!line, the execute bit, and Windows line endings. AH01071: Got error 'Primary script unknown'- PHP-FPM received a path to a PHP file that does not exist on its side. Check that the path Apache sends matches where PHP-FPM sees the files (containers and chroots often differ) and that the file exists. See PHP with Apache.
502, 503 and 504 behind mod_proxy
These codes mean Apache itself is fine but the application behind it is not answering properly.
- 503: backend not reachable
Nothing listens on that address. Check the application and test it directly from the server:(111)Connection refused: AH00957: http: attempt to connect to 127.0.0.1:8080 (127.0.0.1:8080) failed AH01114: HTTP: failed to make connection to backend: 127.0.0.1curl -I http://127.0.0.1:8080/andsudo ss -ltn. For PHP-FPM over a socket the message isAH02454: FCGI: attempt to connect to Unix domain socket /run/php/php8.4-fpm.sock (localhost:8000) failed: the socket path inSetHandleris wrong, PHP-FPM is stopped, or the socket's permissions do not let Apache's user connect.- 503 with "(13)Permission denied" on RHEL
- SELinux blocks httpd from opening network connections. Allow it, persistently:
(sudo setsebool -P httpd_can_network_connect 1httpd_can_network_connect_dbis the narrower switch for database ports only.) - 502: backend too slow or broke the connection
With mod_proxy_http, a backend that does not answer within the proxy timeout gives 502, not 504. The timeout defaults to(70007)The timeout specified has expired: AH01102: error reading status line from remote server 127.0.0.1:8080 AH00898: Error reading from remote server returned by /app/reportTimeout(60 seconds). Make the slow request faster if you can; otherwise raise it only for that path, for exampleProxyPass "/app/" "http://127.0.0.1:8080/" timeout=180, or globally withProxyTimeout. The same message without the timeout part means the backend closed the connection or sent something that is not HTTP: check the application's own log.- 504: gateway timeout
- mod_proxy_fcgi (PHP-FPM) returns 504 when PHP takes longer than the timeout, logging
AH01075: Error dispatching request to …. Check PHP's own limits (max_execution_time,request_terminate_timeout) as well as Apache's.
More about timeouts and headers is in the reverse proxy guide. If errors only appear under load, look at busy workers in server-status: the log line AH00484: server reached MaxRequestWorkers setting means requests are queueing.
A quick checklist
- Reproduce the error while running
tail -fon the right error log. - Search the AH code; it identifies the exact place in httpd that logged it.
apachectl configtestandapachectl -Safter every change.- On RHEL, check
ausearch -m AVCbefore changing file permissions. - For proxy errors, test the backend directly from the server before touching Apache.
- Turn extra logging (
core:info,rewrite:trace3) off again when done.
Sources
- Log Files (error log format), Apache HTTP Server 2.4 documentation
- Core features (LogLevel, ServerName, Timeout, AllowOverride), Apache HTTP Server 2.4 documentation
- mod_authz_core, Apache HTTP Server 2.4 documentation
- mod_proxy (ProxyPass timeout, ProxyTimeout), Apache HTTP Server 2.4 documentation
- Apache httpd 2.4.x source (server/util.c, listen.c, request.c, config.c, core.c, util_script.c; modules/aaa, generators, proxy), GitHub mirror
- Debian apache2 configuration (apache2.conf, ports.conf), sources.debian.org
- Fedora httpd.conf, src.fedoraproject.org
- Using SELinux (labels, restorecon, semanage port, troubleshooting), Red Hat Enterprise Linux 9 documentation
- The Apache HTTP Server and SELinux: Booleans, Red Hat Enterprise Linux 7 documentation
- The Apache HTTP Server and SELinux: Types, Red Hat Enterprise Linux 7 documentation
- PHP-FPM configuration (request_terminate_timeout), PHP Manual
- namei(1), Linux manual pages
- ss(8), Linux manual pages