Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
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.

The arguments that control front-end resolution

  • public controls the general public behavior of the post type.
  • has_archive enables an archive endpoint such as /library/. It does not by itself make single entries queryable.
  • rewrite controls generated URL rules. Setting it to false disables 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'] and rewrite['pages'] control feed and archive-pagination structures when those features are enabled.
  • publicly_queryable controls whether front-end requests can query the post type.
  • query_var controls whether WordPress can load the post type through its query variable. Setting it to false removes 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:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
<?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_queryable is false, front-end requests cannot resolve to single entries even if an archive appears.
  • If query_var is false, do not expect a query-var URL to load the post type. Use a public rewrite configuration instead.
  • If has_archive is 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:

  1. Sign in as an administrator.
  2. Open Settings → Permalinks.
  3. Click Save Changes without changing the selected permalink structure.
  4. 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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

<?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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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

  1. Record the CPT’s effective rewrite slug from the registration code.
  2. In Pages → All Pages, search for a Page with that same slug. Check trashed Pages too if the slug has recently been reused.
  3. Review taxonomy rewrite slugs and any code using add_rewrite_rule() for the same first path segment.
  4. Choose one owner for the path. Rename the Page, change the CPT’s rewrite['slug'], or change the single-post slug.
  5. 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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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_rewrite is enabled.
  • Check that the document root allows overrides where WordPress is installed and that .htaccess is readable and writable when WordPress needs to update it.
  • Compare the generated .htaccess rules 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.

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.Support on Ko-Fi

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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

  1. Confirm the entry is actually published and belongs to the expected post type.
  2. Check publicly_queryable and query_var.
  3. Inspect the single rewrite slug for a conflict with a Page or another route.
  4. Save the Permalinks screen after correcting the code.
  5. 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

  1. Verify the registration callback runs on init on the front end.
  2. Ensure rewrite is an array rather than false.
  3. Check that the intended archive setting and public-query settings are enabled.
  4. 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:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • Open one published CPT single URL while logged out.
  • Open the CPT archive, if has_archive is 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_front was 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.

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.