Skip to content
Server & .htaccess

WordPress Posts Return 404 but the Homepage Works

Fix WordPress posts that 404 while the homepage loads: re-save permalinks, confirm .htaccess is writable, and check AllowOverride and mod_rewrite.

Published

If your homepage loads fine but every individual post and page returns a 404, the URL rewrite rules WordPress relies on are missing or broken in your .htaccess file. The fastest fix: go to Settings → Permalinks in wp-admin and click Save Changes without changing a thing. That forces WordPress to regenerate the rewrite block. If the 404s persist, your .htaccess isn’t writable by the web server, Apache is configured to ignore it, or you’re on Nginx (which doesn’t use .htaccess at all).

Why the homepage works but posts don’t

Your homepage works because a request to / is served straight from index.php at the document root. No rewriting required.

A request to /2026/my-post/ is different. That path is not a real folder on disk. For WordPress to answer it, Apache has to internally route the request to index.php, and WordPress then reads REQUEST_URI, matches it against the rewrite rules stored in the rewrite_rules option in the database, resolves the query vars (WP::parse_request()), and loads the right post.

The handoff from Apache to index.php is the part that breaks. It depends entirely on this block in .htaccess:

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

The two RewriteCond lines say: if the requested path is not a real file (!-f) and not a real directory (!-d), send it to index.php. Remove that block and Apache looks for a literal /2026/my-post/ directory, doesn’t find one, and returns its own 404 — before WordPress ever sees the request.

This is the part most guides skip. When you click Save on the Permalinks screen, WordPress calls flush_rewrite_rules(). On Apache with a writable .htaccess, that triggers save_mod_rewrite_rules(), which regenerates the block above via WP_Rewrite::mod_rewrite_rules() and writes it back using insert_with_markers().

insert_with_markers() only touches the lines between # BEGIN WordPress and # END WordPress. Everything outside those markers is left alone. Everything inside is discarded and rewritten. Newer WordPress versions even print the warning directly in the file:

Any changes to the directives between these markers will be overwritten.

So “WordPress overwrites my .htaccess” is true but narrow: it only rewrites its own marked block, and only when the file is writable. If the file is not writable, the save silently does nothing to disk. WordPress shows a yellow notice on the Permalinks screen telling you to paste the rules manually, and if you miss that notice, your rewrite block stays missing and every post keeps returning 404.

How to fix it, in order

1. Re-save your permalinks. Settings → Permalinks → Save Changes. No edits needed. This re-runs the flush and rewrites the block. It resolves the majority of cases, including stale rules after a migration.

2. Check that .htaccess exists and is writable. It lives in the same folder as wp-config.php. It’s a dotfile, so enable “show hidden files” in your FTP client or use:

ls -la /path/to/wordpress/.htaccess

If it’s missing, create it and paste the correct block. If your install lives in a subdirectory, RewriteBase and the final rewrite target must match that path — getting this wrong is the most common reason a hand-pasted block still 404s. Our .htaccess generator builds the exact block for your install path so you don’t have to guess.

3. Confirm Apache is allowed to read .htaccess. If the virtual host has AllowOverride None, Apache ignores the file completely regardless of its contents. You need AllowOverride All (or at least FileInfo) on the WordPress directory. This is in the server config, not .htaccess, so it requires server access:

<Directory /var/www/html>
    AllowOverride All
</Directory>

4. Confirm mod_rewrite is enabled. On Debian/Ubuntu:

sudo a2enmod rewrite
sudo systemctl restart apache2

5. On Nginx, there is no .htaccess. Rewrites go in the server block instead:

location / {
    try_files $uri $uri/ /index.php?$args;
}

Quick diagnostic: whose 404 is it?

Look at the 404 page. If it’s an unstyled Apache or host error page, Apache never reached WordPress — that’s a .htaccess or server-config problem (steps 2–4). If it’s your theme’s styled 404, WordPress is receiving the request but can’t match it — the rules in the database are stale, and re-saving permalinks (step 1) is the fix. This one distinction saves most people an hour of guessing.

What not to do

Don’t deactivate all your plugins first. A pure “posts 404, homepage works” symptom is a rewrite problem, not a plugin conflict. Disabling plugins is the reflex answer on forums and it almost never touches this.

Don’t chmod 777 your .htaccess. WordPress needs the file writable by the web server user, which is not the same thing as world-writable. 644 with the correct owner is fine and safe; 777 is a security hole that won’t fix the routing anyway.

Don’t paste custom rewrite rules inside the # BEGIN WordPress / # END WordPress markers. They’ll be wiped the next time anyone saves permalinks. Put custom directives above the # BEGIN WordPress line.

Don’t reinstall WordPress. The core files are fine. This is one missing block of text, not a corrupt install.

Don’t blame your theme. Permalink routing is WordPress core. A theme cannot cause it and cannot fix it.

Still stuck?

If you’ve re-saved permalinks, confirmed the block is present and writable, and set AllowOverride All, the remaining suspect is a security plugin or a host-level rule regenerating or locking .htaccess behind your back. Check the file’s modification timestamp right after a permalink save — if it didn’t change, something is blocking the write. Regenerate a clean block with the .htaccess generator, paste it in, and re-test with a hard refresh.

FAQ

Questions

Why do my WordPress posts return 404 but the homepage works?

The URL rewrite rules WordPress relies on are missing or broken in your .htaccess file. The homepage works because a request to / is served straight from index.php at the document root, while a post URL is not a real folder on disk and needs Apache to internally route the request to index.php.

How do I fix WordPress 404 errors on all pages?

Go to Settings then Permalinks in wp-admin and click Save Changes without editing anything. That forces WordPress to regenerate the rewrite block in .htaccess, and it resolves the majority of cases, including stale rules left behind after a site migration.

Where is the .htaccess file in WordPress?

It sits in the same folder as wp-config.php, at the root of your WordPress install. It is a dotfile, so it stays hidden until you turn on show hidden files in your FTP client, or list it directly with ls -la on the path to your install.

Why does WordPress keep overwriting my .htaccess file?

WordPress only rewrites the lines between the # BEGIN WordPress and # END WordPress markers, and only when the file is writable by the web server. Everything outside those markers is left alone, so put custom directives above the # BEGIN WordPress line to stop them being wiped.

Why are my permalinks still 404 after re-saving them?

Apache is likely ignoring your .htaccess entirely. If the virtual host is set to AllowOverride None, Apache skips the file regardless of its contents, so you need AllowOverride All, or at least FileInfo, on the WordPress directory. Also confirm mod_rewrite is enabled. Nginx does not use .htaccess at all.