PrestaShop 9 errors
403 Forbidden on a module's ajax.php in PrestaShop 9
PrestaShop 9 blocks direct access to PHP files inside the modules folder. Modules that answer AJAX through their own ajax.php get 403 Forbidden, and the feature behind it stops working.
- The error
403 Forbidden: /modules/mymodule/ajax.php- Removed in
- PrestaShop 9.0
- Usually found in
- Older modules with their own PHP endpoints
What happened
PrestaShop 9 adds a modules/.htaccess file that denies direct web access to .php, .tpl, .twig, .json, .yml, .log, .txt and config.xml files inside the modules folder. It closes an old security hole: many attacks on PrestaShop stores went through forgotten PHP scripts in module folders.
The side effect: modules that answer requests through their own file, such as modules/mymodule/ajax.php, cron.php or upload.php, now get 403 Forbidden. There is no PHP error and nothing in the PrestaShop log. A button or filter simply does nothing, and the browser's developer tools show the 403 response.
This applies to Apache. On Nginx the rule has to be added to the server configuration, so the same modules may still work there, along with the security hole.
The fix
Move the endpoint into a module front controller. That is the standard way since PrestaShop 1.5, so it works in every version:
// modules/mymodule/controllers/front/ajax.php
class MyModuleAjaxModuleFrontController extends ModuleFrontController
{
public function postProcess()
{
$result = ['ok' => true];
$this->ajaxRender(json_encode($result));
exit;
}
}
Build its address with the link class instead of hard-coding the path:
$url = $this->context->link->getModuleLink('mymodule', 'ajax', [], true);
Cron scripts become a front controller too, protected with a secret token in the URL.
Do not delete or loosen modules/.htaccess to bring the old file back. The next upgrade restores it, and it is there for a reason.
Where else it hides
find modules -maxdepth 2 -name "*.php" ! -name "index.php" | grep -v "/vendor/"
Then check which of those files the module calls from JavaScript or a cron job: the main module file (mymodule.php) is fine, it is never opened directly.