.htaccess и nginx: чем отличаются и как переносить правила
nginx не читает .htaccess вообще — это архитектурное решение. Вся конфигурация задаётся в блоках server {} и location {}, загружается один раз при старте и не требует чтения файлов при каждом запросе. Эта статья — таблица соответствий Apache↔nginx, ключевые различия (в том числе грабли с add_header) и ссылка на конвертер .htaccess → nginx.
Почему nginx не читает .htaccess
Это сознательное архитектурное решение. В Apache .htaccess — механизм per-directory конфигурации: файл лежит рядом с данными и читается Apache при каждом запросе ко всем файлам в этом каталоге. Это удобно для shared-хостинга (нет доступа к главному конфигу), но создаёт оверхед — дисковые операции на каждый запрос.
nginx спроектирован иначе: вся конфигурация описывается в файлах конфига (обычно /etc/nginx/sites-available/), загружается один раз при старте или перезагрузке (nginx -s reload) и хранится в памяти. Нет никаких .htaccess-файлов, нет дисковых операций при запросах. Следствие: если у вас Server: nginx и .htaccess лежит в каталоге — он просто не читается и не действует. Никаких ошибок — просто игнорируется.
Проверить, какой у вас сервер: curl -I https://example.com — смотрите заголовок Server:.
Таблица соответствий Apache↔nginx
RewriteEngine On+RewriteRule ^old$ /new [L]→rewrite ^/old$ /new last;илиreturn 301 /new;(вlocation)Redirect 301 /old /new→return 301 /new;RedirectMatch 301 ^/old/(.*)$ /new/$1→rewrite ^/old/(.*)$ /new/$1 permanent;<FilesMatch "\.(sql|bak)$"> Require all denied→location ~* \.(sql|bak)$ { deny all; }Header [always] set Name Value→add_header Name Value [always];(но см. грабли ниже)Header unset X-Powered-By→more_clear_headers X-Powered-By;(модульheaders_more)ErrorDocument 404 /404.html→error_page 404 /404.html;ExpiresByType text/css "access plus 1 month"→location ~* \.css$ { expires 30d; }(по расширению, не по MIME)Options -Indexes→autoindex off;DirectoryIndex index.php index.html→index index.php index.html;Order Allow,Deny+Allow from all→allow all;Require all denied→deny all;Require ip 192.168.1.0/24→allow 192.168.1.0/24; deny all;AuthType Basic+AuthUserFile /path/.htpasswd+Require valid-user→auth_basic "Area";+auth_basic_user_file /path/.htpasswd;AddOutputFilterByType DEFLATE text/html text/css→gzip on; gzip_types text/html text/css;RewriteCond %{REQUEST_FILENAME} !-f+RewriteRule . index.php [L](фронт-контроллер) →try_files $uri $uri/ /index.php$is_args$args;
Что в nginx делается иначе
- Нет директивы
Options— управление симлинками:disable_symlinks on;; нет аналогаMultiViews(используйтеtry_files). - PHP через FastCGI — не модуль, как в Apache (
mod_php), а отдельный процесс PHP-FPM. Настройка:location ~ \.php$ { fastcgi_pass unix:/run/php/php8.2-fpm.sock; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; }. - Переменные окружения —
SetEnvIf/[E=…]аналогов нет напрямую; используйтеmap { }илиset $var value;. RewriteMap→map { }в блокеhttp {}.- Apache
<If>→if ()в nginx (ноifв nginx — ограниченный, часто лучшеmap). - Нет
php_value/php_flag— настройки PHP задаются в конфиге PHP-FPM (php.ini/ пул-конфиг) или черезfastcgi_param PHP_VALUE "…";.
Грабля: add_header не наследуется
В Apache директива Header в родительском <Directory> или <VirtualHost> наследуется во вложенные блоки <Location>. В nginx — нет: если в дочернем блоке location есть хотя бы один add_header, все add_header из родительского блока теряются.
server { # Этот заголовок НЕ будет отправляться из location /api/, # потому что там есть свой add_header: add_header X-Frame-Options "SAMEORIGIN" always; location /api/ { add_header Cache-Control "no-store" always; # X-Frame-Options здесь уже НЕ добавляется! proxy_pass http://backend; } }
server { location /api/ { # Повторяем все нужные заголовки: add_header X-Frame-Options "SAMEORIGIN" always; add_header Cache-Control "no-store" always; proxy_pass http://backend; } }
Схема nginx-фронт + Apache-бэк
Популярная схема на shared-хостингах с панелями управления (ISPmanager, Plesk в «proxy mode»): nginx принимает запросы, отдаёт статику (CSS, JS, картинки), а динамику проксирует на Apache, работающий на внутреннем порту (например, 8080). В этой схеме:
- Статику (файлы, которые nginx отдаёт напрямую)
.htaccessне обрабатывает — там нет Apache. - Динамику (PHP-запросы, которые nginx проксирует на Apache)
.htaccessобрабатывает на стороне Apache.
Если хостинг переключил сайт в режим «только nginx» (nginx-only без Apache-бэка) — .htaccess полностью перестаёт действовать. Как это проверить — в хабе «.htaccess на хостингах» → раздел про чистый nginx.
Конвертер .htaccess → nginx
На сайте есть конвертер .htaccess → nginx: вставьте содержимое .htaccess — получите примерный nginx-конфиг. Конвертер переводит контроль доступа, заголовки, страницы ошибок, кэш, gzip, типовые RewriteRule. Что не переводится точно (произвольные цепочки RewriteCond, php_value, RewriteMap) — попадает в список «Нужно проверить вручную». Проверяйте каждую строку перед использованием в продакшене.
Когда выбрать Apache, когда nginx
- Apache: shared-хостинг (там нет выбора), нужен
.htaccessper-directory, простота конфигурации через файлы рядом с кодом, приложения, заточенные под Apache (старый WordPress, некоторые CMS). - nginx: высокая нагрузка, отдача статики, reverse-proxy, микросервисы, минимальное потребление памяти — всё в одном конфиге, без per-directory overhead.
- Оба вместе: nginx-фронт (статика, SSL-терминация, rate-limiting) + Apache/PHP-FPM-бэк (динамика,
.htaccessработает на Apache) — классика для нагруженных shared-хостингов.
Ссылки
- Конвертер .htaccess → nginx — автоматический перевод.
- «.htaccess на разных хостингах» — cPanel, ISPmanager, Plesk, nginx-only.
- «Как работает mod_rewrite» — синтаксис RewriteRule, RewriteCond, флаги.
- «PCRE: регулярные выражения для mod_rewrite».
- Справочник директив .htaccess.
- Линтер .htaccess — синтаксис, устаревшие директивы.