You publish a new post or visit a URL you know is live, and the browser shows "404 Not Found" instead of the page. The URL is right. The post is published. Other pages on the same site load fine. Sometimes only one URL 404s, sometimes a whole category does, and sometimes every pretty permalink breaks at once while yoursite.nl/?p=123 still works.
What a 404 actually means here
RFC 9110 §15.5.5 defines 404 as: "the origin server did not find a current representation for the target resource or is not willing to disclose that one exists." Plain reading: the web server looked at the URL, asked WordPress what to do with it, and WordPress said it had nothing to serve for that URL.
On a WordPress site, that almost always means the URL-to-post translation is broken. WordPress uses rewrite rules to turn a pretty URL like /blog/hello-world/ into a query for the post with the slug hello-world. Those rules are stored in the WordPress database and, on Apache, written out to .htaccess. When that chain breaks, WordPress cannot map the URL to a post, and the request falls through to the 404 template. The post still exists. The link to it is what is gone.
One quick test separates this category of 404 from every other cause. Visit yoursite.nl/?p=123 (replace 123 with the actual post ID from the admin). If that URL loads the post and the pretty URL does not, the content is fine and the rewrite layer is the problem. If the ?p=123 version also 404s, the post itself is gone or unpublished, which is a completely different fix. And if visiting any URL on the site shows a different error entirely (like a blank page or a generic crash message), you are probably looking at the "There has been a critical error on this website" message or a 403 Forbidden, not a 404.
Common causes, ordered by likelihood
1. The rewrite rules are stale and need to be regenerated
This is the single most common cause. WordPress caches its rewrite rules in the database option rewrite_rules. That cache is regenerated when WordPress calls flush_rewrite_rules(), which happens on plugin activation, theme switching, permalink setting changes, and a few other events. Between those events, the rules are whatever was written the last time something called that function. If a plugin registered a new rewrite rule and forgot to flush, or if a migration left stale rules from the old site, the pretty URL no longer resolves. Symptom: the admin shows the post at its correct slug, the ?p= version works, the pretty URL 404s.
2. The post or page was moved, unpublished, or its slug changed
The next most common cause, and the one readers feel silliest about once they find it. The permalink the visitor is hitting does not actually exist on the site anymore. The slug was edited on a revision. The post was moved back to draft. The page was trashed. A URL that used to be live has no matching post, so WordPress returns a real 404, which is the correct behavior. The ?p=123 test catches this: if even the ?p= URL 404s, the post is gone from the site as far as WordPress is concerned.
3. The .htaccess rewrite rules are missing or broken (Apache only)
If you are on Apache or LiteSpeed, every pretty URL on your site goes through the WordPress block in .htaccess. If that block is missing, or the file does not exist at all, every non-homepage URL returns 404 while the homepage still works. I see this most often after a migration that copied files but skipped hidden files, after a hardening tool overwrote .htaccess with a stricter block that no longer routes to index.php, or after a chmod run made the file unreadable. nginx does not read .htaccess, so skip this cause on nginx.
A broken .htaccess is also the most common cause of the ERR_TOO_MANY_REDIRECTS error and a frequent contributor to the 500 Internal Server Error. If you are seeing 404s alongside either of those on the same site, fix .htaccess first and the other two often resolve themselves.
4. The server-level rewrite is wrong (nginx or Apache)
On nginx, WordPress needs try_files $uri $uri/ /index.php?$args; in its location / block. Without it, nginx sees that /blog/hello-world/ is not a file on disk and returns 404 before PHP is ever involved. On Apache, the parallel problem is mod_rewrite being disabled at the module level, or AllowOverride being set to None so Apache ignores .htaccess entirely. Symptom on both: every pretty URL 404s, the ?p=123 version works, and even re-saving permalinks does not fix it because the rewrite layer above WordPress is the problem.
5. Multisite subdirectory rewrite rules are missing
WordPress Multisite in subdirectory mode needs extra rewrite rules that single-site installs do not need, and those rules only work if they are written into .htaccess (Apache) or the nginx server block. After a migration, a WordPress core update that changed the default rules, or a manual edit that clipped the multisite-specific lines, every site in the network except the main one starts returning 404 on every page. If your site is a subdirectory multisite and every subsite 404s, this is almost always the cause.
Diagnose which cause applies
Run these checks in order. They are non-destructive and tell you exactly which of the five causes you have before you touch anything.
Check 1: does the ?p=123 version load? Find the post ID in the admin (it is in the URL of the post editor, like post.php?post=123). Visit https://yoursite.nl/?p=123 directly. If it loads the post, the content is fine and the rewrite layer is broken: jump to causes 1, 3, 4, or 5. If it also 404s, the post itself is gone. Check the trash, check the publish status, check the slug. You will know it worked when: you can say "the ?p= version loads" or "the ?p= version also 404s" with certainty.
Check 2: does only one URL 404, or all of them? Try the homepage, a couple of other published posts, and a page. If only one URL 404s while the rest load, cause 2 (moved or unpublished post) is the most likely. If every pretty URL except the homepage 404s, the rewrite layer is gone and you are looking at cause 1, 3, 4, or 5. You will know it worked when: you can describe the scope ("only this URL" or "every URL except the homepage").
Check 3: what does your server stack look like? Open a terminal or the hosting control panel and find out whether the site runs on Apache or nginx. On Apache, this determines whether cause 3 applies. On nginx, skip directly to cause 4. If you are on managed hosting and do not know, check the response headers: curl -I https://yoursite.nl/ shows a Server: header with nginx, Apache, or LiteSpeed. You will know it worked when: you can name the web server.
Check 4: is the .htaccess file intact (Apache only)? Over SFTP or the hosting file manager, open the site root and look for .htaccess. Make sure hidden files are visible. Open it and check that it contains the default WordPress block from the WordPress httpd documentation:
# BEGIN WordPress
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
# END WordPress
If the file is missing, or the block is not there, that is cause 3. You will know it worked when: you can either see the default block in the file or confirm it is missing.
Check 5: is it a multisite? In wp-config.php, look for define( 'MULTISITE', true ); and define( 'SUBDOMAIN_INSTALL', false );. If both are present, you have a subdirectory multisite, and cause 5 is in play. You will know it worked when: you can answer "yes, subdirectory multisite" or "no, regular single site".
Solutions, per cause
Cause #1 fix: re-save permalinks to regenerate the rewrite rules
This is the fix that works about 70% of the time, and it takes five seconds. Log into wp-admin, go to Settings → Permalinks, and click Save Changes without changing any setting. That action triggers flush_rewrite_rules(), which rebuilds the rewrite cache from every registered rule, and on Apache also rewrites the WordPress block in .htaccess if the file is writable. You do not need to touch any dropdown. WordPress only cares that the form was submitted.
If the Save Changes button is disabled, pick a different permalink structure, save, then switch back to your real structure and save again. The important part is that the save handler runs.
Verification: visit the URL that was 404'ing. It should now load. The rewrite_rules row in the wp_options table is also repopulated, and on Apache the .htaccess file's modified time is refreshed.
Cause #2 fix: restore the post, fix the slug, or redirect to the new URL
If the ?p=123 check showed the content is gone, work out which of the three happened. If the post is in the trash, restore it. If it was unpublished, publish it. If the slug was changed and the old URL still gets traffic, add a 301 redirect from the old path to the new one with a plugin like Redirection, or directly in your server config if you prefer not to add a plugin.
For a single URL that genuinely has no replacement on the site, the best fix is to let the 404 stand and add a 410 (Gone) response if you want search engines to drop the URL faster. WordPress does not do this out of the box; a redirect plugin or a custom wp_safe_redirect() snippet can.
Verification: the URL now loads the restored post, or redirects to the correct destination, or returns a deliberate 404/410 with no matching post in the database.
Cause #3 fix: regenerate or recreate .htaccess (Apache only)
If Check 4 showed the file is missing or the WordPress block is gone, the fastest fix is to let WordPress rewrite it for you: go to Settings → Permalinks and click Save Changes. WordPress writes a fresh block if it has write permission on the file. If .htaccess exists but is not writable, the WordPress file permissions documentation notes that 644 is the recommended value, and that if you want WordPress to update the file automatically on permalink save, 666 works but is less secure than manually syncing changes.
If you cannot reach wp-admin (the admin also 404s), create the file manually in the site root. Paste the default WordPress block from Check 4, save, and upload. That restores the rewrite layer and you can log into the admin again.
Do not paste the file from a Windows text editor without confirming the line endings are Unix. An .htaccess with CRLF line endings can fail silently and make diagnosis confusing.
Verification: the failing URL loads, and opening .htaccess over SFTP shows the WordPress block at the top of the file.
Cause #4 fix: fix the nginx or Apache config
On nginx, add or correct the try_files directive in the site's location / block per the WordPress nginx guidance:
location / {
try_files $uri $uri/ /index.php?$args;
}
Reload nginx (sudo systemctl reload nginx on most Linux hosts) and retest. No restart is needed for a config reload.
On Apache, confirm mod_rewrite is enabled (sudo a2enmod rewrite on Debian/Ubuntu) and that the vhost has AllowOverride All (or at least AllowOverride FileInfo) on the WordPress directory. Without AllowOverride, Apache ignores .htaccess entirely and the WordPress rewrite block is never read. Reload Apache after changes.
On managed hosting, you are usually not in a position to edit these files. Send your host the exact error and ask them to confirm try_files (nginx) or mod_rewrite + AllowOverride (Apache) are configured correctly.
Verification: every pretty URL loads, not just the one you were testing. The server's access log shows 200 responses where it was showing 404.
Cause #5 fix: restore the multisite rewrite block
For a subdirectory multisite on Apache, the default block is different from the single-site block. WordPress writes the correct version from Network Admin → Settings → Network Setup. Open that page and copy the suggested .htaccess rules into your site root, replacing the existing WordPress block. On nginx, the Network Setup page shows the corresponding server block. Use its recommendations as the canonical source because they are generated for your specific install path.
After replacing the block, re-save permalinks from any subsite admin so the rewrite cache in the database is also refreshed.
Verification: visiting a page on any subsite in the network loads the page, not a 404.
When to escalate
If the steps above do not resolve the issue within 20 minutes, hand it off to your host or a developer with this checklist ready. It saves a round trip and routes the ticket to the right engineer:
- The exact URL that returns 404, and whether the
?p=123version of the same post loads. - The scope: one URL, one category, every pretty URL, or the whole site.
- Your stack: Apache or nginx, PHP version, WordPress version, whether it is multisite.
- What changed in the last 48 hours: plugin updates, theme updates, core updates, migrations, server changes.
- Whether Settings → Permalinks → Save Changes has any effect.
- On Apache: whether
.htaccessexists and contains the default WordPress block. - On nginx: whether the site block contains
try_files $uri $uri/ /index.php?$args;. - The relevant lines from the server access log around the time of the 404 (look for the 404 status code and the URL).
On a well-configured host, a 404 on a published WordPress page that exists in the admin is a five-minute fix. Anything longer than that usually means the rewrite layer is the problem, not the post, and the information above points at which layer.
How to prevent it from coming back
Three habits keep rewrite-related 404s rare on a healthy site:
- After any migration, always re-save permalinks. A migration moves files and a database, but the rewrite rules cache in
wp_optionsand the.htaccessfile often end up mismatched with the new install path. One extra click on Save Changes right after the migration completes catches most of these before a visitor ever sees them. - Keep your permalink structure stable. Every time the structure changes, existing inbound links break unless you add redirects. The best moment to pick a permalink structure is the day you launch the site. The worst is the day after a successful campaign.
- Version-control
.htaccessif you edit it by hand. If your site runs on Apache and you have custom rewrite rules, keep.htaccessin a git repo alongside the rest of your deploy. A regenerated.htaccessfrom Settings → Permalinks is recoverable; a hand-edited one that was overwritten by a plugin is not.
Rewrite-related 404s are annoying because they look like the content is gone. It almost never is. The content is in the database, the URL is registered, and the only thing broken is the map between the two. The five fixes above rebuild that map.