Fixing WordPress 404 Not Found Errors When Using Permalinks

I was setting up a new install of WordPress recently, running locally on my laptop. After a while I started getting “404 Not Found” errors on opening any individual page on my new WordPress site: I couldn’t open posts or static pages and had no access on tag or category links.

My goal was to create a “sandbox” where I could write drafts of new posts, experiment with themes and plugins – all kinds of things that I might not want to do on a live site. Everything worked well initially, but then I started getting errors like these:

Not Found
The requested URL /blog/2018/06/a-second-post/ was not found on this server.

The main page opened and displayed fine, the admin screen was OK, but anything else got a “404 Not Found” error.

I stared at the permalinks to the pages for a long time; they all looked like the one above and seemed to be correct. I had played around with the Permalink settings in WordPress but eventually left them at “Month and Name”, which is similar to what I use on the production site.

I did an internet search for the problem and found there are quite a few sites with posts about this “404 Not Found” problem. But all of them focus on problems with the .htaccess file. The general advice for 404 errors was to go to Permalink settings and simply click “Save” there: this will rewrite the .htaccess file and fix the problem in many cases. If you’re a developer running a WAMP or MAMP site, there’s also a recommendation to check that the “mod_rewrite” module is enabled there.

I tried all that, but things still weren’t working right.

The .htaccess file on the new site looked good:

# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /blog/
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /blog/index.php [L]
</IfModule>>

# END WordPress

A .htaccess file is written whenever the permalinks configuration is changed in the WordPress dashboard. So this must have been written when I was experimenting with the Permalink format. As far as I could see, these rewrite rules looked OK.

I’ll say at this point that I created this setup by copying a bunch of config files from the default Apache install under /etc/apache2 into a separate directory structure. This allowed me to run a separate server under a port other than port 80. It’s not difficult to set this up, and it’s worked for me before.

Nuke the Site and Start Over

Having found nothing notable after looking at .htaccess config and taking a glance at the Apache server logs, and post data in the database, I decided to delete the site altogether and started over, setting up Apache and then installing WordPress all over again. This time I put the WordPress files at the document root of the site, no more “/blog” folder, but I kept the port 7000 configuration. And everything worked just fine: I created a couple of posts and found that I could access them and that they would display correctly in WordPress.

At least I was finally up and running again. But I was still very curious about what could have gone so badly wrong the first time. Then I remembered that I had played around with different permalink formats after the first install. I had not looked at permalink config this second time.

Reproducing the Problem

I noticed that when I hovered over a link to a post, tag, or category in the new install, the link address had an explicit “/index.php” in it.

A visit to the Permalinks Settings page revealed that the selected configuration was unusual: it wasn’t set to “Plain” or “Month and Name”, but to “Custom Structure”, and the structure was specified as this:

/index.php/%year%/%monthnum%/%day%/%postname%/

Notice this “/index.php” at the front of the path. This was configuration that was set up with by WordPress install; I hadn’t changed it up to this point.

I tried setting the permalink setting to Month and Name as I had before, and bam! Posts and pages no longer displayed, showing the same “404 Not Found” error as before. I clicked the radio button to choose “Custom Structure” again, and the posts and pages still didn’t work, but a careful look at the format of the common structure revealed that it no longer had the “/index.php” prefix on it.

When I restored the “/index.php” prefix to the permalink’s custom structure, the posts and pages were found again and would display correctly. But the “/index.php” in the post URL was ugly and not in character with the “nice” permalink format.

Digging A Little Deeper: mod_rewrite

Permalinks with “/index.php” in them are called PATHINFO permalinks. According to the WordPress Permalink documentation, the PATHINFO style of permalink will be configured if the Apache rewrite module is not enabled. But as far as I knew, it was enabled.

Is there an issue with the Apache mod_rewrite module not being loaded correctly? I first checked the httpd.conf configuration file; the mod_rewrite module is configured there, the following line was uncommented:

LoadModule rewrite_module libexec/apache2/mod_rewrite.so

Double checking, I tried some command line options on the apachectl command (which starts the apache server) to debug module configuration:

$ apachectl -k start -d /Users/mydev/wpdev -f /Users/mydev/wpdev/conf/httpd.conf -t -D DUMP_RUN_CFG -D DUMP_MODULES -D DUMP_INCLUDES

The dump from this command included the following output:

Mutex rewrite-map: using_defaults
rewrite_module (shared)

Looks good.

And just to triple check, I listed all loaded modules in a separate .php page using a little code like this:

<?php
foreach ( apache_get_modules() as $mod) {
  print $mod . "<br/>";
}
?>

The “mod_rewrite” module was listed in the output (the loaded modules are also listed in the output from a call to “phpinfo();”)

The Apache mod_rewrite rule seemed to be loading, but I became suspicious that it was not working. I tried changing the rewrite rule in .htaccess, misspelling “RewriteBase” as “RwriteBase” – and the site worked OK, no errors in the error_log file. I would think that the mod_rewrite code would object to this syntax error.

This led to a lot of digging around in the Apache mod_rewrite documentation. I found that a logging mode can be set in Apache configuration to help debug issues with modules. Finding the “LogLevel” directive in the httpd.conf file, I changed it to this:

#
# LogLevel: Control the number of messages logged to the error_log.
# Possible values include: debug, info, notice, warn, error, crit,
# alert, emerg.
#
LogLevel debug rewrite:trace3

I restarted the Apache server and browsed to a page. About half a dozen entries were written to the error_log file listing some verbose information about modules names like “authz_core” and “mod_hfs_apple2”, but there was no mention of “mod_rewrite”. And there was a log entry saying that the requested page was not found:

AH00128: File does not exist: /Users/mydev/wpdev/docroot/2018/06/tools-for-the-curious/, referer: http://localhost:7000/

I tried a few internet searches again, this time using terms like “WordPress PATHINFO 404 File Not Found mod_rewrite”, and after a few minutes, I found this post that resolved the problem.

The cause: in configuring the Apache server, I had missed changing “AllowOverride None” to “AllowOverride All” in the <Directory> directive in the httpd.conf file! A small error like this caused the rewrite module to fail silently. A comment in the httpd.conf file does say “AllowOverride controls what directives may be placed in .htaccess files”, but doesn’t mention which directives might be affected.

With this configuration changed and a restart of the Apache server, I began to see messages logged for the “mod_rewrite” module in the “error_log” file:

[Thu Jun 07 17:32:54.609357 2018] [rewrite:trace3] [pid 35286] mod_rewrite.c(480): [client ::1:56861] ::1 - - [perdir /Users/mydev/wpdev/docroot/] add path info postfix: /Users/mydev/wpdev/docroot/2018 -> /Users/mydev/wpdev/docroot/2018/06/tools-for-the-curious/, referer: http://localhost:7000/

With the “AllowOverride” option changed to “All”, I could choose a permalink format other than “Custom Structure”, like the “Month and Name” structure that I wanted, and the posts, tags, and categories pages were found and displayed correctly. No more 404 errors! Hovering over post links in the home page, the link addresses no longer contained “/index.php” in them; they looked like clean, maintainable permalinks again.

Applying the Principle of Least Privilege

The documentation for the “AllowOverride” directive does say that “AllowOverride All” enables three kinds of access for Apache modules: AuthConfig, FileInfo, Indexes, and Limit. With a little experimentation, I found that only “FileInfo” and “Limit” options are needed for the Apache rewrite module to work. AuthConfig enables various modes of login like HTTP basic authentication, which WordPress does not use (it uses form based authentication in PHP). Indexes might be useful in some cases (see the Apache server documentation for more info on this). As I believe it’s good security to always grant just the minimum privileges necessary for code to work, I configured my local server to use just the two required options.

TL;DR

I was getting “404 Not Found” errors when trying to access posts and pages on my newly installed WordPress site. The site was set up on a local machine, not an internet hosting site, and was configured to use port 7000 rather than the default port 80. Something about this configuration caused WordPress to install with a “custom” permalink structure that included “/index.php” in it.

In experimenting with permalinks, I had changed it to a different format then changed them back to custom structure, but the “/index.php” was no longer set in the default value for the custom structure. An initial fix, restoring “index.php” to the custom permalink structure, allowed the posts to work again.

The final resolution, however, was to update the “AllowOverride” directive in the httpd.conf configuration from “AllowOverride None” to “AllowOverride All”. A more minimal setting of “AllowOverride FileInfo Limit” also worked for me.

I found along the way that setting LogLevel configuration to “debug” in httpd.conf is a useful tool for debugging issues with Apache modules; verbose output is written to the Apache “error_log” file which can help to track down obscure problems.

References

Documentation on the AllowOverride directive:
core – Apache HTTP Server Version 2.4

Documentation on the FileInfo and Limit settings for AllowOverride, which
specifically control whether mod_rewrite is active in the Apache server:
Override Class Index for .htaccess – Apache HTTP Server Version 2.4

Introductory documentation on Apache mod_rewrite:
Apache mod_rewrite Introduction – Apache HTTP Server Version 2.4

Documentation on Apache module logging is available in the main mod_rewrite page (logging is a general feature of the Apache server, not specific to mod_rewrite):
mod_rewrite – Apache HTTP Server Version 2.4

The general advice about re-saving .htaccess file to fix the “404 Not Found” error:
How to Fix WordPress Posts Returning 404 Error

The post that led me to the “AllowOverride” fix:
apache mod_rewrite is not working or not enabled

Documentation for Permalinks on the WordPress site:
Using Permalinks << WordPress Codex

Versions

WordPress 4.9.6
Apache/2.4.28

Add a Comment