From 0d2c6277e103a87da5462ba8ec07abab80bc0da3 Mon Sep 17 00:00:00 2001 From: alexey Date: Mon, 23 Mar 2026 01:15:59 +0300 Subject: [PATCH] Added mod_rewrite for nginx module --- .gitignore | 10 + BUILD.md | 182 ++ BUILD.ru.md | 182 ++ CHANGELOG | 8 + LICENSE | 228 ++ README.md | 470 +++ README.ru.md | 481 +++ build | 20 + cms/drupal-mod_headers/.htaccess | 25 + cms/drupal/.htaccess | 19 + cms/drupal/.htpasswd | 1 + cms/drupal/.well-known/robots.txt | 6 + cms/drupal/README.md | 162 + cms/drupal/core/install.php | 1 + cms/drupal/core/modules/system/tests/http.php | 1 + .../core/modules/system/tests/https.php | 1 + cms/drupal/core/rebuild.php | 1 + cms/drupal/favicon.ico | 2 + cms/drupal/index.php | 15 + cms/drupal/nginx.conf | 159 + cms/drupal/site5.conf | 20 + .../all/default/settings.php.dummy/.gitkeep | 0 cms/drupal/somedir/.gitkeep | 0 cms/drupal/somedir/index.php | 3 + cms/drupal/test-drupal-rewriterules.sh | 182 ++ cms/grav/.htaccess | 74 + cms/grav/.htaccess.test | 1 + cms/grav/.htpasswd | 1 + cms/grav/.well-known/robots.txt | 1 + cms/grav/LICENSE.txt | 1 + cms/grav/README.md | 119 + cms/grav/backup/archive.zip | 1 + cms/grav/bin/helper.php | 1 + cms/grav/cache/test.txt | 1 + cms/grav/composer.json | 1 + cms/grav/composer.lock | 1 + cms/grav/existing.jpg | 0 cms/grav/index.php | 15 + cms/grav/nginx.conf | 159 + cms/grav/normal-page.md | 1 + cms/grav/site2.conf | 14 + cms/grav/somedir/.gitkeep | 0 cms/grav/system/config.xml | 1 + cms/grav/test-mustache.php | 1 + cms/grav/test-rewriterules.sh | 150 + cms/grav/test.md | 1 + cms/grav/tests/unit-test.php | 1 + cms/grav/twig-test.html | 1 + cms/grav/user/config.yml | 1 + cms/grav/user/data.json | 1 + cms/grav/user/module.php | 1 + cms/grav/user/script.sh | 1 + cms/grav/user/settings.yaml | 1 + cms/grav/user/template.twig | 1 + cms/grav/user/test.txt | 1 + cms/grav/vendor/module.txt | 1 + cms/grav/webserver-configs/nginx.conf | 1 + cms/joomla/.htaccess | 16 + cms/joomla/.well-known/robots.txt | 6 + cms/joomla/README.md | 153 + cms/joomla/api/index.php | 56 + cms/joomla/api/index.php.dummy | 1 + cms/joomla/base64-test.php | 2 + cms/joomla/globals-test.php | 2 + cms/joomla/index.php | 15 + cms/joomla/nginx.conf | 159 + cms/joomla/request-test.php | 2 + cms/joomla/script-test.php | 2 + cms/joomla/site4.conf | 16 + cms/joomla/test-joomla-rewriterules.sh | 170 ++ cms/simple/.htaccess | 73 + cms/simple/README.md | 127 + cms/simple/SUMMARY.md | 311 ++ cms/simple/index.html | 40 + cms/simple/nginx.conf | 159 + cms/simple/redirect.html | 26 + cms/simple/show.html | 26 + cms/simple/site1.conf | 10 + cms/simple/somedir/.gitkeep | 0 cms/simple/stop.html | 23 + cms/simple/subdir/.gitkeep | 0 cms/simple/subdir/.htaccess | 30 + cms/simple/subdir/file.html | 26 + cms/simple/subdir/other.xmx | 27 + cms/simple/test.xmx | 26 + cms/wordpress-multy/.htaccess | 14 + cms/wordpress-multy/README.md | 152 + cms/wordpress-multy/custom-script.php | 6 + cms/wordpress-multy/index.php | 15 + cms/wordpress-multy/script.js | 4 + cms/wordpress-multy/style.css | 8 + .../test-wp-multi-rewriterules.sh | 171 ++ cms/wordpress-multy/wp-admin/admin.php | 2 + .../wp-content/themes/.gitkeep | 0 .../wp-content/uploads/test.jpg | 2 + cms/wordpress-multy/wp-includes/js/jquery.js | 2 + cms/wordpress/nginx.conf | 159 + cms/wordpress/site3.conf | 16 + cms/wordpress/test-wp-rewriterules.sh | 140 + cms/wordpress/wordpress/.htaccess | 8 + cms/wordpress/wordpress/README.md | 109 + cms/wordpress/wordpress/existing.jpg | 0 cms/wordpress/wordpress/index.php | 15 + cms/wordpress/wordpress/somedir/.gitkeep | 0 cms/wordpress/wordpress/wp_api.php | 54 + modules/mod_rewrite/config | 23 + .../ngx_http_apache_rewrite_engine.c | 857 ++++++ .../ngx_http_apache_rewrite_engine.h | 332 ++ .../ngx_http_apache_rewrite_expand.c | 337 +++ .../ngx_http_apache_rewrite_fastcgi.c | 387 +++ .../ngx_http_apache_rewrite_fastcgi.h | 90 + .../mod_rewrite/ngx_http_apache_rewrite_map.c | 178 ++ .../ngx_http_apache_rewrite_module.c | 2676 +++++++++++++++++ .../ngx_http_apache_rewrite_variable.c | 562 ++++ package_preparer.sh | 261 ++ packages/deb/README.build | 68 + packages/deb/changelog | 5 + packages/deb/control | 21 + packages/deb/copyright | 26 + packages/deb/postinst | 24 + packages/deb/postrm | 21 + packages/deb/rules | 28 + packages/deb/triggers | 10 + packages/rpm/nginx-mod-rewrite.spec | 58 + 124 files changed, 11079 insertions(+) create mode 100644 .gitignore create mode 100644 BUILD.md create mode 100644 BUILD.ru.md create mode 100644 CHANGELOG create mode 100644 LICENSE create mode 100644 README.md create mode 100644 README.ru.md create mode 100644 build create mode 100644 cms/drupal-mod_headers/.htaccess create mode 100644 cms/drupal/.htaccess create mode 100644 cms/drupal/.htpasswd create mode 100644 cms/drupal/.well-known/robots.txt create mode 100644 cms/drupal/README.md create mode 100644 cms/drupal/core/install.php create mode 100644 cms/drupal/core/modules/system/tests/http.php create mode 100644 cms/drupal/core/modules/system/tests/https.php create mode 100644 cms/drupal/core/rebuild.php create mode 100644 cms/drupal/favicon.ico create mode 100644 cms/drupal/index.php create mode 100644 cms/drupal/nginx.conf create mode 100644 cms/drupal/site5.conf create mode 100644 cms/drupal/sites/all/default/settings.php.dummy/.gitkeep create mode 100644 cms/drupal/somedir/.gitkeep create mode 100644 cms/drupal/somedir/index.php create mode 100755 cms/drupal/test-drupal-rewriterules.sh create mode 100644 cms/grav/.htaccess create mode 100644 cms/grav/.htaccess.test create mode 100644 cms/grav/.htpasswd create mode 100644 cms/grav/.well-known/robots.txt create mode 100644 cms/grav/LICENSE.txt create mode 100644 cms/grav/README.md create mode 100644 cms/grav/backup/archive.zip create mode 100644 cms/grav/bin/helper.php create mode 100644 cms/grav/cache/test.txt create mode 100644 cms/grav/composer.json create mode 100644 cms/grav/composer.lock create mode 100644 cms/grav/existing.jpg create mode 100644 cms/grav/index.php create mode 100644 cms/grav/nginx.conf create mode 100644 cms/grav/normal-page.md create mode 100644 cms/grav/site2.conf create mode 100644 cms/grav/somedir/.gitkeep create mode 100644 cms/grav/system/config.xml create mode 100644 cms/grav/test-mustache.php create mode 100755 cms/grav/test-rewriterules.sh create mode 100644 cms/grav/test.md create mode 100644 cms/grav/tests/unit-test.php create mode 100644 cms/grav/twig-test.html create mode 100644 cms/grav/user/config.yml create mode 100644 cms/grav/user/data.json create mode 100644 cms/grav/user/module.php create mode 100644 cms/grav/user/script.sh create mode 100644 cms/grav/user/settings.yaml create mode 100644 cms/grav/user/template.twig create mode 100644 cms/grav/user/test.txt create mode 100644 cms/grav/vendor/module.txt create mode 100644 cms/grav/webserver-configs/nginx.conf create mode 100644 cms/joomla/.htaccess create mode 100644 cms/joomla/.well-known/robots.txt create mode 100644 cms/joomla/README.md create mode 100644 cms/joomla/api/index.php create mode 100644 cms/joomla/api/index.php.dummy create mode 100644 cms/joomla/base64-test.php create mode 100644 cms/joomla/globals-test.php create mode 100644 cms/joomla/index.php create mode 100644 cms/joomla/nginx.conf create mode 100644 cms/joomla/request-test.php create mode 100644 cms/joomla/script-test.php create mode 100644 cms/joomla/site4.conf create mode 100755 cms/joomla/test-joomla-rewriterules.sh create mode 100644 cms/simple/.htaccess create mode 100644 cms/simple/README.md create mode 100644 cms/simple/SUMMARY.md create mode 100644 cms/simple/index.html create mode 100644 cms/simple/nginx.conf create mode 100644 cms/simple/redirect.html create mode 100644 cms/simple/show.html create mode 100644 cms/simple/site1.conf create mode 100644 cms/simple/somedir/.gitkeep create mode 100644 cms/simple/stop.html create mode 100644 cms/simple/subdir/.gitkeep create mode 100644 cms/simple/subdir/.htaccess create mode 100644 cms/simple/subdir/file.html create mode 100644 cms/simple/subdir/other.xmx create mode 100644 cms/simple/test.xmx create mode 100644 cms/wordpress-multy/.htaccess create mode 100644 cms/wordpress-multy/README.md create mode 100644 cms/wordpress-multy/custom-script.php create mode 100644 cms/wordpress-multy/index.php create mode 100644 cms/wordpress-multy/script.js create mode 100644 cms/wordpress-multy/style.css create mode 100755 cms/wordpress-multy/test-wp-multi-rewriterules.sh create mode 100644 cms/wordpress-multy/wp-admin/admin.php create mode 100644 cms/wordpress-multy/wp-content/themes/.gitkeep create mode 100644 cms/wordpress-multy/wp-content/uploads/test.jpg create mode 100644 cms/wordpress-multy/wp-includes/js/jquery.js create mode 100644 cms/wordpress/nginx.conf create mode 100644 cms/wordpress/site3.conf create mode 100755 cms/wordpress/test-wp-rewriterules.sh create mode 100644 cms/wordpress/wordpress/.htaccess create mode 100644 cms/wordpress/wordpress/README.md create mode 100644 cms/wordpress/wordpress/existing.jpg create mode 100644 cms/wordpress/wordpress/index.php create mode 100644 cms/wordpress/wordpress/somedir/.gitkeep create mode 100644 cms/wordpress/wordpress/wp_api.php create mode 100644 modules/mod_rewrite/config create mode 100644 modules/mod_rewrite/ngx_http_apache_rewrite_engine.c create mode 100644 modules/mod_rewrite/ngx_http_apache_rewrite_engine.h create mode 100644 modules/mod_rewrite/ngx_http_apache_rewrite_expand.c create mode 100644 modules/mod_rewrite/ngx_http_apache_rewrite_fastcgi.c create mode 100644 modules/mod_rewrite/ngx_http_apache_rewrite_fastcgi.h create mode 100644 modules/mod_rewrite/ngx_http_apache_rewrite_map.c create mode 100644 modules/mod_rewrite/ngx_http_apache_rewrite_module.c create mode 100644 modules/mod_rewrite/ngx_http_apache_rewrite_variable.c create mode 100644 package_preparer.sh create mode 100644 packages/deb/README.build create mode 100644 packages/deb/changelog create mode 100644 packages/deb/control create mode 100644 packages/deb/copyright create mode 100755 packages/deb/postinst create mode 100755 packages/deb/postrm create mode 100755 packages/deb/rules create mode 100644 packages/deb/triggers create mode 100644 packages/rpm/nginx-mod-rewrite.spec diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6f78aef --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +nginx-1.25.30 +.clangd +.devcontainer +Dockerfile +logs +nginx-1.25.3 +.zed +tmpbuild + + diff --git a/BUILD.md b/BUILD.md new file mode 100644 index 0000000..fd09676 --- /dev/null +++ b/BUILD.md @@ -0,0 +1,182 @@ +# Package Build + +## Building a binary file using a Docker image + +To build the image, download and extract nginx to the project root: +For example: +``` +wget https://nginx.org/download/nginx-1.26.3.tar.gz +tar xvf nginx-1.26.3.tar.gz +``` +Next, build the image using the Dockerfile in the project root (build for almalinux:9): +``` +docker build -t nginx-mod_rewrite . +``` + +We obtain an image with nginx and the built mod_rewrite. + +To run the image, you need to execute the command: +``` +docker run --name nginx-mod_rewrite -p 8080:80 -p 8081:8081 nginx-mod_rewrite +``` + +## Building the package for a specified operating system + +In the project root there is a script `package_preparer.sh` that allows you to build an rpm or deb package for a specific operating system. Which OS and native nginx version the package will be built for depends on the image used. + +To build the package, run the command: +``` +bash package_preparer.sh prepare "almalinux:9" +``` + +The build was tested on the following images: + +* almalinux:9 +* almalinux:8 +* rockylinux:9 +* ubuntu:24.04 +* debian:stable + +After the build, the packages will appear in the `tmpbuild` directory: +``` +$ ls -1 tmpbuild/*.rpm +tmpbuild/nginx-mod-rewrite-0.1-1.el9.src.rpm +tmpbuild/nginx-mod-rewrite-0.1-1.el9.x86_64.rpm +tmpbuild/nginx-mod-rewrite-debuginfo-0.1-1.el9.x86_64.rpm +tmpbuild/nginx-mod-rewrite-debugsource-0.1-1.el9.x86_64.rpm +``` +or +``` +$ ls -1 tmpbuild/nginx-mod-rewrite* +tmpbuild/nginx-mod-rewrite_0.1-1_amd64.buildinfo +tmpbuild/nginx-mod-rewrite_0.1-1_amd64.changes +tmpbuild/nginx-mod-rewrite_0.1-1_amd64.deb +tmpbuild/nginx-mod-rewrite_0.1-1.dsc +tmpbuild/nginx-mod-rewrite_0.1-1.tar.gz +tmpbuild/nginx-mod-rewrite-0.1.tar.gz +tmpbuild/nginx-mod-rewrite-dbgsym_0.1-1_amd64.deb +``` + +To ensure that the command +```bash +bash -x package_preparer.sh prepare "os-image" +``` +works correctly, the following utilities must be installed and available on the system (each of which comes from a package supplied by your Linux distribution): + +| Utility | What it does in the `prepare` section | Package (Debian/Ubuntu) | +|---------|---------------------------------------|--------------------------| +| **`bash`** | Runs the script itself | already installed | +| **`rm`, `mkdir`, `cp`, `find`, `head`, `gzip`, `tee`** | Cleans/creates directories, copies files, searches, compresses, logs | `coreutils` | +| **`sed`** (GNU sed) | Parameterizes templates, removes/replaces strings | `sed` (GNU sed, the `sed` package from coreutils) | +| **`awk`** | Extracts the version from `CHANGELOG`, generates the changelog file | `gawk` (or the `awk` package) | +| **`tar`** | Creates tar archives, adds files | `tar` | +| **`docker`** (commands `docker build`, `docker run`, `docker rmi`) | Builds a temporary image, runs a container, cleans up | `docker.io` / `docker-ce` + running **daemon** | + +> **Note on `sed`** +> The script uses extended regular expressions (`sed -E`) and in-place file modification (`sed -i`). This is the GNU variant of `sed`. On systems where `sed` is the BSD variant (e.g., macOS), the command `sed -E -i` will not work, so in such environments you need to install GNU `sed` (`gsed`) and replace calls with `gsed`. + +The Docker daemon must be running, and the user must have permission to use it (or be in the `docker` group). + +The main build occurs in a Docker container, so the base system does not get cluttered with extra packages. The set of utilities used by the script is present in the distribution by default, so you only need to install Docker. + +The builder analyzes the nginx version installed in the distribution and performs the build in an environment with source files of the same nginx version, using commands similar to those used when building nginx from the distribution. + +## Manual build from sources + +If you need to build the module yourself, you need to run the following commands. + +Here is an example for AlmaLinux 9. + +Install the necessary build packages: +``` +dnf -y install openssl-devel pcre-devel zlib-devel gcc gcc-c++ make wget +``` + +In the project root: +``` +bash package_preparer.sh download 1.22.1 +``` + +You can specify any available version on the website `https://nginx.org/ru/download.html`; it will be downloaded and extracted into the current directory. + +Then change directory into nginx-1.22.1: +``` +cd nginx-1.22.1 +``` +and set the build configuration. Here is an example configuration, it may differ; this example is a typical configuration: +``` +./configure \ + --with-compat \ + --add-dynamic-module=../modules/mod_rewrite \ + --prefix=/etc/nginx \ + --sbin-path=/usr/sbin/nginx \ + --modules-path=/etc/nginx/modules \ + --conf-path=/etc/nginx/nginx.conf \ + --error-log-path=/var/log/nginx/error.log \ + --http-log-path=/var/log/nginx/access.log \ + --pid-path=/var/run/nginx.pid \ + --lock-path=/var/run/nginx.lock \ + --http-client-body-temp-path=/var/lib/nginx/body \ + --http-proxy-temp-path=/var/lib/nginx/proxy \ + --http-fastcgi-temp-path=/var/lib/nginx/fastcgi \ + --http-uwsgi-temp-path=/var/lib/nginx/uwsgi \ + --http-scgi-temp-path=/var/lib/nginx/scgi \ + --with-http_ssl_module \ + --with-http_v2_module \ + --with-http_realip_module \ + --with-http_gzip_static_module \ + --with-http_stub_status_module \ + --with-http_slice_module \ + --with-http_dav_module \ + --with-http_auth_request_module \ + --with-http_secure_link_module \ + --with-stream \ + --with-stream_ssl_module \ + --with-stream_realip_module \ + --with-stream_ssl_preread_module \ + --with-pcre-jit +``` + +During execution, the following should appear: +``` +adding module in ../modules/mod_rewrite + + ngx_http_apache_rewrite_module was configured +``` + +You can add `--add-dynamic-module=../modules/mod_rewrite` to your nginx configuration. + +Next: +``` +make modules +``` +This command will build the module: +``` +# ls -1 objs/ngx_http_apache_rewrite_module.so +objs/ngx_http_apache_rewrite_module.so +``` +Then you can move it to the nginx modules directory: +``` +cp objs/ngx_http_apache_rewrite_module.so /etc/nginx/modules/ +``` +and load the module in the nginx configuration file: +``` + +cat /etc/nginx/nginx.conf + +load_module modules/ngx_http_apache_rewrite_module.so; + +... + +server { + ... + HtaccessEnable on; + + RewriteEngine On; + + location / { + RewriteEngine On; + + } + + } +``` diff --git a/BUILD.ru.md b/BUILD.ru.md new file mode 100644 index 0000000..d3f983a --- /dev/null +++ b/BUILD.ru.md @@ -0,0 +1,182 @@ +# Сборка пакета + +## Сборка бинарного файла с использованием docker-образа + +Для сборки образа скачайте и разархивируйте nginx в корень проекта: +Например так: +``` +wget https://nginx.org/download/nginx-1.26.3.tar.gz +tar xvf nginx-1.26.3.tar.gz +``` +Далее соберите образ, используя Dockerfile в корне проекта (сборка под almalinux:9): +``` +docker build -t nginx-mod_rewrite . +``` + +Получаем образ с nginx и собранным mod_rewrite. + +Для запуска образа необходимо выполнить команду: +``` +docker run --name nginx-mod_rewrite -p 8080:80 -p 8081:8081 nginx-mod_rewrite +``` + +## Сборка пакета под заданную операционную систему + +В корне проекта расположен скрипт `package_preparer.sh`, который позволяет собрать rpm или deb пакет под определенную операционную систему. Под какую операционную систему и нативную версию nginx будет собран пакет, зависит от используемого образа. + +Для сборки пакета необходимо запустить команду: +``` +bash package_preparer.sh prepare "almalinux:9" +``` + +Сборка проверялась на образах: + +* almalinux:9 +* almalinux:8 +* rockylinux:9 +* ubuntu:24.04 +* debian:stable + + + +После сборки собранные пакеты появятся в каталоге tmpbuild: +``` +$ ls -1 tmpbuild/*.rpm +tmpbuild/nginx-mod-rewrite-0.1-1.el9.src.rpm +tmpbuild/nginx-mod-rewrite-0.1-1.el9.x86_64.rpm +tmpbuild/nginx-mod-rewrite-debuginfo-0.1-1.el9.x86_64.rpm +tmpbuild/nginx-mod-rewrite-debugsource-0.1-1.el9.x86_64.rpm +``` +или +``` +$ ls -1 tmpbuild/nginx-mod-rewrite* +tmpbuild/nginx-mod-rewrite_0.1-1_amd64.buildinfo +tmpbuild/nginx-mod-rewrite_0.1-1_amd64.changes +tmpbuild/nginx-mod-rewrite_0.1-1_amd64.deb +tmpbuild/nginx-mod-rewrite_0.1-1.dsc +tmpbuild/nginx-mod-rewrite_0.1-1.tar.gz +tmpbuild/nginx-mod-rewrite-0.1.tar.gz +tmpbuild/nginx-mod-rewrite-dbgsym_0.1-1_amd64.deb +``` + +Для того чтобы команда +```bash +bash -x package_preparer.sh prepare "os-image" +``` +работала корректно, в системе должны быть установлены и доступны следующие утилиты (каждая из которых берётся из пакета, поставляемого в вашем дистрибутиве Linux): + +| Утилита | Что делает в `prepare`‑разделе | Пакет (Debian/Ubuntu) | +|---------|--------------------------------|------------------------| +| **`bash`** | Запуск самого скрипта | уже установлен | +| **`rm`, `mkdir`, `cp`, `find`, `head`, `gzip`, `tee`** | Очистка/создание каталогов, копирование файлов, поиск, сжатие, логирование | `coreutils` | +| **`sed`** (GNU sed) | Параметризация шаблонов, удаление/замена строк | `sed` (GNU sed, пакет `sed` из coreutils) | +| **`awk`** | Извлечение версии из `CHANGELOG`, генерация changelog‑файла | `gawk` (или `awk`‑пакет) | +| **`tar`** | Создание tar‑архива, добавление файлов | `tar` | +| **`docker`** (команды `docker build`, `docker run`, `docker rmi`) | Построение временного образа, запуск контейнера, очистка | `docker.io` / `docker-ce` + работающий **daemon** | + +> **Замечание по `sed`** +> В скрипте используются расширенные регулярные выражения (`sed -E`) и модификация файла на месте (`sed -i`). Это GNU‑вариант `sed`. На системах, где `sed` – BSD‑вариант (например, macOS), команда `sed -E -i` не будет работать, поэтому в таких окружениях нужно установить GNU‑`sed` (`gsed`) и заменить вызовы на `gsed`. + +Docker‑демон должен быть запущен, и пользователь должен иметь права на его использование (или быть в группе `docker`). + +Основная сборка происходит в docker‑контейнере, поэтому основная система не засоряется лишними пакетами. Основной набор утилит, используемых скриптом, присутствует в дистрибутиве по умолчанию, поэтому необходимо только установить docker. + +Сборщик анализирует версию nginx, установленную в дистрибутиве, и производит сборку в окружении исходных файлов аналогичной версии nginx дистрибутива, а также командами, аналогичными, которые использовались при сборке nginx из дистрибутива. + +## Ручная сборка из исходников + +Если же необходимо собрать модуль самостоятельно, необходимо выполнить следующие команды. + +Привожу пример для Almalinux 9. + +Установите необходимые пакеты для сборки: +``` +dnf -y install openssl-devel pcre-devel zlib-devel gcc gcc-c++ make wget +``` + +В корне проекта: +``` +bash package_preparer.sh download 1.22.1 +``` + +В качестве версии можно задать любую доступную на сайте `https://nginx.org/ru/download.html`, она будет скачана и распакована в текущий каталог. + +Далее перейти в папку nginx-1.22.1: +``` +cd nginx-1.22.1 +``` +и задать конфигурацию сборки. Я привожу пример конфигурации, она может отличаться; данный пример — это типичная конфигурация: +``` +./configure \ + --with-compat \ + --add-dynamic-module=../modules/mod_rewrite \ + --prefix=/etc/nginx \ + --sbin-path=/usr/sbin/nginx \ + --modules-path=/etc/nginx/modules \ + --conf-path=/etc/nginx/nginx.conf \ + --error-log-path=/var/log/nginx/error.log \ + --http-log-path=/var/log/nginx/access.log \ + --pid-path=/var/run/nginx.pid \ + --lock-path=/var/run/nginx.lock \ + --http-client-body-temp-path=/var/lib/nginx/body \ + --http-proxy-temp-path=/var/lib/nginx/proxy \ + --http-fastcgi-temp-path=/var/lib/nginx/fastcgi \ + --http-uwsgi-temp-path=/var/lib/nginx/uwsgi \ + --http-scgi-temp-path=/var/lib/nginx/scgi \ + --with-http_ssl_module \ + --with-http_v2_module \ + --with-http_realip_module \ + --with-http_gzip_static_module \ + --with-http_stub_status_module \ + --with-http_slice_module \ + --with-http_dav_module \ + --with-http_auth_request_module \ + --with-http_secure_link_module \ + --with-stream \ + --with-stream_ssl_module \ + --with-stream_realip_module \ + --with-stream_ssl_preread_module \ + --with-pcre-jit +``` + +Во время выполнения команды должно появиться: +``` +adding module in ../modules/mod_rewrite + + ngx_http_apache_rewrite_module was configured +``` + +Можно добавить `--add-dynamic-module=../modules/mod_rewrite` в свою конфигурацию nginx. + +Далее: +``` +make modules +``` +данная команда соберет модуль: +``` +# ls -1 objs/ngx_http_apache_rewrite_module.so +objs/ngx_http_apache_rewrite_module.so +``` +Далее его можно переложить в каталог модулей nginx: +``` +cp objs/ngx_http_apache_rewrite_module.so /etc/nginx/modules/ +``` +и подключить модуль в файле конфигурации nginx: +``` +cat /etc/nginx/nginx.conf + +load_module modules/ngx_http_apache_rewrite_module.so; + +... + +server { + ... + HtaccessEnable on; + + RewriteEngine On; + + location / { + RewriteEngine On; + + } + + } diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 0000000..80977be --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,8 @@ +version: 0.1-1 + +-- +* Sat Mar 14 2026 Alexey BayRepo - 0.1-1 +- Added debian/ubuntu build + +* Fri Mar 13 2026 Alexey BayRepo - 0.1-0 +- New package mod_rewrite for nginx diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4ee4e1c --- /dev/null +++ b/LICENSE @@ -0,0 +1,228 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2026 Alexey BayRepo + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/* + * Copyright (C) 2002-2021 Igor Sysoev + * Copyright (C) 2011-2021 Nginx, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ diff --git a/README.md b/README.md new file mode 100644 index 0000000..8e41ac7 --- /dev/null +++ b/README.md @@ -0,0 +1,470 @@ +## Apache mod_rewrite Compatibility Module for Nginx + +This module provides Apache's `mod_rewrite` compatibility for Nginx, enabling `.htaccess` file support and standard rewrite rules. + +--- + +# Configuration Directives + +The following directives are available in this module: + +### 1. RewriteEngine + +**Purpose:** Enable or disable the rewrite engine. + +| Context | Available Levels | +|----------------|---------------------------------------------| +| `main`, `server`, `location` | ✓ All three levels supported | + +**Syntax:** +```nginx +RewriteEngine on|off +``` + +**Possible Values:** +- `on` - Enable rewrite engine +- `off` - Disable rewrite engine (default) + +**Example in nginx.conf:** +```nginx +http { + server { + RewriteEngine on; + + location /blog/ { + # Rules apply here + } + + location /static/ { + RewriteEngine off; # Disable for this location + } + } +} +``` + +--- + +### 2. RewriteRule + +**Purpose:** Define a rewrite rule that matches a URL pattern and substitutes it with a new value. + +| Context | Available Levels | +|----------------|---------------------------------------------| +| `server`, `location` | ✓ Server and Location level | +| `.htaccess` | ✓ Supported in .htaccess files | + +**Syntax:** +```nginx +RewriteRule pattern substitution [flags] +``` + +**Parameters:** + +- **pattern** - Regular expression to match the URL (supports backreferences $1, $2, etc.) + - Pattern can be prefixed with `!` for negation + - `-` as substitution means "no substitution" (only match for conditions) + +- **substitution** - Target URL or file path to rewrite to + - Can be a relative path (`/new/path`) or absolute redirect (`http://example.com/new`) + +**Flags:** `[flag1,flag2,...]` (optional): + +| Flag | Abbreviation | Description | +|------|--------------|-------------| +| `B` | - | Escape backreferences in output (prevent URL encoding issues) | +| `C` | Chain | Chain this rule with the next one (requires previous match) | +| `D` | DPI | Discard path info after substitution | +| `E` | Env=var:val | Set an environment variable for later use (FastCGI integration) | +| `F` | Forbidden | Return HTTP 403 Forbidden status | +| `G` | Gone | Return HTTP 410 Gone status | +| `L` | Last | Stop processing rules after this one | +| `N` | Next=limit | Loop (retry) up to N times for infinite rewriting prevention | +| `P` | Proxy | Proxy the request internally (Phase 2) | +| `Q` | QSA | Append original query string to substitution URL | +| `Q` | QSD | Discard original query string | +| `Q` | QSL | Set "query string last" marker | +| `R` | Redirect=code| Perform external redirect with status code (301, 302, 307) or: Permanent, Temp, SeeOther | +| `S` | Skip=N | Skip N subsequent rules after this one | +| `T` | Type=mime | Force specific MIME type for the response | +| `NC` | - | Case-insensitive matching | +| `NOESCAPE` | - | Do not escape special characters in output | +| `END`| - | Stop all rewriting and pass to content phase | + +**Example:** +```nginx +# Redirect /old-path/ to /new-path/ (permanent redirect) +RewriteRule ^old-path/(.*)$ /new-path/$1 [R=301,L] + +# Condition-based rewrite (chain) +RewriteCond %REQUEST_URI !^/admin/ +RewriteRule ^admin/(.*)$ /login.php?user=$1 [NC,E,END] + +# In .htaccess: +RewriteEngine on +RewriteCond %{HTTP_HOST} ^www\.example\.com$ [OR] +RewriteCond %{HTTPS} off +RewriteRule ^(.*)$ https://example.com/$1 [R=301,L] +``` + +--- + +### 3. RewriteCond + +**Purpose:** Define conditions that must be met before a `RewriteRule` is applied. Multiple conditions are ANDed together; OR flag can change this behavior. + +| Context | Available Levels | +|----------------|---------------------------------------------| +| `server`, `location` | ✓ Server and Location level | +| `.htaccess` | ✓ Supported in .htaccess files | + +**Syntax:** +```nginx +RewriteCond input_string pattern [flags] +``` + +**Parameters:** + +- **input_string** - Variable or string to test (e.g., `%REQUEST_URI`, `%HTTP_HOST`, `%REQUEST_FILENAME`) + - Common variables: `REQUEST_URI`, `HTTP_HOST`, `REQUEST_METHOD`, `REQUEST_FILENAME` + +- **pattern** - Test pattern for the condition: + - **File tests:** `-f` (file exists), `-d` (directory exists), `-x` (executable), `-s` (non-zero size) + - **Link tests:** `-h` or `-l` (hard/soft link), `-L` (link target exists) + - **Integer comparisons:** `-lt`, `-le`, `-eq`, `-gt`, `-ge`, `-ne` + - **String comparisons:** `=`, `>`, `<`, `>=`, `<=` + - **Regular expressions:** Any regex pattern + +- **flags** (optional): + - `NC` - Case-insensitive matching + - `OR` / `ornext` - OR logic between conditions (instead of AND) + +**Examples:** +```nginx +# Condition: file exists +RewriteCond %REQUEST_FILENAME -f + +# Condition: not a directory +RewriteCond %REQUEST_FILENAME !-d + +# Condition: string comparison +RewriteCond %{HTTP_HOST} ^www\.example\.com$ [NC] + +# Integer comparison (file size > 1024 bytes) +RewriteCond %REQUEST_FILENAME -s +RewriteCond %{FILESIZE} -gt 1024 + +# Multiple conditions with OR logic +RewriteCond %{HTTP_HOST} ^www\.example\.com$ [OR] +RewriteCond %{HTTPS} off + +# Regular expression pattern +RewriteCond %{REQUEST_URI} ^/old/(.*)$ +``` + +--- + +### 4. RewriteBase + +**Purpose:** Set the base URL path for relative substitutions in rewrite rules. + +| Context | Available Levels | +|----------------|---------------------------------------------| +| `location` | ✓ Location level only | +| `.htaccess` | ✓ Supported in .htaccess files | + +**Syntax:** +```nginx +RewriteBase /path/ +``` + +**Parameters:** +- Must start with `/` and end with `/` (trailing slash required) + +**Example:** +```nginx +# Inside a specific location +location /blog/ { + RewriteBase /blog/ + + # Relative substitution resolves to: /blog/news.html + RewriteRule ^news\.html$ news.php [L] +} + +# In .htaccess: +RewriteEngine on +RewriteBase /subdir/ +RewriteRule ^index\.html$ home.php [L] +``` + +--- + +### 5. RewriteOptions + +**Purpose:** Configure rewrite behavior options that affect rule inheritance and processing order. + +| Context | Available Levels | +|----------------|---------------------------------------------| +| `main`, `server`, `location` | ✓ All three levels supported | + +**Syntax:** +```nginx +RewriteOptions option1 [option2 ...] +``` + +**Possible Options:** + +| Option | Description | +|-----------------|-------------| +| `Inherit` | Inherit rules from parent locations/servers (default behavior) | +| `InheritBefore` | Process parent rules before child rules | + +**Example:** +```nginx +http { + server { + RewriteOptions Inherit + + location /parent/ { + RewriteRule ^parent/(.*)$ /new/$1 [L] + } + + location /child/ { + # Inherits rules from parent by default with 'Inherit' option + RewriteBase /child/ + } + } +} +``` + +--- + +### 6. RewriteMap + +**Purpose:** Define name-value mappings for lookup tables in rewrite expressions. + +| Context | Available Levels | +|----------------|---------------------------------------------| +| `server` | ✓ Server level only | +| `.htaccess` | ✗ Not supported in .htaccess | + +**Syntax:** +```nginx +RewriteMap name type:source +``` + +**Parameters:** + +- **name** - Map identifier (used as variable like `%MAPNAME:value`) +- **type** - Type of map: + - `int` - Internal function (supported: `tolower`, `toupper`, `escape`, `unescape`) + - `txt` - Text file lookup (not supported yet) + - `rnd` - Random lookup (not supported yet) + - `prg` - Program lookup (not supported yet) + +**Example:** +```nginx +server { + # Map to lowercase version of hostname + RewriteMap lc int:tolower + + server_name example.com; + + location / { + # Use mapped value in rule + RewriteRule ^lc/([^/]+)$ /redirect/$1 [L] + } +} + +# Usage in rules: +RewriteRule ^(.*)$ /%lc:$1 [L] +``` + +--- + +### 7. HtaccessEnable + +**Purpose:** Enable or disable `.htaccess` file parsing for the server/location. + +| Context | Available Levels | +|----------------|---------------------------------------------| +| `main`, `server` | ✓ Main and Server level only | +| `.htaccess` | ✗ Not supported in .htaccess | + +**Syntax:** +```nginx +HtaccessEnable on|off +``` + +**Parameters:** +- `on` - Enable `.htaccess` parsing +- `off` - Disable `.htaccess` parsing (default) + +**Example:** +```nginx +http { + server { + HtaccessEnable on + + # .htaccess files will be searched upward from the request path + location /subdir/ { + root /var/www/html; + } + } +} + +# In nginx.conf, you can also enable at http level: +http { + HtaccessEnable on; + + server { + # Inherits setting from http level + } +} +``` + +--- + +### 8. HtaccessName + +**Purpose:** Specify an alternative filename for `.htaccess` files. + +| Context | Available Levels | +|----------------|---------------------------------------------| +| `main`, `server` | ✓ Main and Server level only | +| `.htaccess` | ✗ Not supported in .htaccess | + +**Syntax:** +```nginx +HtaccessName filename +``` + +**Parameters:** +- Any string (default is `.htaccess`) + +**Example:** +```nginx +server { + HtaccessName .webconfig + + # Will look for .webconfig files instead of .htaccess + location / { + root /var/www/html; + } +} + +# In .htaccess: +HtaccessEnable on +HtaccessName .custom_htac +``` + +--- + +### 9. RewriteFallBack + +**Purpose:** Specify an alternative fallback path when the rewritten file doesn't exist (instead of default `/index.php`). This directive can only be used in `.htaccess` files and is read by `ngx_htaccess_parse_file_from_ha()`. The fallback path is cached per request and retrieved in `ngx_http_apache_rewrite_url_register_hook_with_fallback()`. + +| Context | Available Levels | +|----------------|---------------------------------------------| +| `.htaccess` | ✓ Supported only in .htaccess files | + +**Syntax:** +```apache +RewriteFallBack /path/to/fallback.php +``` + +**Parameters:** +- Path must start with `/` (required) +- Default fallback is `/index.php` if not specified + +**Example in .htaccess:** +```apache +# Use custom fallback when file not found +RewriteFallBack /custom/app.php + +# Fallback will redirect to /custom/app.php?query_string instead of /index.php +RewriteEngine on +RewriteCond %{REQUEST_FILENAME} !-f +RewriteRule ^(.*)$ index.php [QSA,L] +``` + +**Example with query string:** +```apache +RewriteFallBack /handler.php?lang=ru + +# When file doesn't exist, request is redirected to the fallback path +# preserving the original query string if present +``` + +--- + +## Configuration Levels Summary + +| Directive | http/main | server | location | .htaccess | +|-----------------|-----------|--------|----------|-----------| +| RewriteEngine | ✓ | ✓ | ✓ | ✓ | +| RewriteRule | - | ✓ | ✓ | ✓ | +| RewriteCond | - | ✓ | ✓ | ✓ | +| RewriteBase | - | - | ✓ | ✓ | +| RewriteOptions | ✓ | ✓ | ✓ | - | +| RewriteMap | - | ✓ | - | - | +| HtaccessEnable | ✓ | ✓ | - | - | +| HtaccessName | ✓ | ✓ | - | - | +| RewriteFallBack | ✗ | ✗ | - | ✓ | + +--- + +## .htaccess File Format + +`.htaccess` files follow the Apache format: + +```apache +# Comments start with # +RewriteEngine on + +RewriteCond %{REQUEST_URI} ^/old/(.*)$ +RewriteRule ^old/(.*)$ /new/$1 [R=301,L] + +# Multiple conditions (AND logic by default) +RewriteCond %{HTTP_HOST} www\.example\.com$ +RewriteCond %{HTTPS} off +RewriteRule (.*) https://www.example.com/$1 [R=301,L] + +RewriteBase /subdir/ +RewriteFallBack /custom/fallback.php +RewriteCond !-f +RewriteCond !-d +RewriteRule ^(.*)$ index.php?route=$1 [QSA,L] +``` + +When a file doesn't exist after URL rewriting, the module falls back to: +1. `RewriteFallBack` path if specified in `.htaccess`, or +2. `/index.php` as default fallback + +--- + +## Module Features + +### FastCGI Integration +The module automatically passes environment variables (set via `[E=VAR:VAL]` flags) to FastCGI applications without manual configuration. + +### Environment Variables +Environment variables persist across request phases and are available for downstream modules. + +### .htaccess Caching +`.htaccess` files are cached by modification time in the request pool to improve performance on repeated requests. The `RewriteFallBack` directive is also stored in the cache entry and retrieved when needed. + +### RewriteFallBack Directive +The `RewriteFallBack` directive allows customizing the fallback path used when a rewritten file doesn't exist: + +1. **Caching:** The fallback path from `.htaccess` is cached per request to avoid repeated parsing +2. **Fallback Logic:** When `try_files` fails, the module redirects to the configured fallback instead of `/index.php` +3. **Query String Preservation:** Original query string is preserved and appended to fallback path + +--- + +## Notes + +- Rewrite rules are processed in order as defined in configuration or `.htaccess` files +- The `[L]` flag stops processing at the current rule +- The `[N]` flag enables looping for infinite redirects prevention (max 10,000 rounds) +- Location-level rules override server-level rules unless `Inherit` option is set diff --git a/README.ru.md b/README.ru.md new file mode 100644 index 0000000..f9644ac --- /dev/null +++ b/README.ru.md @@ -0,0 +1,481 @@ +## Модуль совместимости Apache mod_rewrite для Nginx + +Этот модуль обеспечивает совместимость Apache `mod_rewrite` для Nginx, позволяя использовать файлы `.htaccess` и стандартные правила переписывания. + +--- + +# Директивы конфигурации + +Ниже перечислены доступные директивы в этом модуле: + +### 1. RewriteEngine + +**Назначение:** Включить или отключить механизм переписывания. + +| Контекст | Доступные уровни | +|----------|------------------| +| `main`, `server`, `location` | ✓ Поддерживаются все три уровня | + +**Синтаксис:** +```nginx +RewriteEngine on|off +``` + +**Возможные значения:** +- `on` - Включить механизм переписывания +- `off` - Отключить механизм переписывания (по умолчанию) + +**Пример в nginx.conf:** +```nginx +http { + server { + RewriteEngine on; + + location /blog/ { + # Правила применяются здесь + } + + location /static/ { + RewriteEngine off; # Отключить для этой локации + } + } +} +``` + +--- + + +### 2. RewriteRule + +**Назначение:** Определить правило переписывания, которое сопоставляет шаблон URL и заменяет его новым значением. + +| Контекст | Доступные уровни | +|----------|------------------| +| `server`, `location` | ✓ Уровни сервера и локации | +| `.htaccess` | ✓ Поддерживается в файлах .htaccess | + +**Синтаксис:** +```nginx +RewriteRule pattern substitution [flags] +``` + +**Параметры:** + +- **pattern** - Регулярное выражение для сопоставления URL (поддерживаются обратные ссылки $1, $2 и т.д.) + - Паттерн может быть префиксирован `!` для отрицания + - `-` в качестве замены означает "нет замены" (только проверка условий) + +- **substitution** - Целевой URL или путь файла для переписывания + - Может быть относительным путем (`/new/path`) или абсолютным перенаправлением (`http://example.com/new`) + +**Флаги:** `[flag1,flag2,...]` (необязательно): + +| Флаг | Короткая запись | Описание | +|------|-----------------|----------| +| `B` | - | Экранировать обратные ссылки в выводе (предотвратить проблемы с кодировкой URL) | +| `C` | Chain | Присоединить это правило к следующему (требует предыдущего совпадения) | +| `D` | DPI | Отбросить информацию о пути после замены | +| `E` | Env=var:val | Установить переменную окружения для последующего использования (FastCGI интеграция) | +| `F` | Forbidden | Вернуть статус HTTP 403 Forbidden | +| `G` | Gone | Вернуть статус HTTP 410 Gone | +| `L` | Last | Остановить обработку правил после этого | +| `N` | Next=limit | Цикл (повторить) до N раз для предотвращения бесконечных переписываний | +| `P` | Proxy | Проксировать запрос внутренне (Фаза 2) | +| `Q` | QSA | Прикрепить исходную строку запроса к URL замены | +| `Q` | QSD | Удалить исходную строку запроса | +| `Q` | QSL | Установить маркер "query string last" | +| `R` | Redirect=code | Выполнить внешнее перенаправление со статусом (301, 302, 307) или: Permanent, Temp, SeeOther | +| `S` | Skip=N | Пропустить N последующих правил после этого | +| `T` | Type=mime | Принудительно установить MIME-тип для ответа | +| `NC` | - | Непрерывное сопоставление без учета регистра | +| `NOESCAPE` | - | Не экранировать специальные символы в выводе | +| `END`| - | Остановить все переписывания и перейти к фазе контента | + +**Пример:** +```nginx +# Перенаправление /old-path/ в /new-path/ (постоянное перенаправление) +RewriteRule ^old-path/(.*)$ /new-path/$1 [R=301,L] + +# Условное переписывание (цепочка) +RewriteCond %REQUEST_URI !^/admin/ +RewriteRule ^admin/(.*)$ /login.php?user=$1 [NC,E,END] + +# В .htaccess: +RewriteEngine on +RewriteCond %{HTTP_HOST} ^www\.example\.com$ [OR] +RewriteCond %{HTTPS} off +RewriteRule ^(.*)$ https://example.com/$1 [R=301,L] +``` + +--- + + +### 3. RewriteCond + +**Назначение:** Определить условия, которые должны быть выполнены, прежде чем будет применено правило `RewriteRule`. Множественные условия объединяются по умолчанию (AND); можно изменить это с помощью флага OR. + +| Контекст | Доступные уровни | +|----------|------------------| +| `server`, `location` | ✓ Уровни сервера и локации | +| `.htaccess` | ✓ Поддерживается в файлах .htaccess | + +**Синтаксис:** +```nginx +RewriteCond input_string pattern [flags] +``` + +**Параметры:** + +- **input_string** - Переменная или строка для проверки (например, `%REQUEST_URI`, `%HTTP_HOST`, `%REQUEST_METHOD`, `%REQUEST_FILENAME`) + - Общие переменные: `REQUEST_URI`, `HTTP_HOST`, `REQUEST_METHOD`, `REQUEST_FILENAME` + +- **pattern** - Шаблон проверки: + - **Тесты файлов:** `-f` (файл существует), `-d` (директория существует), `-x` (исполняемый), `-s` (не нулевой размер) + - **Тесты ссылок:** `-h` или `-l` (жёсткая/мягкая ссылка), `-L` (цель ссылки существует) + - **Сравнение целых чисел:** `-lt`, `-le`, `-eq`, `-gt`, `-ge`, `-ne` + - **Сравнение строк:** `=`, `>`, `<`, `>=`, `<=` + - **Регулярные выражения:** любой шаблон regex + +- **flags** (необязательно): + - `NC` - Не чувствительно к регистру + - `OR` / `ornext` - Логика OR между условиями (вместо AND) + +**Примеры:** +```nginx +# Условие: файл существует +RewriteCond %REQUEST_FILENAME -f + +# Условие: не директория +RewriteCond %REQUEST_FILENAME !-d + +# Сравнение строк +RewriteCond %{HTTP_HOST} ^www\.example\.com$ [NC] + +# Сравнение целых чисел (размер файла > 1024 байт) +RewriteCond %REQUEST_FILENAME -s +RewriteCond %{FILESIZE} -gt 1024 + +# Множественные условия с OR +RewriteCond %{HTTP_HOST} ^www\.example\.com$ [OR] +RewriteCond %{HTTPS} off + +# Регулярный шаблон +RewriteCond %{REQUEST_URI} ^/old/(.*)$ +``` + +--- + + +### 4. RewriteBase + +**Назначение:** Установить базовый URL-путь для относительных замен в правилах переписывания. + +| Контекст | Доступные уровни | +|----------|------------------| +| `location` | ✓ Только уровень локации | +| `.htaccess` | ✓ Поддерживается в файлах .htaccess | + +**Синтаксис:** +```nginx +RewriteBase /path/ +``` + +**Параметры:** +- Должен начинаться с `/` и заканчиваться `/` (обязательный слеш) + +**Пример:** +```nginx +# Внутри конкретной локации +location /blog/ { + RewriteBase /blog/ + + # Относительная замена разрешается: /blog/news.html + RewriteRule ^news\.html$ news.php [L] +} + +# В .htaccess: +RewriteEngine on +RewriteBase /subdir/ +RewriteRule ^index\.html$ home.php [L] +``` + +--- + + +### 5. RewriteOptions + +**Назначение:** Настроить параметры поведения переписывания, которые влияют на наследование правил и порядок их обработки. + +| Контекст | Доступные уровни | +|----------|------------------| +| `main`, `server`, `location` | ✓ Все три уровня поддерживаются | + +**Синтаксис:** +```nginx +RewriteOptions option1 [option2 ...] +``` + +**Возможные опции:** + +| Опция | Описание | +|----------------|----------| +| `Inherit` | Наследовать правила из родительских локаций/серверов (поведение по умолчанию) | +| `InheritBefore` | Обрабатывать правила родительских локаций до правил дочерних | + +**Пример:** +```nginx +http { + server { + RewriteOptions Inherit + + location /parent/ { + RewriteRule ^parent/(.*)$ /new/$1 [L] + } + + location /child/ { + # Наследует правила от родителя по умолчанию с опцией 'Inherit' + RewriteBase /child/ + } + } +} +``` + +--- + + +### 6. RewriteMap + +**Назначение:** Определить имя‑значение карты для поиска таблиц в выражениях переписывания. + +| Контекст | Доступные уровни | +|----------|------------------| +| `server` | ✓ Только уровень сервера | +| `.htaccess` | ✗ Не поддерживается в .htaccess | + +**Синтаксис:** +```nginx +RewriteMap name type:source +``` + +**Параметры:** + +- **name** - Идентификатор карты (используется как переменная, например `%MAPNAME:value`) +- **type** - Тип карты: + - `int` - Внутренняя функция (поддерживаемые: `tolower`, `toupper`, `escape`, `unescape`) + - `txt` - Поиск по текстовому файлу (пока не реализовано) + - `rnd` - Поиск по случайному (пока не реализовано) + - `prg` - Поиск по программе (пока не реализовано) + +**Пример:** +```nginx +server { + # Карта к нижнему регистру имени хоста + RewriteMap lc int:tolower + + server_name example.com; + + location / { + # Использовать сопоставленное значение в правиле + RewriteRule ^lc/([^/]+)$ /redirect/$1 [L] + } +} + +# Использование в правилах: +RewriteRule ^(.*)$ /%lc:$1 [L] +``` + +--- + + +### 7. HtaccessEnable + +**Назначение:** Включить или отключить разбор файлов `.htaccess` для сервера/локации. + +| Контекст | Доступные уровни | +|----------|------------------| +| `main`, `server` | ✓ Только уровни основного и сервера | +| `.htaccess` | ✗ Не поддерживается в .htaccess | + +**Синтаксис:** +```nginx +HtaccessEnable on|off +``` + +**Параметры:** +- `on` - Включить разбор `.htaccess` +- `off` - Отключить разбор `.htaccess` (по умолчанию) + +**Пример:** +```nginx +http { + server { + HtaccessEnable on + + # Файлы .htaccess будут искаться вверх от пути запроса + location /subdir/ { + root /var/www/html; + } + } +} + +# В nginx.conf, можно включить на уровне http: +http { + HtaccessEnable on; + + server { + # Наследует настройку из уровня http + } +} +``` + +--- + + +### 8. HtaccessName + +**Назначение:** Указать альтернативное имя файла для файлов `.htaccess`. + +| Контекст | Доступные уровни | +|----------|------------------| +| `main`, `server` | ✓ Только уровни основного и сервера | +| `.htaccess` | ✗ Не поддерживается в .htaccess | + +**Синтаксис:** +```nginx +HtaccessName filename +``` + +**Параметры:** +- Любая строка (по умолчанию `.htaccess`) + +**Пример:** +```nginx +server { + HtaccessName .webconfig + + # Будет искать файлы .webconfig вместо .htaccess + location / { + root /var/www/html; + } +} + +# В .htaccess: +HtaccessEnable on +HtaccessName .custom_htac +``` + +--- + +### 9. RewriteFallBack + +**Назначение:** Указать альтернативный путь отката, если переписанный файл не существует (вместо стандартного `/index.php`). Эта директива может использоваться только в файлах `.htaccess` и читается функцией `ngx_htaccess_parse_file_from_ha()`. Путь отката кэшируется для каждого запроса и извлекается в `ngx_http_apache_rewrite_url_register_hook_with_fallback()`. + +| Контекст | Доступные уровни | +|-----------------|----------------------------------------------| +| `.htaccess` | ✓ Поддерживается только в файлах .htaccess | + +**Синтаксис:** +```apache +RewriteFallBack /path/to/fallback.php +``` + +**Параметры:** +- Путь должен начинаться с `/` (обязательно) +- По умолчанию путь отката – `/index.php`, если не указан + +**Пример в .htaccess:** +```apache +# Использовать пользовательский откат, если файл не найден +RewriteFallBack /custom/app.php + +# Откат перенаправит на /custom/app.php?query_string вместо /index.php +RewriteEngine on +RewriteCond %{REQUEST_FILENAME} !-f +RewriteRule ^(.*)$ index.php [QSA,L] +``` + +**Пример с параметром строки запроса:** +```apache +RewriteFallBack /handler.php?lang=ru + +# Если файл не существует, запрос будет перенаправлен на путь отката +# с сохранением исходной строки запроса, если она присутствует +``` + +--- + +## Сводка уровней конфигурации + +| Директива | http/main | server | location | .htaccess | +|------------------|-----------|--------|----------|-----------| +| RewriteEngine | ✓ | ✓ | ✓ | ✓ | +| RewriteRule | - | ✓ | ✓ | ✓ | +| RewriteCond | - | ✓ | ✓ | ✓ | +| RewriteBase | - | - | ✓ | ✓ | +| RewriteOptions | ✓ | ✓ | ✓ | - | +| RewriteMap | - | ✓ | - | - | +| HtaccessEnable | ✓ | ✓ | - | - | +| HtaccessName | ✓ | ✓ | - | - | +| RewriteFallBack | ✗ | ✗ | - | ✓ | + +--- + + +## Формат файла .htaccess + +Файлы `.htaccess` следуют формату Apache: + +```apache +# Комментарии начинаются с # +RewriteEngine on + +RewriteCond %{REQUEST_URI} ^/old/(.*)$ +RewriteRule ^old/(.*)$ /new/$1 [R=301,L] + +# Множественные условия (логика AND по умолчанию) +RewriteCond %{HTTP_HOST} www\.example\.com$ +RewriteCond %{HTTPS} off +RewriteRule (.*) https://www.example.com/$1 [R=301,L] + +RewriteBase /subdir/ +RewriteFallBack /custom/fallback.php +RewriteCond !-f +RewriteCond !-d +RewriteRule ^(.*)$ index.php?route=$1 [QSA,L] +``` + +Если после переписывания URL файл не существует, модуль переходит к: +1. Путь `RewriteFallBack`, если указан в `.htaccess`, или +2. `/index.php` как стандартный откат +``` + +--- + + +## Возможности модуля + +### Интеграция с FastCGI +Модуль автоматически передаёт переменные окружения (установленные через флаги `[E=VAR:VAL]`) в приложения FastCGI без дополнительной настройки. + +### Переменные окружения +Переменные окружения сохраняются между фазами запроса и доступны downstream-модулям. + +### Кэширование .htaccess +Файлы `.htaccess` кэшируются по времени изменения в памяти запроса, чтобы ускорить повторные запросы. + +### Директива RewriteFallBack +Директива `RewriteFallBack` позволяет настроить путь отката, который используется, когда переписанный файл не существует: + +1. **Кеширование:** Путь отката из `.htaccess` кэшируется на каждый запрос, чтобы избежать повторного разборов. +2. **Логика отката:** Когда `try_files` завершается неудачей, модуль перенаправляет на заданный откат вместо `/index.php`. +3. **Сохранение строки запроса:** Исходная строка запроса сохраняется и добавляется к пути отката. +``` + +--- + +## Заметки + +- Правила переписывания обрабатываются в порядке, определенном в конфигурации или файлах `.htaccess` +- Флаг `[L]` останавливает обработку на текущем правиле +- Флаг `[N]` включает цикл для предотвращения бесконечных перенаправлений (максимум 10 000 раундов) +- Правила уровня локации переопределяют правила уровня сервера, если не задана опция `Inherit` diff --git a/build b/build new file mode 100644 index 0000000..d655dd6 --- /dev/null +++ b/build @@ -0,0 +1,20 @@ +#!/bin/bash + +cd nginx-1.25.3 +make clean +./configure --with-compat --add-dynamic-module=../modules/mod_rewrite --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/etc/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/lib/nginx/body --http-proxy-temp-path=/var/lib/nginx/proxy --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --http-scgi-temp-path=/var/lib/nginx/scgi --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-http_gzip_static_module --with-http_stub_status_module --with-http_slice_module --with-http_dav_module --with-http_auth_request_module --with-http_secure_link_module --with-stream --with-stream_ssl_module --with-stream_realip_module --with-stream_ssl_preread_module --with-pcre-jit --with-debug +if [ $? -ne 0 ]; then + cd .. + exit 1 +fi +make modules +if [ $? -ne 0 ]; then + cd .. + exit 1 +fi +make install +if [ $? -ne 0 ]; then + cd .. + exit 1 +fi +cd .. diff --git a/cms/drupal-mod_headers/.htaccess b/cms/drupal-mod_headers/.htaccess new file mode 100644 index 0000000..7d9bf81 --- /dev/null +++ b/cms/drupal-mod_headers/.htaccess @@ -0,0 +1,25 @@ +RewriteEngine on +RewriteRule ^ - [E=protossl] +RewriteCond %{HTTPS} on +RewriteRule ^ - [E=protossl:s] +RewriteRule ^ - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] +RewriteRule "/\.|^\.(?!well-known/)" - [F] +RewriteCond %{REQUEST_URI} ^(.*)?/(install\.php) [OR] +RewriteCond %{REQUEST_URI} ^(.*)?/(rebuild\.php) +RewriteCond %{REQUEST_URI} !core +RewriteRule ^ %1/core/%2 [L,QSA,R=301] +RewriteRule ^core/install\.php core/install.php?rewrite=ok [QSA,L] +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteCond %{REQUEST_URI} !=/favicon.ico +RewriteRule ^ index.php [L] +RewriteCond %{REQUEST_URI} !/core/[^/]*\.php$ +RewriteCond %{REQUEST_URI} !/core/modules/system/tests/https?\.php +RewriteCond %{HTTP:Accept-encoding} gzip +RewriteCond %{REQUEST_FILENAME}\.gz -s +RewriteRule ^(.*css_[a-zA-Z0-9-_]+)\.css$ $1\.css\.gz [QSA] +RewriteCond %{HTTP:Accept-encoding} gzip +RewriteCond %{REQUEST_FILENAME}\.gz -s +RewriteRule ^(.*js_[a-zA-Z0-9-_]+)\.js$ $1\.js\.gz [QSA] +RewriteRule \.css\.gz$ - [T=text/css,E=no-gzip:1,E=no-brotli:1] +RewriteRule \.js\.gz$ - [T=text/javascript,E=no-gzip:1,E=no-brotli:1] diff --git a/cms/drupal/.htaccess b/cms/drupal/.htaccess new file mode 100644 index 0000000..ab368bf --- /dev/null +++ b/cms/drupal/.htaccess @@ -0,0 +1,19 @@ + RewriteEngine on + RewriteRule ^ - [E=protossl] + RewriteCond %{HTTPS} on + RewriteRule ^ - [E=protossl:s] + RewriteRule ^ - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] + RewriteRule "/\.|^\.(?!well-known/)" - [F] + RewriteCond %{REQUEST_URI} ^(.*)?/(install\.php) [OR] + RewriteCond %{REQUEST_URI} ^(.*)?/(rebuild\.php) + RewriteCond %{REQUEST_URI} !core + RewriteRule ^ %1/core/%2 [L,QSA,R=301] + RewriteRule ^core/install\.php core/install.php?rewrite=ok [QSA,L] + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_URI} !=/favicon.ico + RewriteRule ^ index.php [L] + RewriteCond %{REQUEST_URI} !/core/[^/]*\.php$ + RewriteCond %{REQUEST_URI} !/core/modules/system/tests/https?\.php + RewriteCond %{REQUEST_URI} !/core/modules/statistics/statistics\.php$ + RewriteRule "^(.+/.*|autoload)\.php($|/)" - [F] diff --git a/cms/drupal/.htpasswd b/cms/drupal/.htpasswd new file mode 100644 index 0000000..4415402 --- /dev/null +++ b/cms/drupal/.htpasswd @@ -0,0 +1 @@ +# This is a test .htpasswd hidden file - should be blocked by RewriteRule "/\.|^\.(?!well-known/)" - [F] diff --git a/cms/drupal/.well-known/robots.txt b/cms/drupal/.well-known/robots.txt new file mode 100644 index 0000000..9ce043f --- /dev/null +++ b/cms/drupal/.well-known/robots.txt @@ -0,0 +1,6 @@ +# This is a robots.txt file for Drupal site testing +# Should be allowed by RewriteRule "/\.|^\.(?!well-known/)" - [F] exception + +User-agent: * +Disallow: /admin/ +Allow: / diff --git a/cms/drupal/README.md b/cms/drupal/README.md new file mode 100644 index 0000000..1255ceb --- /dev/null +++ b/cms/drupal/README.md @@ -0,0 +1,162 @@ +# Drupal .htaccess Test Structure + +## Directory Layout Overview + +``` +/test1/cms/drupal/ +├── core/ - Drupal core directory tests +│ ├── install.php - Install script (protected by RewriteRule ^core/install\.php) +│ ├── rebuild.php - Rebuild script (protected by RewriteRule ^core/rebuild\.php) +│ └── modules/system/tests/ - Tests directory with https/http.php files +│ ├── https.php - Test HTTPS test file (excluded from routing) +│ └── http.php - Test HTTP test file (excluded from routing) +├── favicon.ico - Existing favicon for !-f condition test +├── index.php - Drupal entry point (routes non-existing files) +│ - Returns: "Drupal Content Route" page +├── .htaccess - Hidden .htaccess file (blocked by hidden files rule) +├── .htpasswd - Hidden .htpasswd file (blocked by hidden files rule) +├── .well-known/ - Well-known directory (allowed exception) +│ └── robots.txt - Allowed file via exception (?!well-known) +├── somedir/ - Directory for testing !-d condition (200 OK) +├── test-drupal-rewriterules.sh - Bash script to test all rules using curl +└── README.md - This documentation file +``` + +## Apache Rules Explained - Drupal + +### 1. RewriteEngine Activation + +```apache +RewriteEngine on +``` + +**Что делает:** Включает модуль mod_rewrite для этого каталога +**Зачем нужно:** Без этого все правила rewrite не работают + +### 2. Protocol Variables (HTTP/HTTPS Detection) + +```apache +RewriteRule ^ - [E=protossl] +RewriteCond %{HTTPS} on +RewriteRule ^ - [E=protossl:s] +``` + +**Что делает:** Устанавливает переменную окружения `protossl` = "https" или "http" в зависимости от HTTPS status +**Зачем нужно:** Drupal использует это для генерации правильных ссылок (http vs https) +- Если HTTPS off → protossl = "" (пусто, http) +- Если HTTPS on → protossl = "s" + +### 3. HTTP Authorization Header Passing + +```apache +RewriteRule ^ - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] +``` + +**Что делает:** Копирует Authorization header в переменную HTTP_AUTHORIZATION для PHP +**Зачем нужно:** Drupal REST API требует эту переменную для аутентификации + +### 4. Hidden Files/Patterns Protection Rule + +```apache +RewriteRule "/\.|^\.(?!well-known/)" - [F] +``` + +**Что делает:** Блокирует (403 Forbidden) файлы начинающиеся с точки, кроме .well-known/ +- `/\.` - блокирует /filename.хотя бы одна точка в path +- `^\.(?!well-known/)` - блокирует /隐藏文件, но исключает /well-known/ + +**Зачем нужно:** Защита от доступа к скрытым файлам (.htaccess, .htpasswd, .git) +**Исключение:** .well-known/robots.txt разрешён (для SEO и security) + +### 5. Core install/rebuild.php Protection Rules + +```apache +RewriteCond %{REQUEST_URI} ^(.*)?/(install\.php) [OR] +RewriteCond %{REQUEST_URI} ^(.*)?/(rebuild\.php) +RewriteCond %{REQUEST_URI} !core +RewriteRule ^ %1/core/%2 [L,QSA,R=301] +``` + +**Что делает:** Редирект 301 с /install.php → /core/install.php, /rebuild.php → /core/rebuild.php +**Зачем нужно:** Перемещает install/rebuild скрипты в core directory для безопасности + +### 6. Core install.php Rewrite + +```apache +RewriteRule ^core/install\.php core/install.php?rewrite=ok [QSA,L] +``` + +**Что делает:** Переписывает запрос на core/install.php с добавлением параметра rewrite=ok +**Зачем нужно:** Drupal internal handling для процесса установки + +### 7. Main Drupal Routing Rules (!-f, !-d) + +```apache +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteCond %{REQUEST_URI} !=/favicon.ico +RewriteRule ^ index.php [L] +``` + +**Что делает:** Маршрутизирует через index.php все запросы на несуществующие файлы И директории, кроме favicon.ico +**Зачем нужно:** "Чистые URL" Drupal (похоже на WordPress), routing через index.php + +### 8. Core Modules Tests Files Exceptions + +```apache +RewriteCond %{REQUEST_URI} !/core/[^/]*\.php$ +RewriteCond %{REQUEST_URI} !/core/modules/system/tests/https?\.php +``` + +**Что делает:** Исключает из routing core/*.php и tests/https.php/http.php файлы +**Зачем нужно:** Эти файлы должны обрабатываться напрямую, не через main index.php + +## Test Script Features + +The script includes test functions: +1. **test_rule()** - checks HTTP status code only +2. **test_rule_content()** - checks both status AND response body content + +## Multisite-Specific Testing Scenarios + +### Hidden Files Protection + +| URL | Правило | Ожидаемый результат | +|-----|---------|---------------------| +| `http://test.my.brp/.htaccess` | hidden files rule `/\.|^\.(?!well-known/)` | 403 Forbidden ✓ | +| `http://test.my.brp/.htpasswd` | hidden files rule | 403 Forbidden ✓ | +| `http://test.my.brp/.well-known/robots.txt` | well-known exception | 200 OK (allowed) ✓ | + +### Core install/rebuild.php Protection + +| URL | Правило | Ожидаемый результат | +|-----|---------|---------------------| +| `http://test.my.brp/install.php` | RewriteRule ^(.*)?/(install\.php) + core exclusion | 301 → /core/install.php ✓ | +| `http://test.my.brp/rebuild.php` | RewriteRule ^(.*)?/(rebuild\.php) + core exclusion | 301 → /core/rebuild.php ✓ | + +### Core Files Routing Exceptions + +| URL | Правило | Ожидаемый результат | +|-----|---------|---------------------| +| `http://test.my.brp/core/install.php` | RewriteRule ^core/install\.php ... rewrite=ok parameter | 200 OK ✓ | +| `http://test.my.brp/core/modules/system/tests/https.php` | tests exception !-https?\.php condition | 200 OK ✓ | +| `http://test.my.brp/core/modules/system/tests/http.php` | tests exception !-https?\.php condition (s matches empty) | 200 OK ✓ | + +## Run Tests + +Execute the test script to verify all rules: +```bash +cd /home/alexey/projects/workspace-zed/test1/cms/drupal +./test-drupal-rewriterules.sh +``` + +Expected results for Drupal tests (all should be **PASS ✓**): +- Basic page routing via index.php: HTTP 200 + "Drupal Content Route" ✓ +- Hidden files (.htaccess, .htpasswd) blocked: HTTP 403 ✓ +- .well-known/robots.txt allowed: HTTP 200 ✓ +- install.php redirect to core: HTTP 301 ✓ +- rebuild.php redirect to core: HTTP 301 ✓ +- favicon.ico direct access (!-f): HTTP 200 ✓ +- Non-existing page routing to index.php: HTTP 200 ✓ +- Directory access (!-d): HTTP 200 ✓ +- Tests files https.php/http.php excluded from routing: HTTP 200 ✓ diff --git a/cms/drupal/core/install.php b/cms/drupal/core/install.php new file mode 100644 index 0000000..020e85f --- /dev/null +++ b/cms/drupal/core/install.php @@ -0,0 +1 @@ +# This is a test Drupal core/install.php file - should be protected by RewriteRule ^core/install\.php core/install.php?rewrite=ok [QSA,L] diff --git a/cms/drupal/core/modules/system/tests/http.php b/cms/drupal/core/modules/system/tests/http.php new file mode 100644 index 0000000..dc896e8 --- /dev/null +++ b/cms/drupal/core/modules/system/tests/http.php @@ -0,0 +1 @@ +# This is a test Drupal http.php in tests directory - should be excluded from index.php routing by RewriteCond %{REQUEST_URI} !/core/modules/system/tests/https?\.php diff --git a/cms/drupal/core/modules/system/tests/https.php b/cms/drupal/core/modules/system/tests/https.php new file mode 100644 index 0000000..0d8b08b --- /dev/null +++ b/cms/drupal/core/modules/system/tests/https.php @@ -0,0 +1 @@ +# This is a test Drupal https.php in tests directory - should be excluded from index.php routing by RewriteCond %{REQUEST_URI} !/core/modules/system/tests/https?\.php diff --git a/cms/drupal/core/rebuild.php b/cms/drupal/core/rebuild.php new file mode 100644 index 0000000..8dd8cef --- /dev/null +++ b/cms/drupal/core/rebuild.php @@ -0,0 +1 @@ +# This is a test Drupal core/rebuild.php file - should be protected by RewriteRule ^core/rebuild\.php core/rebuild.php?rewrite=ok [QSA,L] diff --git a/cms/drupal/favicon.ico b/cms/drupal/favicon.ico new file mode 100644 index 0000000..8ba1ca3 --- /dev/null +++ b/cms/drupal/favicon.ico @@ -0,0 +1,2 @@ +# This is a placeholder for Drupal test favicon.ico +# Real favicon would be an image file, but we use text for testing diff --git a/cms/drupal/index.php b/cms/drupal/index.php new file mode 100644 index 0000000..d4e6431 --- /dev/null +++ b/cms/drupal/index.php @@ -0,0 +1,15 @@ +Drupal Test Site"; +echo "

Drupal Content Route

"; +echo "

This page is served by index.php via RewriteRule.

"; +echo "
Drupal Configuration Loaded
"; +echo ""; + +// Exit +exit; diff --git a/cms/drupal/nginx.conf b/cms/drupal/nginx.conf new file mode 100644 index 0000000..9138219 --- /dev/null +++ b/cms/drupal/nginx.conf @@ -0,0 +1,159 @@ + +load_module modules/ngx_http_apache_rewrite_module.so; +worker_processes 1; + +error_log logs/error.log debug; + +events { + worker_connections 1024; +} + + +http { + include mime.types; + default_type application/octet-stream; + + sendfile on; + + keepalive_timeout 65; + + + server { + listen 8081; + server_name localhost; + + + location / { + root html; + index index.html index.htm; + } + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root html; + } + + } + + server { + listen 8081; + server_name example1.com; + + root /sites/site1; + + HtaccessEnable on; + + RewriteEngine On; + + location / { + RewriteEngine On; + } + + } + + server { + listen 8081; + server_name example2.com; + + root /sites/site2; + + HtaccessEnable on; + + RewriteEngine On; + index index.php; + + location / { + RewriteEngine On; + autoindex on; + } + + location ~* \.php$ { + RewriteEngine On; + include fastcgi_params; + fastcgi_split_path_info ^(.+\.php)(/.+)$; + fastcgi_pass unix:/run/php-fpm/www.sock; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + + } + } + + server { + listen 8081; + server_name example3.com; + + root /sites/site3; + + HtaccessEnable on; + + RewriteEngine On; + index index.php; + + location / { + RewriteEngine On; + autoindex on; + } + + location ~* \.php$ { + RewriteEngine On; + include fastcgi_params; + fastcgi_split_path_info ^(.+\.php)(/.+)$; + fastcgi_pass unix:/run/php-fpm/www.sock; # подключаем сокет php-fpm + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + } + } + + server { + listen 8081; + server_name example4.com; + + root /sites/site4; + + HtaccessEnable on; + + RewriteEngine On; + index index.php; + + location / { + RewriteEngine On; + autoindex on; + } + + location ~* \.php$ { + RewriteEngine On; + include fastcgi_params; + fastcgi_split_path_info ^(.+\.php)(/.+)$; + fastcgi_pass unix:/run/php-fpm/www.sock; # подключаем сокет php-fpm + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + } + } + + server { + listen 8081; + server_name example5.com; + + root /sites/site5; + + HtaccessEnable on; + + RewriteEngine On; + index index.php; + + location / { + RewriteEngine On; + autoindex on; + } + + location ~* \.php$ { + RewriteEngine On; + include fastcgi_params; + fastcgi_split_path_info ^(.+\.php)(/.+)$; + fastcgi_pass unix:/run/php-fpm/www.sock; # подключаем сокет php-fpm + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + } + } + +} diff --git a/cms/drupal/site5.conf b/cms/drupal/site5.conf new file mode 100644 index 0000000..a824b5a --- /dev/null +++ b/cms/drupal/site5.conf @@ -0,0 +1,20 @@ + + DocumentRoot "/sites/site5" + ServerName example5.com + ErrorLog logs/site5.log + + DirectoryIndex index.php + + + Options +Indexes +FollowSymLinks + AllowOverride All + Require all granted + + + Require all granted + + + SetHandler "proxy:unix:/run/php-fpm/www.sock|fcgi://localhost" + + + diff --git a/cms/drupal/sites/all/default/settings.php.dummy/.gitkeep b/cms/drupal/sites/all/default/settings.php.dummy/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/cms/drupal/somedir/.gitkeep b/cms/drupal/somedir/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/cms/drupal/somedir/index.php b/cms/drupal/somedir/index.php new file mode 100644 index 0000000..d96673c --- /dev/null +++ b/cms/drupal/somedir/index.php @@ -0,0 +1,3 @@ +|%3E) [NC,OR] + +# GLOBALS exploitation attempt +RewriteCond %{QUERY_STRING} GLOBALS(=|\[|\%[0-9A-Z]{0,2}) [OR] + +# _REQUEST manipulation (PHP superglobal abuse) +RewriteCond %{QUERY_STRING} _REQUEST(=|\[|\%[0-9A-Z]{0,2}) + +# Block malicious requests with 403 Forbidden +RewriteRule .* index.php [F] + +# ============================================ +# 2. Grav CMS Core Routing Rules +# ============================================ + +# Prevent rewriting to index.php if already there +RewriteCond %{REQUEST_URI} !^/index\.php + +# Allow access to existing files +RewriteCond %{REQUEST_FILENAME} !-f + +# Allow access to existing directories +RewriteCond %{REQUEST_FILENAME} !-d + +# Route everything else through Grav's index.php +RewriteRule .* index.php [L] + +# ============================================ +# 3. Sensitive Directory Protection +# ============================================ + +# Block system, cache, logs, backups folders +RewriteRule ^(\.git|cache|bin|logs|backup|webserver-configs|tests)/(.*) error [F] + +# Block system and vendor directories (prevent access to .txt/.xml files) +RewriteRule ^(system|vendor)/(.*)\.(txt|xml|md|html|json|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ error [F] + +# Block user directory access to configuration and content files +RewriteRule ^(user)/(.*)\.(txt|md|json|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ error [F] + +# ============================================ +# 4. File Extension Protection +# ============================================ + +# Block raw .md file access (content source files) +RewriteRule \.md$ error [F] + +# ============================================ +# 5. Hidden Files/Directories Protection +# ============================================ + +# Block hidden files except well-known/robots.txt directories +RewriteRule (^|/)\.(?!well-known) - [F] + +# ============================================ +# 6. Critical Configuration File Protection +# ============================================ + +# Block sensitive configuration and documentation files +RewriteRule ^(LICENSE\.txt|composer\.lock|composer\.json|\.htaccess)$ error [F] diff --git a/cms/grav/.htaccess.test b/cms/grav/.htaccess.test new file mode 100644 index 0000000..1d37b0d --- /dev/null +++ b/cms/grav/.htaccess.test @@ -0,0 +1 @@ +This is a .htaccess file - should be blocked by RewriteRule ^(LICENSE\.txt|composer\.lock|composer\.json|\.htaccess)$ error [F] diff --git a/cms/grav/.htpasswd b/cms/grav/.htpasswd new file mode 100644 index 0000000..cf0fc76 --- /dev/null +++ b/cms/grav/.htpasswd @@ -0,0 +1 @@ +This is a hidden .htpasswd file - should be blocked by RewriteRule (^|/)\.(?!well-known) - [F] diff --git a/cms/grav/.well-known/robots.txt b/cms/grav/.well-known/robots.txt new file mode 100644 index 0000000..d852aeb --- /dev/null +++ b/cms/grav/.well-known/robots.txt @@ -0,0 +1 @@ +Robots.txt file - should be ALLOWED because it matches (.well-known) exception in RewriteRule (^|/)\.(?!well-known) - [F] diff --git a/cms/grav/LICENSE.txt b/cms/grav/LICENSE.txt new file mode 100644 index 0000000..31fd571 --- /dev/null +++ b/cms/grav/LICENSE.txt @@ -0,0 +1 @@ +This is a LICENSE.txt file - should be blocked by RewriteRule ^(LICENSE\.txt|composer\.lock|composer\.json|\.htaccess)$ error [F] diff --git a/cms/grav/README.md b/cms/grav/README.md new file mode 100644 index 0000000..ec0a223 --- /dev/null +++ b/cms/grav/README.md @@ -0,0 +1,119 @@ +# Grav CMS Test Structure - Updated + +## Directory Layout Overview + +``` +/test1/cms/grav/ +├── .git/ - Git folder tests (blocked) +│ └── secret.txt +├── .well-known/ - Well-known directory (allowed exception) +│ └── robots.txt +├── .htaccess - Main Apache rules configuration +├── .htpasswd - Hidden file (blocked by Rule 5) +├── bin/ - Bin folder tests (blocked) +│ └── helper.php +├── backup/ - Backup folder tests (blocked) +│ └── archive.zip +├── cache/ - Cache folder tests (blocked) +│ └── test.txt +├── composer.json - Config file protection (blocked) +├── composer.lock - Config file protection (blocked) +├── existing.jpg - Existing file for routing test (200 OK) +├── index.php - Grav CMS entry point - routes non-existing files +│ - Returns: "Grav CMS Content Route" page +├── LICENSE.txt - Config file protection (blocked) +├── logs/ - Logs folder tests (blocked) +│ └── app.log +├── normal-page.md - Normal Grav CMS page (routes through index.php) +├── README.md - This documentation file +├── somedir/ - Empty directory for routing test (200 OK) +├── system/ - System folder tests (blocked extensions) +│ └── config.xml +├── vendor/ - Vendor folder tests (blocked extensions) +│ └── module.txt +├── user/ - User folder tests (blocked extensions) +│ ├── test.txt +│ ├── data.json +│ ├── template.twig +│ ├── script.sh +│ ├── module.php +│ ├── config.yml +│ └── settings.yaml +├── webserver-configs/ - Webserver configs folder tests (blocked) +│ └── nginx.conf +├── tests/ - Tests folder tests (blocked) +│ └── unit-test.php +├── test-mustache.php - Security: Mustache template injection pattern +├── twig-test.html - Security: Twig syntax injection pattern +├── test-rewriterules.sh - Bash script to test all rules using curl +└── README.md - Documentation +``` + +## Updated Test Script Features + +### New Content Verification Function + +The script now includes a `test_rule_content()` function that: +1. Checks HTTP status code matches expected value +2. Verifies response body contains expected content string + +**Example usage:** +```bash +test_rule_content "Normal page routing via index.php" \ + "$BASE_URL/normal-page/" \ + "200" \ + "Grav CMS Content Route" +``` + +This tests that: +- URL `/home/alexey/projects/workspace-zed/test1/cms/grav/normal-page/` (non-existing file) +- Returns HTTP 200 status (routed through index.php via Rule 2) +- Response body contains "Grav CMS Content Route" text from index.php + +## Test Coverage Summary + +### 1. Security Rules - Malicious Patterns ✓ +- Template injection: `{{ }}`, `{% %}` in URI/Query String → **403** +- Base64 payloads: `base64_encode()` pattern → **403** +- Script injection: `