Make WordPress Core

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:

  1. Spin up a fresh WordPress
  2. Go to Settings > Permalinks > Permalink Structure > Custom Structure
  3. Set the Custom Structure to: /%category%/%postname%
  4. 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)

Screenshot 2023-03-11 at 1.00.01 PM.png (260.6 KB ) - added by mujuonly 4 years ago.
Permalink settings
Screenshot 2023-03-11 at 12.59.44 PM.png (498.8 KB ) - added by mujuonly 4 years ago.
Category archive

Download all attachments as: .zip

Change History (14)

#1 @costdev
4 years ago

  • Keywords has-testing-info needs-testing added

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:

  • #37812 - For the same setup, reports 404 (opposite of this ticket), with possible changes resulting in this ticket's issue, but with inconsistent reports from commenters.

#2 follow-up: @kalpeshh
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 @krdevio
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 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

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 @mujuonly
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

  1. The exact permalink setting I had was /%category%/%postname%.html
Last edited 4 years ago by mujuonly (previous) (diff)

@mujuonly
4 years ago

Permalink settings

@mujuonly
4 years ago

Category archive

#5 @sarunraj
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 @kalpeshh
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.

#8 @wordpressdotorg
16 months ago

  • Keywords has-test-info added; has-testing-info removed

#9 @codente
5 months ago

Is there any update on this issue? I just ran into it.

#10 @kalpeshh
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 @codente
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

  1. Settings โ†’ Permalinks โ†’ Custom Structure: /%category%/%postname%/
  2. Visit example.com/0/
  3. 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):

  1. 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_rule comes back empty and no query vars are set at all.
  2. WP_Query::parse_tax_query() โ€” ! empty( $query_vars[ $t->query_var ] ) skips building the taxonomy clause for a term slug of "0", so is_category is never set and is_home wins. handle_404() then short-circuits on is_home() and never 404s.
  3. WP_Query::get_queried_object() โ€” elseif ( $category_name ) fails to look up a category whose slug is "0", so get_queried_object() returns null and handle_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 $wpdb for this reason.)
  • WP_Query::get_queried_object(), the is_tag branch โ€” elseif ( $tag ) has the identical problem for a tag slug of "0". Not reachable as a reported bug today, partly because of the wp_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.

Note: See TracTickets for help on using tickets.