iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more
Antihost offers managed WordPress hosting with free migration, one-click staging and a 14-day free trial.
A broken custom post type (CPT) URL is usually caused by one of four things: the post type was registered with the wrong rewrite settings, WordPress is using stale rewrite rules, another Page or route owns the same slug, or the web server is not passing pretty URLs to WordPress. Register the CPT correctly, regenerate rules after registration or slug changes, remove collisions, and then verify Apache or Nginx configuration if the WordPress-side fixes do not restore the URL.
Start with the symptom you can reproduce
Test both a single CPT entry and the CPT archive. Write down the exact path that fails, the HTTP result (normally a WordPress 404, a server 404, or a redirect), and whether ordinary Pages still work. The pattern narrows the search before you change code.
| # | Preview | Product | Price | |
|---|---|---|---|---|
| 1 |
|
WordPress All-in-One For Dummies | $25.40 | Buy on Amazon |
| 2 |
|
WordPress for Beginners 2026: A Visual Step-by-Step Guide to Mastering WordPress (Webmaster Series) | $19.99 | Buy on Amazon |
| 3 |
|
WordPress For Dummies (For Dummies (Computer/Tech)) | $16.59 | Buy on Amazon |
| What fails | Most likely scope | First action |
|---|---|---|
| One CPT single URL returns a WordPress 404, while the archive works | Single-item registration settings, a conflicting single slug, or a missing permastruct | Inspect publicly_queryable, query_var, and the registered rewrite slug; then save Permalinks |
| The archive and every single CPT URL return 404 | Stale or missing rewrite rules, an incorrect rewrite argument, or a registration callback that never runs |
Confirm registration on init, then regenerate rules |
| The old URL works after a slug change but the new URL does not | Rewrite rules still contain the old permastruct | Save Settings → Permalinks and test the new path in a private window |
| All pretty URLs fail, including ordinary Pages | Apache, .htaccess, Nginx, document-root, or hosting configuration |
Check server rewrite support rather than changing CPT code repeatedly |
How WordPress builds a CPT permalink
register_post_type() tells WordPress that a post type exists and whether it can be addressed publicly. Its rewrite argument creates the permastruct used to turn a path such as /library/the-title/ into a WordPress query. WordPress stores generated rewrite rules in a cache; changing PHP code alone does not immediately replace that cache.
The registration must run on the init action, before WordPress resolves the request. A post type can exist in the database and appear in the dashboard while still being unreachable on the front end if registration is conditional, runs too late, or is never loaded on the current request.
#1 Best Overall
The arguments that control front-end resolution
publiccontrols the general public behavior of the post type.has_archiveenables an archive endpoint such as/library/. It does not by itself make single entries queryable.rewritecontrols generated URL rules. Setting it tofalsedisables generated rewrites.rewrite['slug']sets the path segment used for singles and, normally, the archive.rewrite['with_front']determines whether the site-wide front base from the Permalink Settings structure is prepended.rewrite['feeds']andrewrite['pages']control feed and archive-pagination structures when those features are enabled.publicly_queryablecontrols whether front-end requests can query the post type.query_varcontrols whether WordPress can load the post type through its query variable. Setting it tofalseremoves that query-var route.
A CPT intended only for internal administration may deliberately set some of these values to false. That is valid, but it also means a public permalink cannot work until the relevant settings are enabled.
Step 1: verify the registration code
Locate the function that registers the post type and make sure it is loaded on every front-end request. This is a minimal public registration with an explicit, stable slug:
<?php
function register_book_post_type() {
register_post_type( 'book', array(
'public' => true,
'has_archive' => true,
'publicly_queryable' => true,
'query_var' => 'book',
'rewrite' => array(
'slug' => 'library',
'with_front' => false,
'feeds' => true,
'pages' => true,
),
'show_in_rest' => true,
) );
}
add_action( 'init', 'register_book_post_type' );
The post-type key (book) and the public slug (library) do not have to match. Keep the key stable once content exists; changing it can make existing posts appear to belong to a different type. Change the public slug only when you intentionally want a different URL structure.
Check for settings that silently prevent a URL
- If you see
'rewrite' => false, WordPress will not generate pretty CPT rules. Replace it with an array when public permalinks are required. - If
publicly_queryableis false, front-end requests cannot resolve to single entries even if an archive appears. - If
query_varis false, do not expect a query-var URL to load the post type. Use a public rewrite configuration instead. - If
has_archiveis false, an archive URL is not expected to work; single-entry URLs can still work when the other public settings are correct. - Make sure the callback is attached to
init, not to an action that runs after request parsing.
After changing registration code, clear any object or page cache that could serve an old 404, but do not mistake cache clearing for rewrite regeneration. They are separate operations.
Step 2: regenerate WordPress rewrite rules
The quickest administrative repair is to make WordPress rebuild its rewrite cache:
- Sign in as an administrator.
- Open Settings → Permalinks.
- Click Save Changes without changing the selected permalink structure.
- Open a CPT single URL and its archive URL in a new private window.
Visiting or saving the Permalinks screen triggers a rewrite flush. It also reports when WordPress cannot write the Apache .htaccess file. If the screen displays replacement rules, follow the instruction provided by WordPress or give the generated rules to the person who manages the server.
Do this after the CPT is registered. If you flush first and register later, the regenerated set still lacks the CPT rules, so the 404 remains.
Step 3: flush safely in a plugin
A plugin that owns a CPT should flush rules during activation, after registering the type. Do not call flush_rewrite_rules() on every request; the WordPress core reference describes flushing as expensive because it rebuilds the rule set and may write server configuration.
Rank #2
<?php
add_action( 'init', 'register_book_post_type' );
register_activation_hook( __FILE__, 'my_plugin_activate' );
function my_plugin_activate() {
register_book_post_type();
flush_rewrite_rules();
}
register_deactivation_hook( __FILE__, 'my_plugin_deactivate' );
function my_plugin_deactivate() {
flush_rewrite_rules();
}
The activation callback explicitly calls the registration function because activation can occur before a normal init request has completed. The sequence matters: register first, flush second. If the plugin changes its slug in a later release, use a versioned upgrade routine or a one-time flag to flush once after the new code is active, then remove or disable that flag.
For a one-off maintenance task, the Permalinks screen is safer than adding a permanent flush to a theme or plugin. A hard flush can rewrite .htaccess; use it only when the application owner understands the server layout and has a backup of the existing rules.
Free tools Windows power users keep installed
One-click scans. No signup required.
Step 4: remove Page and route collisions
A Page, taxonomy, another CPT, or a custom rewrite rule can claim the same path segment as the CPT. For example, a Page at /events/ and a CPT whose rewrite slug is also events create an ambiguous route. WordPress’s common-errors guidance specifically recommends checking that a CPT and a Page do not have the same name.
Find and resolve a collision
- Record the CPT’s effective rewrite slug from the registration code.
- In Pages → All Pages, search for a Page with that same slug. Check trashed Pages too if the slug has recently been reused.
- Review taxonomy rewrite slugs and any code using
add_rewrite_rule()for the same first path segment. - Choose one owner for the path. Rename the Page, change the CPT’s
rewrite['slug'], or change the single-post slug. - Save the Permalinks screen once the names are unique, then retest both archive and single URLs.
Do not solve a collision by adding more ad-hoc redirects before deciding which object should own the URL. Redirects can conceal the conflict while leaving generated rules ambiguous.
Step 5: inspect custom rewrite code and rule order
Sites with multilingual plugins, permalink-management plugins, custom taxonomies, or direct calls to add_rewrite_rule() can fail even when the CPT registration is correct. A custom rule may use the wrong query variable, match before the CPT rule, or be generated with an outdated slug.
Checks for custom rules
- Confirm that the query string produced by a custom rule uses a registered query variable and the correct post type.
- Check whether a broad regular expression matches the CPT path before the more specific rule. Rule order can send the request to the wrong handler.
- After adding, removing, or changing a rule, flush and regenerate the rules. The API documentation recommends saving Settings → Permalinks as the administrative method.
- Temporarily disable one competing rewrite extension at a time in a staging copy. If the URL begins working, inspect that extension’s rule and query-variable settings rather than permanently leaving it disabled.
Keep custom rules as narrow as possible. A rule intended for /events/ should not also match every first-level path on the site.
Step 6: verify the web server
If the registration, slug, collision check, and flush are correct but pretty URLs still fail, the problem may be outside WordPress. Pretty permalinks require the web server to pass non-file paths to WordPress.
Apache checks
- Ask the host or server administrator to confirm that
mod_rewriteis enabled. - Check that the document root allows overrides where WordPress is installed and that
.htaccessis readable and writable when WordPress needs to update it. - Compare the generated
.htaccessrules with the actual document root. A site installed in a subdirectory needs the correct base path. - Review server error logs for rewrite or permission errors instead of repeatedly changing CPT code.
Nginx and managed hosting checks
Nginx does not use .htaccess. The host must configure an equivalent try_files rule (or its platform-specific equivalent) that sends unknown paths to WordPress’s front controller. Ask the provider to verify the document root, PHP location block, and any cache layer that could be returning a stored 404. A server-level 404 for every pretty URL points to this configuration; a WordPress-branded 404 usually means the request reached WordPress but no matching query was generated.
Rank #3
Do not overwrite server configuration supplied by a managed host without a rollback plan. Give the host the exact failing URL, a working test URL if one exists, and the result of saving Permalinks so they can distinguish an application rule from a server rule.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Use a rewrite-inspection plugin when the cause is unclear
Rewrite Rules Inspector can display the current permalink structure, registered custom post types and taxonomies, and the generated rules. This is useful when several plugins alter rewrites and you need to see whether the CPT rule exists at all.
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →Use it as a diagnostic aid, not as a substitute for fixing registration. Its documented soft flush calls flush_rewrite_rules(false), which regenerates the in-memory rule set without requesting a hard write to server configuration. A soft flush is appropriate while investigating rule generation; use the normal Permalinks save or a controlled lifecycle flush when the server file must also be updated.
Follow the right branch for common failures
The archive works but a single CPT entry is a 404
- Confirm the entry is actually published and belongs to the expected post type.
- Check
publicly_queryableandquery_var. - Inspect the single rewrite slug for a conflict with a Page or another route.
- Save the Permalinks screen after correcting the code.
- If only one entry fails, test another published entry to rule out a content-specific slug or redirect issue.
The CPT was created recently and every URL is a 404
- Verify the registration callback runs on
initon the front end. - Ensure
rewriteis an array rather thanfalse. - Check that the intended archive setting and public-query settings are enabled.
- Save Settings → Permalinks after the code is active.
You changed the CPT slug and the old URL still loads
The old route can remain in the cached rule set until it is flushed. Save Permalinks, clear page or CDN caches, and test the new URL. If the old URL continues to resolve after the new route works, look for an intentional redirect, a stale cache, or a second rule that still uses the former slug; do not assume WordPress has automatically removed every redirect created by another plugin.
Every pretty URL fails, but the dashboard works
Test a normal Page. If it also fails, stop editing CPT registration and inspect mod_rewrite, .htaccess, Nginx routing, the document root, and host-level caching. A CPT-specific code change cannot repair a server that never forwards pretty paths to WordPress.
Choose a repair method by scope and risk
| Repair method | Scope | Operational risk | Who should perform it | When it persists |
|---|---|---|---|---|
| Save Settings → Permalinks | Regenerates the site’s WordPress rewrite cache | Low; WordPress may request an .htaccess update |
WordPress administrator | Immediately after the current registration and slug are correct |
| Activation-hook flush | Plugin-owned CPT rules | Controlled; runs during activation rather than every request | Plugin developer | On activation and any deliberate versioned migration |
| Soft flush during diagnosis | Rule inspection without a hard server-file write | Lower than a hard flush, but it does not replace server configuration | Developer or administrator using a rewrite-inspection tool | For the current generated rules |
| Server configuration change | All pretty URLs handled by Apache, Nginx, or a host platform | Potentially site-wide; take a backup and rollback plan | Hosting or server administrator | Until the server configuration changes again |
Final verification checklist
Run these tests after each change so you know which action fixed the problem:
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstall- Open one published CPT single URL while logged out.
- Open the CPT archive, if
has_archiveis enabled. - Open a second CPT entry to detect a slug-specific problem.
- Test a normal Page and a post to identify server-wide failures.
- Check the site with and without the front base when
with_frontwas changed. - Confirm that feeds or paginated archive pages work if those structures are enabled.
- Inspect the final generated rules after a flush when custom rewrite code is present.
- Clear page, object, and CDN caches only after the rewrite structure is correct.
- Record the final public slug and keep it stable; future changes require another controlled flush and, where appropriate, redirects from the old address.
The Bottom Line
Fix CPT permalink 404s in order: validate registration and public-query settings, register on init, flush rules after registration or slug changes, remove Page and rewrite collisions, and then have the host verify Apache or Nginx routing. Never put a rewrite flush on every request.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

