Add_Rewrite_Rule Not Working usually means WordPress hasn’t registered or flushed your rule yet, or the rule pattern/query can’t be reached.
When a custom URL keeps returning a 404, it’s tempting to keep tweaking regex until it “magically” clicks. In WordPress, rewrite rules are stored, merged, and cached in ways that can make a correct rule look broken. You can troubleshoot it with a short checklist that pinpoints the failure point.
This guide stays on WordPress’s Rewrite API (add_rewrite_rule()) and the pieces it depends on now: permalinks, tags, query vars, and the flush step. You’ll also see a safe way to test what WordPress thinks your rules are, without guessing.
Why Add_Rewrite_Rule Not Working Happens
WordPress doesn’t read your rewrite rule straight from your plugin file on each request. It builds a big rules array and stores it. That’s why a brand-new rule can fail even when the code is correct. Most “add_rewrite_rule not working” reports fall into a few buckets.
- Rules weren’t flushed — WordPress is still serving the old rules set, so your new pattern never gets a chance to match.
- The query target is wrong — The rewrite points somewhere WordPress won’t parse, often missing
index.phpor using a query var WordPress refuses. - The regex never matches — A tiny pattern mismatch (missing trailing slash, anchors, or capture group) blocks the rule.
- Priority/order is off — A broader rule matches first, so your custom rule is skipped.
- The request never reaches WordPress — Server rules, caching, or a static file match bypasses WordPress entirely.
If you work through the checks in order, you’ll stop chasing ghosts and fix the exact layer that’s failing.
Fast Checks Before You Touch Code
Start with the basics that block routing. These take minutes and often solve the issue right away.
Confirm Pretty Permalinks Are Enabled
If your site is set to “Plain” permalinks, WordPress won’t use rewrite rules for pretty URLs. Open Settings → Permalinks and pick any non-plain structure, then save. Even if it already looks correct, re-saving is a clean way to refresh the rules set.
Flush Rules The Safe Way
During development, you can flush by visiting Settings → Permalinks. For code, flush only on activation or on a version change, not on each page load. Flushing on each request is expensive and can hurt performance on busy sites.
- Hook activation — Register your rewrite rule on
init, then flush once in your activation hook. - Hook deactivation — Flush again on deactivation so the old rule is removed cleanly.
- Use a version flag — If your rules change later, bump a stored version and flush once after the update runs.
Check For A Cached 404
Some caching layers store 404 responses. If you added the rule after a 404 was cached, you may keep seeing the old result. Clear your page cache and any CDN cache, then test again in a private window.
Fixing Rewrite Rules In WordPress Plugins
Once permalinks and flushing are handled, the next most common cause is an incorrect rule definition. A rewrite rule has three moving parts: the regex pattern, the query string, and the position (top or bottom) in the rules list.
Use A Regex That Matches The Real URL
WordPress matches the path part of the URL, not the full domain. Keep your pattern tight, anchored, and explicit about slashes. If you want /movies/inception/ to route, match that shape and capture the slug.
- Anchor the pattern — Use
^and$so you don’t accidentally match extra paths. - Handle the trailing slash — Either require it or make it optional, then test both forms.
- Capture only what you need — Use
([^/]+)for slugs, and avoid overly greedy groups.
Point To index.php With Valid Query Vars
WordPress parses rewritten requests through index.php. If your query string doesn’t start with index.php?, WordPress may not route it. Also, WordPress ignores unknown query vars unless you register them.
- Start with index.php — Rewrite to something like
index.php?post_type=movie&name=$matches[1]. - Register custom tags — If you need a custom var, add a rewrite tag with
add_rewrite_tag(). - Whitelist query vars — Add your custom var with the
query_varsfilter so WordPress accepts it.
Pick The Right Rule Position
If your pattern is specific but still loses, a more general rule may be matching first. Setting your rule to top can help during debugging. Once it works, you can decide if it should stay at the top or live at the bottom.
As a quick sanity test, temporarily set your rule to top and see if it begins to fire. If that fixes it, you’ve learned that order is the real issue, not the pattern.
Make WordPress Recognize Your Variables And Endpoints
Even when the URL matches, WordPress can still serve a 404 if it can’t map the request to a known route. Think of rewrite rules as the “door,” and query parsing as the “room” you’re trying to enter.
Use add_rewrite_tag For Custom Params
If you rewrite to index.php?my_param=value and WordPress doesn’t know my_param, it may drop it. Register a tag for that variable and give it a regex that matches the values you expect.
Use add_rewrite_endpoint For “Extra Path” Patterns
Endpoints are a clean fit when you want URLs like /account/orders/ without creating a whole new page. Endpoints also require flushing, and they rely on the same rewrite storage system as rules.
Confirm Your Template Or Query Can Resolve
After the rewrite, WordPress still needs to find content. If you rewrite to a custom post type, confirm the post type exists, uses the slug you’re using, and has the right rewrite settings. If you rewrite to a page, confirm the page slug is correct and not duplicated by another route.
| What You See | Likely Cause | Fast Fix |
|---|---|---|
| 404 on the new URL | Rules not flushed | Visit Permalinks or flush on activation |
| URL matches but vars are empty | Query var not registered | Add rewrite tag and query_vars filter |
| Wrong content loads | Rule order conflict | Move rule to top, then refine pattern |
| Works locally, fails on host | Request bypasses WordPress | Check server rewrites, caching, static matches |
Debug Like A Pro Without Guessing
Debugging rewrites gets easier when you can see what WordPress is doing. You’re trying to answer two questions: did the incoming path match a rule, and did WordPress parse the request into the query you expected?
Inspect The Rules Array
In a dev site, you can print the rewrite rules array and confirm your pattern exists. If your pattern is missing, the issue is registration or flushing. If it’s present, the issue is matching or query parsing. Some developers use a temporary admin-only screen to display rules, then remove it after testing.
Log The Matched Rule And Query
WordPress exposes hooks that let you log what rule matched. Logging the matched query string tells you whether capture groups resolved the way you thought.
Test With Simple Patterns First
When you suspect your regex, start with a trivial pattern that matches a fixed string. Once that fires, add one capture group. Then add your optional pieces. This step-up approach turns a hard regex problem into a short series of easy checks.
- Match a fixed path — Route
^rewrite-test/?$to a known page query. - Add one capture — Extend to
^rewrite-test/([^/]+)/?$and log$matches[1]. - Add the real structure — Move from the test path to your final URL shape.
If you’re still stuck, revisit your expectation. In many cases, the route is matching, but the rewritten query points to content that doesn’t exist.
Server And Hosting Issues That Block Rewrites
Sometimes the rule is fine and WordPress is ready, but the request never reaches WordPress. This can happen when the web server is configured to serve certain paths as static files, or when a host uses a rewrite setup that differs from your local machine.
Apache And .htaccess Basics
On Apache, WordPress relies on mod_rewrite and the rewrite rules inside .htaccess (or a virtual host config). If mod_rewrite is disabled or overrides are blocked, WordPress permalinks can break, and your custom rules won’t run either.
- Confirm mod_rewrite is enabled — Without it, pretty permalinks won’t work at all.
- Allow overrides — Your server must allow
.htaccessto apply rewrite rules. - Keep WordPress rules intact — If the main WordPress block is missing, requests may bypass WordPress.
Nginx And Managed Hosting Quirks
On Nginx, there’s no .htaccess. The server block must pass requests to WordPress’s front controller. Some managed hosts also add caching or security rules that intercept certain paths, file extensions, or reserved directories.
- Check the try_files line — WordPress needs requests to fall back to
index.php. - Avoid rewriting static assets — Requests for
.cssor.jsmay never hit WordPress. - Watch for security filters — Some hosts block patterns that look like traversal or query injection.
If you test on staging behind basic auth or a proxy, the path that hits WordPress might differ from what you expect. Always test on the exact domain and path that visitors use.
A Clean Fix Pattern You Can Reuse
When you want reliable rewrites, build them as a small system: register the rule, register the variables, flush on activation, and add a lightweight way to verify that the route resolves. This prevents the common cycle where things work on your machine and fail after deployment.
Activation First, Then Stable Routing
Register your rule on init so it exists on each request, then flush once when the plugin activates. After that, the rule should remain stable until you change it. If you change the rule shape in a later release, use a version flag so you can flush once after the update.
Keep Patterns Specific And Non-Overlapping
Specific patterns reduce rule collisions. They also reduce the chance that a core rule or a page slug steals your route. Use a distinct prefix segment for custom routes, like /tools/ or /api/, then match inside that space.
Verify With A Simple “Route Health” Check
A small health check can save hours. It can be as simple as visiting a known test URL and confirming it loads the right template, or logging the matched query in debug mode. Once your rule is working, remove any temporary output so it doesn’t clutter your admin area.
If you’ve been dealing with add_rewrite_rule not working for hours, run through this order: permalinks, flush, pattern match, query vars, rule order, then server routing. That sequence solves the majority of cases with the least wasted effort.
Treat rewrites as part of your release process. Make them predictable, flush them safely, and log the match when you need to.
