Skip to content

Use the default front controller for URLs generated outside a web request (#25976) - #41172

Open
lbajsarowicz wants to merge 5 commits into
magento:2.4-developfrom
lbajsarowicz:fix/25976-cli-web-server-rewrites
Open

Use the default front controller for URLs generated outside a web request (#25976)#41172
lbajsarowicz wants to merge 5 commits into
magento:2.4-developfrom
lbajsarowicz:fix/25976-cli-web-server-rewrites

Conversation

@lbajsarowicz

Copy link
Copy Markdown
Contributor

Description

With Use Web Server Rewrites disabled, URLs generated outside a web request get the console binary as their entry point — https://example.com/magento/catalog/... instead of https://example.com/index.php/catalog/.... This affects anything that builds URLs from the CLI: transactional emails sent by cron, sitemap generation, indexers, custom commands.

Magento\Store\Model\Store::_updatePathUseRewrites() takes the entry point straight from the running script:

$scriptFilename = $this->_request->getServer('SCRIPT_FILENAME');
$indexFileName = is_string($scriptFilename) ? basename($scriptFilename) : '';

Under bin/magento that basename is magento, which is not a front controller at all.

Fix

Rather than trying to detect the execution context, this validates the result: if the resolved entry point is not a .php script it cannot be a web front controller, so fall back to index.php — exactly the value the _isCustomEntryPoint() branch two lines above already produces.

if (!str_ends_with(strtolower($indexFileName), '.php')) {
    // The running script is not a web entry point (CLI, cron, unknown) - use the default one
    $indexFileName = 'index.php';
}

A .php filename is passed through byte-for-byte, so every real web request — pub/index.php, pub/get.php, pub/static.php, pub/cron.php, and any custom entry point — is untouched. This also fixes the empty case, which previously emitted a double slash (https://example.com/link//).

That index.php is the right answer here is already encoded in the test framework: dev/tests/integration/framework/Magento/TestFramework/Bootstrap/Environment.php:24 fakes SCRIPT_FILENAME = 'index.php' precisely because the real CLI value is unusable, and Magento/Framework/UrlTest.php:45 asserts http://localhost/index.php/.

Why not detect the CLI directly

PHP_SAPI === 'cli' looks like the obvious fix and there is core precedent for it, but it is wrong for this method: the unit and integration suites all run under the cli SAPI while deliberately emulating web requests. StoreTest::testGetBaseUrlEntryPoint and the integration testGetBaseUrlForCustomEntryPoint both assert web behaviour from a CLI process, so a SAPI check would break them and make the branch untestable.

Setting Store::CUSTOM_ENTRY_POINT_PARAM from the console entry point was the other candidate. The natural hook is Magento\Framework\Console\Cli, but referencing Magento\Store\Model\Store from lib/internal/Magento/Framework is forbidden by the library dependency static test, and doing it in bin/magento means writing to $_SERVER. It would also fix nothing for third-party scripts that bootstrap Magento themselves.

This PR continues #34639

Rebased onto current 2.4-develop, preserving the commits of @rmsundar1 and @engcom-Bravo.

One correction worth recording, since it is why the original stalled. @ihor-sviziev's "It doesn't look real example" was right: the branch tested SCRIPT_FILENAME !== 'bin/magento', a strict comparison against a relative literal, while the real CLI value is an absolute path such as /var/www/html/bin/magento — so the condition never matched and the fix never fired. That comparison was not @rmsundar1's original approach; his commit used getOriginalPathInfo() as the "not a web request" signal (right instinct, wrong signal — it is legitimately empty for a homepage request), and the literal was introduced later while chasing test failures. Both are replaced here.

Fixed Issues

Fixes #25976

Manual testing scenarios

  1. Set Stores > Configuration > General > Web > Search Engine Optimization > Use Web Server Rewrites to No.
  2. From the CLI, generate a URL — e.g. run a cron job that sends a transactional email containing a store link, or bin/magento sitemap:generate.
  3. Before: links contain /magento/. After: links contain /index.php/.
  4. Load any storefront page and confirm URLs are unchanged (still /index.php/... from a normal web request).
  5. Configure a custom entry point (e.g. custom_entry.php) and confirm it is still used verbatim for web requests.

Questions or comments

Gates run locally on 2.4-develop (Warden, PHP 8.3):

  • Unit: Store/Test/Unit/Model/StoreTest.php — 49 tests pass, including a new six-case data provider covering a default web entry point, a custom web entry point, an absolute CLI path, the custom-entry-point flag, and empty/missing SCRIPT_FILENAME.
  • Integration: Magento/Store/Model/StoreTest.php + Magento/Framework/UrlTest.php — 96 tests pass. These are the ones that emulate web requests from a CLI process, so they are the real regression check for this change.
  • PHPCS Magento2: clean. PHPStan level 1: no errors.
  • Negative check: three of the six new cases fail against unpatched Store.php (yielding magento/ and //) and pass with the fix; the other three pass both before and after, pinning the no-change guarantee for web requests.

No signature changes — Store stays @api-compatible, with no new constructor parameter or DI wiring.

One theoretical case for a second opinion: hosting where the front controller has no .php extension would now get index.php instead of that name. I could not construct such a setup for Magento (all shipped entry points are .php, and rewrite-based extensionless URLs still report the .php file in SCRIPT_FILENAME), and index.php is the correct front controller there anyway — but it is the one behaviour change outside CLI beyond the empty-value case.

Contribution checklist

  • Pull request has a meaningful description of its purpose
  • All commits are accompanied by meaningful commit messages
  • All new or changed code is covered with unit/integration tests (if applicable)
  • All automated tests passed successfully (all builds are green)

rmsundar1 and others added 5 commits August 30, 2026 13:15
…uest (magento#25976)

With "Use Web Server Rewrites" disabled, Store::_updatePathUseRewrites()
appended basename(SCRIPT_FILENAME) to the base URL, which under CLI is the
console binary - producing links such as .../magento/catalog/... . Fall back
to index.php whenever the running script is not a .php entry point, which
also covers an empty or missing SCRIPT_FILENAME.
@lbajsarowicz

Copy link
Copy Markdown
Contributor Author

@magento run all tests

@m2-assistant

m2-assistant Bot commented Aug 30, 2026

Copy link
Copy Markdown

Hi @lbajsarowicz. Thank you for your contribution!
Here are some useful tips on how you can test your changes using Magento test environment.
❗ Automated tests can be triggered manually with an appropriate comment:

  • @magento run all tests - run or re-run all required tests against the PR changes
  • @magento run <test-build(s)> - run or re-run specific test build(s)
    For example: @magento run Unit Tests

<test-build(s)> is a comma-separated list of build names.

Allowed build names are:
  1. Database Compare
  2. Functional Tests CE
  3. Functional Tests EE
  4. Functional Tests B2B
  5. Integration Tests
  6. Magento Health Index
  7. Sample Data Tests CE
  8. Sample Data Tests EE
  9. Sample Data Tests B2B
  10. Static Tests
  11. Unit Tests
  12. WebAPI Tests
  13. Semantic Version Checker

You can find more information about the builds here
ℹ️ Run only required test builds during development. Run all test builds before sending your pull request for review.


For more details, review the Code Contributions documentation.
Join Magento Community Engineering Slack and ask your questions in #github channel.

@lbajsarowicz

Copy link
Copy Markdown
Contributor Author

@magento run Unit Tests

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

'magento' adding in the URL when Use Web Server Rewrites set to NO using Console Command

3 participants