Чистые ссылки: конфигурация Apache, Nginx и IIS для удаления index.php
Основы настройки URL для удаления index.php
Каким образом настроить Apache для скрытия index.php?
Наиболее распространённый способ
Apache использует модуль mod_rewrite для преобразования URL. Основная цель - перенаправлять запросы, не указывающие на реальный файл, на index.php.
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php/$1 [L]
убрать index php (удаление index.php из url)
Пояснение:
RewriteEngine On- включает обработку правил.RewriteCond %{REQUEST_FILENAME} !-f- проверяет, что запрошенный файл не существует.RewriteCond %{REQUEST_FILENAME} !-d- проверяет, что запрошенная директория не существует.RewriteRule ^(.*)$ index.php/$1 [L]- перенаправляет всё на index.php, добавляя URI после слэша.
Также можно использовать вариант без захвата пути:
RewriteRule .* index.php [L]
Цель: все нефизические URL обрабатываются через index.php, что даёт чистые адреса без принудительного добавления index.php.
Типичные ошибки и их устранение:
- Ошибка 404: возможно, модуль mod_rewrite не включён. Команда
sudo a2enmod rewriteв Debian/Ubuntu. - Циклический редирект: правило не проверяет индексный файл - добавьте проверку на index.php:
RewriteCond %{REQUEST_URI} !^index\.php. - Не работают статические файлы (CSS, JS, картинки): убедитесь, что RewriteCond проверяет реальные файлы, или укажите корневую директорию
RewriteBase /.
Как удалить index.php в среде Nginx?
Nginx не использует .htaccess, настройка выполняется в конфигурационном файле сервера. Пример:
location / {
try_files $uri $uri/ /index.php?$query_string;
}
Пояснение: директива try_files пытается сначала отдать статический файл ($uri), затем директорию ($uri/), и если ничего не найдено - передаёт запрос на index.php с исходным query string.
Возникающие проблемы:
- Если PHP обрабатывается через fastcgi, необходимо убедиться, что блок
location ~ \.php$определён выше общего location. - Параметр
$uri/может вызвать нежелательное перенаправление на существующие директории; можно использовать толькоtry_files $uri /index.php?$uri. - Необходимо перезагружать конфигурацию:
sudo nginx -s reload.
Как настроить IIS для скрытия index.php?
В IIS используется модуль URL Rewrite. Правило описывается в файле web.config:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="Remove index.php" stopProcessing="true">
<match url=".*" />
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Rewrite" url="index.php/{R:0}" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
Пояснение: правило срабатывает, если запрошенный файл или директория не существуют. URL переписывается так, что путь передаётся в index.php.
Ошибки:
- Модуль URL Rewrite не установлен - скачать с официального сайта Microsoft. После установки требуется перезапуск IIS.
- Ошибка 500 при неправильном синтаксисе - проверьте кодировку UTF-8, отсутствие лишних тегов.
Как поступить с чистым URL, если доступ к настройкам сервера ограничен?
Использовать встроенный PHP роутер из php.ini или файл router.php. Запуск сервера с опцией:
$ php -S localhost:8000 index.php
В этом режиме все запросы обрабатываются через index.php, если не найден статический файл. Однако это не подходит для продакшена.
Другой способ - написать собственный PHP-роутер, который проверяет $_SERVER['REQUEST_URI'] и подключает нужные контроллеры. Пример:
$request = $_SERVER['REQUEST_URI'];
if (preg_match('/\.(?:css|js|png|jpg)$/', $request)) {
return false;
}
// Перенаправление на единую точку входа
$_GET['_url'] = ltrim($request, '/');
require 'index.php';
Цель: достичь чистых URL без модификации конфигурации веб-сервера, подходит для локальной разработки.
Недостатки:
- Производительность ниже, чем при настройке сервера.
- Требует обработки статических файлов внутри PHP, что замедляет отдачу.
- Не решает проблемы SEO и красоты URL в продакшене.
Дополнительные и нестандартные примеры кода
Расширенный .htaccess для многосайтовой установки (WordPress)
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
# Если запрос относится к реальному файлу или директории, не переписывать
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# Исключить путь к подсайтам (например, /blog/)
RewriteRule !^(blog|other) /index.php [L]
# Если WordPress в подпапке
RewriteRule ^blog/(.*)$ blog/index.php?url=$1 [QSA,L]
</IfModule>
Результат: URL вида example.com/blog/статья обрабатывается blog/index.php без index.php в строке.
Nginx: обработка index.php для нескольких корней (Microservices)
server {
listen 80;
server_name example.com;
root /var/www/html;
location / {
try_files $uri $uri/ /index.php?$args;
}
location /api/ {
alias /var/www/api/public;
try_files $uri $uri/ /api/index.php?$args;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
Обращение к /api/users передаётся в /api/index.php, URL остаётся чистым.
IIS: правило с проверкой на index.php в URL (предотвращение зацикливания)
<rule name="Remove index.php" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
<add input="{URL}" pattern="^/index\.php" negate="true" />
</conditions>
<action type="Rewrite" url="index.php/{R:0}" appendQueryString="true" />
</rule>
Правило не применяется, если запрос уже содержит index.php, разрывая цикл.
PHP-роутер для встроенного сервера с обработкой ошибок 404
// router.php
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$allowedExtensions = ['css', 'js', 'png', 'jpg', 'gif', 'ico'];
$ext = pathinfo($uri, PATHINFO_EXTENSION);
if (in_array($ext, $allowedExtensions) && file_exists(__DIR__ . $uri)) {
return false; // отдаём статический файл
}
// Прочие запросы - передаём в index.php
$_SERVER['SCRIPT_NAME'] = '/index.php';
require __DIR__ . '/index.php';
Запуск: php -S 0.0.0.0:8080 router.php URL /about обрабатывается через index.php без index.php в строке.