Opened 4 years ago
Last modified 6 weeks ago
#57858 new defect (bug)
If permalink structure is set to /%category%/%postname% , WordPress will match a /0/ path as a category archive
| Reported by: | krdevio | Owned by: | |
|---|---|---|---|
| Priority: | normal | Milestone: | Awaiting Review |
| Component: | Rewrite Rules | Version: | 6.1.1 |
| Severity: | normal | Keywords: | has-test-info needs-testing has-patch has-unit-tests |
| Cc: | Focuses: |
Description
Issue:
When the post permalink structure (Settings > Permalinks > Permalink Structure > Custom Structure ) is set to /%category%/%postname% , any path containing /0/ will be matched as a category archive page.
eg.
example.com/wp-content/0/ will not return a 404, and resolve to category archive template.
example.com/wp-includes/0/ will not return a 404 and resolve to category archive template.
example.com/wp-admin/0/ will not return a 404 and resolve to category archive template.
example.com/some-post-name/0/ will resolve to some-post-name
example.com/0/ will properly resolve to the site_url, but still wont 404
Setting the permalink to anything but that, eg. /%author_name%/%postname% it will properly 404.
I'm guessing somewhere in the permalink structure, 0 is seen as a valid category and not empty/null.
Steps to reproduce:
- Spin up a fresh WordPress
- Go to Settings > Permalinks > Permalink Structure > Custom Structure
- Set the Custom Structure to: /%category%/%postname%
- Visit one of the example URLs above.
We found this issue as we noticed Google was indexing a page like example.com/wp-content/uploads/2011/0/
Attachments (2)
Change History (14)
#2
follow-up:
↓ 3
@
4 years ago
I am getting proper 404 for all the cases you mentioned except below,
example.com/0/ will properly resolve to the home page and does not give 404
/0/some-post-name will properly resolve to some-post-name page regardless of in which category it belongs
#3
in reply to: ↑ 2
@
4 years ago
Replying to kalpeshh:
I am getting proper 404 for all the cases you mentioned except below,
example.com/0/will properly resolve to thehome pageand does not give 404
/0/some-post-namewill properly resolve tosome-post-namepage regardless of in which category it belongs
I just retested, you know what you are correct. My apologies My permalink settings were not exactly
/%category%/%postname%, and testing that it was fine as you said.
With that said it might still be a bug: The exact permalink setting I had was /%category%/%postname%.html (.html at the end). I guess adding the .html at the end causes /0/ to match as mentioned initially.
#4
@
4 years ago
Welcome to core track, thanks for creating ticket
I have tried to reproduce this issue and you are right it does resolve to category archive
The link example.com/wp-content/0/ will not return a 404, and resolve to category archive template.
Test Report
Env
WordPress 6.1.1
Chrome Version 110.0.5481.177 (Official Build) (arm64)
MacOS Monterey
Theme: Twenty Twenty Three
Steps to test
- The exact permalink setting I had was /%category%/%postname%.html
#5
@
4 years ago
Welcome to core track, thanks for creating ticket
I have tried to reproduce this issue and you are right it does resolve to category archive when you adding permalink /%category%/%postname%.html . if you add permalink /%category%/%postname%/html like this it will redirect into 404 page โhttps://prnt.sc/x_kBuK_lVqFK.
Env
WordPress 6.1.1
Chrome 110.0.5481.177
MacBook Air M1
Theme: Twenty Twenty Three
Gutenberg Editor
This ticket was mentioned in โSlack in #core-test by juhise. โView the logs.
4 years ago
#7
@
4 years ago
- Keywords needs-patch added
The issue is happening because of REGEX match if you have .htm or anything starting with dot (.) at the end of permalink.
Seems like a valid issue.
#10
@
5 months ago
I verified testcases below. And everything seems to be working as expected.
Permalink: /%category%/%postname%.html
Test case 1:
http://localhost:8889/uncategorized/hello-world.html
โ
Working as expected and not giving 404
Test case 2:
http://localhost:8889/0/hello-world.html
โ
Working as expected and not giving 404 (as category index exist)
Test case 3:
`โhttp://localhost:8889/0/
โ Working but should have given 404
Test case 4:
`โhttp://localhost:8889/1/
โ
Working as expected and giving 404
#11
@
5 months ago
It is not working as expected but maybe what I'm seeing isn't this exact issue? I'm not sure.
When the post permalink structure (Settings > Permalinks > Permalink Structure > Custom Structure ) is set to /%category%/%postname% , the category slug without /%category%/ will match as the category archive but won't work as expected - pagination doesn't work for example.
This ticket was mentioned in โPR #12740 on โWordPress/wordpress-develop by โ@micahele.
6 weeks ago
#12
- Keywords has-patch has-unit-tests added; needs-patch removed
## Summary
Fixes #57858.
A request path consisting of the string 0 is falsy in PHP, so several truthiness guards treat it as though no path had been requested at all.
With a permalink structure of /%category%/%postname%/, a request for /0/ never reaches the rewrite rule loop and silently falls through to the front page instead of resolving the (.+?)/?$ catch-all rule and returning a 404.
This is the case @kalpeshh re-confirmed still reproduces on trunk in comment:10 ("test case 3 ... Working but should have given 404").
### Reproduction
- Settings โ Permalinks โ Custom Structure:
/%category%/%postname%/ - Visit
example.com/0/ - It renders the front page (HTTP 200) instead of a 404.
## Root cause
Three separate falsy-"0" checks along the request path, each of which is independently load-bearing (verified by reverting them one at a time against the new tests):
WP::parse_request()โif ( empty( $request_match ) )treats a requested path of"0"as an empty request, so the entire rewrite-rule loop is skipped and only the^$(home) rule is considered. This is the primary bug:matched_rulecomes back empty and no query vars are set at all.WP_Query::parse_tax_query()โ! empty( $query_vars[ $t->query_var ] )skips building the taxonomy clause for a term slug of"0", sois_categoryis never set andis_homewins.handle_404()then short-circuits onis_home()and never 404s.WP_Query::get_queried_object()โelseif ( $category_name )fails to look up a category whose slug is"0", soget_queried_object()returnsnullandhandle_404()404s a category archive that genuinely exists.
The change in (2) is deliberately narrow: for scalars it swaps ! empty( $x ) for '' !== (string) $x, whose only behavioural delta versus the original is the numeric-zero family ("0", 0). null, false, '', and empty arrays are all still skipped exactly as before.
## Testing
New test file tests/phpunit/tests/rewrite/zeroPathSegment.php (4 tests):
| Test | Before | After |
|---|---|---|
/0/ 404s when no matching category exists | โ fails (renders front page) | โ passes |
Category with slug 0 resolves to its archive | โ fails (404s) | โ passes |
/wp-content/0/ 404s | โ passes | โ passes (guard) |
/ still resolves to the front page | โ passes | โ passes (guard) |
The two guard tests pass both before and after; they exist to prove the fix does not 404 a legitimate "0" category, and does not regress the empty-request path (which is also falsy).
npm run test:php -- --filter Tests_Rewrite_ZeroPathSegment OK (4 tests, 8 assertions)
Full suite, single site:
Tests: 30520, Assertions: 4558386, Warnings: 86, Skipped: 49
No failures or errors. The 86 warnings are pre-existing PHPUnit 10 forward-compatibility notices (Expecting E_DEPRECATED ... is deprecated) and are unrelated to this change. Focused groups --group rewrite (1392), --group query (1896), --group canonical (1057) and --group taxonomy (878) all pass.
PHPCS on class-wp-query.php reports an identical 0 errors / 34 warnings before and after the patch; none of the warnings fall on the changed lines.
### A note on the test setup
WP_Rewrite::init() resets the registered rewrite tags to the built-in defaults, which do not include %category%. Tests that use a %category% permalink structure therefore have to call create_initial_taxonomies() and flush again, otherwise %category% is never substituted and the generated rules are literally %category%/?$ => index.php?%category%$matches[1]. This mirrors the existing approach in tests/phpunit/tests/query/verboseRewriteRules.php.
## Out of scope (siblings found while investigating)
Two adjacent instances of the same falsy-"0" defect turned up but are deliberately not included here, to keep the diff reviewable:
wp_insert_term()discards a slug of"0"โ! empty( $args['slug'] )falls back to a slug derived from the name, so a category with slug"0"cannot currently be created through the API at all. (The new test forces the slug directly via$wpdbfor this reason.)WP_Query::get_queried_object(), theis_tagbranch โelseif ( $tag )has the identical problem for a tag slug of"0". Not reachable as a reported bug today, partly because of thewp_insert_term()issue above.
Happy to fold either or both in, or open separate tickets, if reviewers prefer.
The .html-suffixed variant discussed in comment:3/comment:4 and the pagination report in comment:11 are not addressed here; the multi-segment cases from the original report (/wp-content/0/ etc.) already 404 on current trunk.
## Use of AI Tools
AI assistance: Yes
Tool(s): Claude Code
Model(s): Claude Opus 5
Used for: Investigation, implementation, and test drafting; the root cause was confirmed empirically by tracing the request through the local Docker environment, and the final code and tests were reviewed and verified by me.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)
Hi @krdevio, welcome to Trac and thanks for opening this ticket.
I'm just adding some keywords to help get this ticket some more attention. ๐
Related/Semi-duplicate: