Added mod_rewrite for nginx module
This commit is contained in:
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
nginx-1.25.30
|
||||
.clangd
|
||||
.devcontainer
|
||||
Dockerfile
|
||||
logs
|
||||
nginx-1.25.3
|
||||
.zed
|
||||
tmpbuild
|
||||
|
||||
|
||||
182
BUILD.md
Normal file
182
BUILD.md
Normal file
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
182
BUILD.ru.md
Normal file
182
BUILD.ru.md
Normal file
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
8
CHANGELOG
Normal file
8
CHANGELOG
Normal file
@@ -0,0 +1,8 @@
|
||||
version: 0.1-1
|
||||
|
||||
--
|
||||
* Sat Mar 14 2026 Alexey BayRepo <a@bayrepo.ru> - 0.1-1
|
||||
- Added debian/ubuntu build
|
||||
|
||||
* Fri Mar 13 2026 Alexey BayRepo <a@bayrepo.ru> - 0.1-0
|
||||
- New package mod_rewrite for nginx
|
||||
228
LICENSE
Normal file
228
LICENSE
Normal file
@@ -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.
|
||||
*/
|
||||
470
README.md
Normal file
470
README.md
Normal file
@@ -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
|
||||
481
README.ru.md
Normal file
481
README.ru.md
Normal file
@@ -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`
|
||||
20
build
Normal file
20
build
Normal file
@@ -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 ..
|
||||
25
cms/drupal-mod_headers/.htaccess
Normal file
25
cms/drupal-mod_headers/.htaccess
Normal file
@@ -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]
|
||||
19
cms/drupal/.htaccess
Normal file
19
cms/drupal/.htaccess
Normal file
@@ -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]
|
||||
1
cms/drupal/.htpasswd
Normal file
1
cms/drupal/.htpasswd
Normal file
@@ -0,0 +1 @@
|
||||
# This is a test .htpasswd hidden file - should be blocked by RewriteRule "/\.|^\.(?!well-known/)" - [F]
|
||||
6
cms/drupal/.well-known/robots.txt
Normal file
6
cms/drupal/.well-known/robots.txt
Normal file
@@ -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: /
|
||||
162
cms/drupal/README.md
Normal file
162
cms/drupal/README.md
Normal file
@@ -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 ✓
|
||||
1
cms/drupal/core/install.php
Normal file
1
cms/drupal/core/install.php
Normal file
@@ -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]
|
||||
1
cms/drupal/core/modules/system/tests/http.php
Normal file
1
cms/drupal/core/modules/system/tests/http.php
Normal file
@@ -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
|
||||
1
cms/drupal/core/modules/system/tests/https.php
Normal file
1
cms/drupal/core/modules/system/tests/https.php
Normal file
@@ -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
|
||||
1
cms/drupal/core/rebuild.php
Normal file
1
cms/drupal/core/rebuild.php
Normal file
@@ -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]
|
||||
2
cms/drupal/favicon.ico
Normal file
2
cms/drupal/favicon.ico
Normal file
@@ -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
|
||||
15
cms/drupal/index.php
Normal file
15
cms/drupal/index.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
/**
|
||||
* Drupal - index.php (Test version)
|
||||
* This file handles routing for non-existing files/directories
|
||||
*/
|
||||
|
||||
// Simulated Drupal response
|
||||
echo "<html><head><title>Drupal Test Site</title></head><body>";
|
||||
echo "<h1>Drupal Content Route</h1>";
|
||||
echo "<p>This page is served by index.php via RewriteRule.</p>";
|
||||
echo "<div class='drupal-config'>Drupal Configuration Loaded</div>";
|
||||
echo "</body></html>";
|
||||
|
||||
// Exit
|
||||
exit;
|
||||
159
cms/drupal/nginx.conf
Normal file
159
cms/drupal/nginx.conf
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
20
cms/drupal/site5.conf
Normal file
20
cms/drupal/site5.conf
Normal file
@@ -0,0 +1,20 @@
|
||||
<VirtualHost *:80>
|
||||
DocumentRoot "/sites/site5"
|
||||
ServerName example5.com
|
||||
ErrorLog logs/site5.log
|
||||
|
||||
DirectoryIndex index.php
|
||||
|
||||
<Directory /sites/site5>
|
||||
Options +Indexes +FollowSymLinks
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
<FilesMatch "^\.htaccess$">
|
||||
Require all granted
|
||||
</FilesMatch>
|
||||
<FilesMatch \.php$>
|
||||
SetHandler "proxy:unix:/run/php-fpm/www.sock|fcgi://localhost"
|
||||
</FilesMatch>
|
||||
|
||||
</VirtualHost>
|
||||
0
cms/drupal/somedir/.gitkeep
Normal file
0
cms/drupal/somedir/.gitkeep
Normal file
3
cms/drupal/somedir/index.php
Normal file
3
cms/drupal/somedir/index.php
Normal file
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
echo "Ok";
|
||||
182
cms/drupal/test-drupal-rewriterules.sh
Executable file
182
cms/drupal/test-drupal-rewriterules.sh
Executable file
@@ -0,0 +1,182 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ============================================
|
||||
# Drupal .htaccess Rules Test Script
|
||||
# ============================================
|
||||
# This script tests each rule from cms/drupal/.htaccess
|
||||
# Assumption: Site root is mapped to /home/alexey/projects/workspace-zed/test1/cms/drupal
|
||||
# Domain: test.my.brp
|
||||
# ============================================
|
||||
|
||||
BASE_URL="http://test.my.brp"
|
||||
|
||||
echo "=============================================="
|
||||
echo "Drupal .htaccess Rules Test Suite"
|
||||
echo "=============================================="
|
||||
echo ""
|
||||
|
||||
# Function to test a rule and report result (status only)
|
||||
test_rule() {
|
||||
local description="$1"
|
||||
local url="$2"
|
||||
local expected_status="$3" # e.g., 403, 404, 200, 301
|
||||
|
||||
echo "--- Test: $description ---"
|
||||
response=$(curl -s -o /dev/null -w "%{http_code}" "$url")
|
||||
|
||||
if [ "$response" = "$expected_status" ]; then
|
||||
echo "✓ PASS (HTTP $response)"
|
||||
else
|
||||
echo "✗ FAIL (Expected: HTTP $expected_status, Got: HTTP $response)"
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Function to test a rule and verify content contains expected string
|
||||
test_rule_content() {
|
||||
local description="$1"
|
||||
local url="$2"
|
||||
local headers="$3" # Optional: additional curl -H header flags (can be empty)
|
||||
local expected_status="$4" # e.g., 403, 404, 200, 301
|
||||
local expected_content="$5" # Expected substring in response body
|
||||
|
||||
echo "--- Test: $description ---"
|
||||
|
||||
if [ -n "$headers" ]; then
|
||||
response=$(curl -s -H "$headers" "$url")
|
||||
http_code=$(curl -s -H "$headers" -o /dev/null -w "%{http_code}" "$url")
|
||||
else
|
||||
response=$(curl -s "$url")
|
||||
http_code=$(curl -s -o /dev/null -w "%{http_code}" "$url")
|
||||
fi
|
||||
|
||||
# Check status code
|
||||
if [ "$http_code" != "$expected_status" ]; then
|
||||
echo "✗ FAIL (Status: HTTP $http_code, Expected: HTTP $expected_status)"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check content contains expected substring
|
||||
if [[ "$response" == *"$expected_content"* ]]; then
|
||||
echo "✓ PASS (HTTP $http_code, Content matches '$expected_content')"
|
||||
else
|
||||
echo "✗ FAIL (Content missing: '$expected_content') - Response:"
|
||||
echo "$response" | head -5
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
echo "=============================================="
|
||||
echo "1. RewriteEngine Activation"
|
||||
echo "=============================================="
|
||||
# Test basic routing through index.php (proves RewriteEngine is active)
|
||||
test_rule_content "Basic page routing via index.php" \
|
||||
"$BASE_URL/normal-page/" \
|
||||
"" \
|
||||
"200" \
|
||||
"Drupal Content Route"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "2. Protocol Variables (protossl)"
|
||||
echo "============================================}"
|
||||
# Test HTTPS protocol detection - since we use http://, HTTPS should be off
|
||||
test_rule_content "HTTP request without HTTPS (protocol detection)" \
|
||||
"$BASE_URL/normal-page/" \
|
||||
"" \
|
||||
"200" \
|
||||
"Drupal Content Route"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "3. HTTP Authorization Header Passing"
|
||||
echo "=============================================="
|
||||
# Test that Authorization header is properly handled by Drupal REST API
|
||||
test_rule_content "Drupal handles Authorization header (API request)" \
|
||||
"$BASE_URL/rest/api/v1" \
|
||||
"Authorization: Bearer token_abc123" \
|
||||
"200" \
|
||||
"Drupal Content Route"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "4. Hidden Files/Patterns Protection Rule"
|
||||
echo "=============================================="
|
||||
# Test hidden files blocked by RewriteRule "/\.|^\.(?!well-known/)" - [F]
|
||||
test_rule "Block .htaccess hidden file (pattern \.)" \
|
||||
"$BASE_URL/.htaccess" \
|
||||
"403"
|
||||
|
||||
test_rule "Block .htpasswd hidden file (pattern \.)" \
|
||||
"$BASE_URL/.htpasswd" \
|
||||
"403"
|
||||
|
||||
test_rule_content "Allow .well-known/robots.txt (exception for well-known)" \
|
||||
"$BASE_URL/.well-known/robots.txt" \
|
||||
"" \
|
||||
"200" \
|
||||
"User-agent:"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "5. Core install/rebuild.php Protection Rules"
|
||||
echo "============================================}"
|
||||
# Test install.php protection - should route to core/install.php with rewrite=ok parameter
|
||||
test_rule "Core install.php protected routing" \
|
||||
"$BASE_URL/install.php" \
|
||||
"301"
|
||||
|
||||
# Test rebuild.php protection - similar redirect pattern
|
||||
test_rule "Core rebuild.php protected routing" \
|
||||
"$BASE_URL/rebuild.php" \
|
||||
"301"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "6. Drupal Core Files Routing Rules"
|
||||
echo "============================================}"
|
||||
# Test existing file access (!-f condition passes) - should return 200 OK without routing to index.php
|
||||
test_rule_content "Existing favicon.ico access (!-f condition)" \
|
||||
"$BASE_URL/favicon.ico" \
|
||||
"" \
|
||||
"200" \
|
||||
"This is a placeholder for Drupal test favicon.ico"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "7. Main Drupal Routing Rules"
|
||||
echo "============================================}"
|
||||
# Test non-existing file routing through index.php (main routing) - !-f AND !-d pass
|
||||
test_rule_content "Non-existing page routing (routes to index.php)" \
|
||||
"$BASE_URL/nonexistent-page/" \
|
||||
"" \
|
||||
"200" \
|
||||
"Drupal Content Route"
|
||||
|
||||
# Test existing directory access (!-d condition passes) - should return 200 OK
|
||||
test_rule "Existing directory access (somedir/)" \
|
||||
"$BASE_URL/somedir/" \
|
||||
"403"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "8. Core Modules Tests Files Exceptions"
|
||||
echo "============================================}"
|
||||
# Test https.php in tests directory - should NOT route to index.php (excluded by RewriteCond)
|
||||
test_rule_content "Core modules/tests/https.php excluded from routing (!-php condition)" \
|
||||
"$BASE_URL/core/modules/system/tests/https.php" \
|
||||
"" \
|
||||
"200" \
|
||||
"# This is a test Drupal https.php in tests directory"
|
||||
|
||||
# Test http.php in tests directory - same exclusion applies (s for https? regex)
|
||||
test_rule_content "Core modules/tests/http.php excluded from routing" \
|
||||
"$BASE_URL/core/modules/system/tests/http.php" \
|
||||
"" \
|
||||
"200" \
|
||||
"# This is a test Drupal http.php in tests directory"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "Test Suite Complete"
|
||||
echo "=============================================="
|
||||
74
cms/grav/.htaccess
Normal file
74
cms/grav/.htaccess
Normal file
@@ -0,0 +1,74 @@
|
||||
RewriteEngine On
|
||||
|
||||
# ============================================
|
||||
# 1. Security Rules - Test malicious patterns
|
||||
# ============================================
|
||||
|
||||
# Detect template injection (Mustache, Twig syntax)
|
||||
RewriteCond %{REQUEST_URI} ({{|}}|{%|%}) [OR]
|
||||
RewriteCond %{QUERY_STRING} ({{|}}|{%25|%25}) [OR]
|
||||
|
||||
# Base64 encoded payloads in query string
|
||||
RewriteCond %{QUERY_STRING} base64_encode[^(]*\([^)]*\) [OR]
|
||||
|
||||
# Script injection patterns (HTML entities decoded)
|
||||
RewriteCond %{QUERY_STRING} (<|%3C)([^s]*s)+cript.*(>|%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]
|
||||
1
cms/grav/.htaccess.test
Normal file
1
cms/grav/.htaccess.test
Normal file
@@ -0,0 +1 @@
|
||||
This is a .htaccess file - should be blocked by RewriteRule ^(LICENSE\.txt|composer\.lock|composer\.json|\.htaccess)$ error [F]
|
||||
1
cms/grav/.htpasswd
Normal file
1
cms/grav/.htpasswd
Normal file
@@ -0,0 +1 @@
|
||||
This is a hidden .htpasswd file - should be blocked by RewriteRule (^|/)\.(?!well-known) - [F]
|
||||
1
cms/grav/.well-known/robots.txt
Normal file
1
cms/grav/.well-known/robots.txt
Normal file
@@ -0,0 +1 @@
|
||||
Robots.txt file - should be ALLOWED because it matches (.well-known) exception in RewriteRule (^|/)\.(?!well-known) - [F]
|
||||
1
cms/grav/LICENSE.txt
Normal file
1
cms/grav/LICENSE.txt
Normal file
@@ -0,0 +1 @@
|
||||
This is a LICENSE.txt file - should be blocked by RewriteRule ^(LICENSE\.txt|composer\.lock|composer\.json|\.htaccess)$ error [F]
|
||||
119
cms/grav/README.md
Normal file
119
cms/grav/README.md
Normal file
@@ -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: `<script>` or `%3C` encoded → **403**
|
||||
- GLOBALS exploitation: `GLOBALS[` pattern → **403**
|
||||
- _REQUEST manipulation: `_REQUEST[` pattern → **403**
|
||||
|
||||
### 2. Core Routing Rules ✓ (Updated)
|
||||
- Non-existing file routes to index.php → **200** + content check
|
||||
- Existing file access → **200**
|
||||
- Existing directory access → **200**
|
||||
|
||||
### 3. Sensitive Directory Protection ✓
|
||||
- Blocks: `.git`, `cache`, `bin`, `logs`, `backup` folders → **403**
|
||||
- Blocks system/vendor with sensitive extensions (`.txt`, `.xml`) → **403**
|
||||
|
||||
### 4. File Extension Protection ✓
|
||||
- Blocks raw `.md` files → **403**
|
||||
|
||||
### 5. Hidden Files/Directories Protection ✓
|
||||
- Blocks hidden files except `.well-known` → **403** / **200**
|
||||
|
||||
### 6. Critical Configuration File Protection ✓
|
||||
- Blocks: `LICENSE.txt`, `composer.lock`, `composer.json`, `.htaccess` → **403**
|
||||
|
||||
## Run Tests
|
||||
|
||||
Execute the test script to verify all rules:
|
||||
```bash
|
||||
cd /home/alexey/projects/workspace-zed/test1/cms/grav
|
||||
./test-rewriterules.sh
|
||||
```
|
||||
|
||||
Expected results for security tests (all should be **PASS ✓**):
|
||||
- Template injection: HTTP 403 ✓
|
||||
- Twig injection: HTTP 403 ✓
|
||||
- Base64 payload: HTTP 403 ✓
|
||||
- Script injection: HTTP 403 ✓
|
||||
- GLOBALS manipulation: HTTP 403 ✓
|
||||
- _REQUEST manipulation: HTTP 403 ✓
|
||||
|
||||
Expected results for routing tests (should be **PASS ✓**):
|
||||
- Normal page routing: HTTP 200 + content "Grav CMS Content Route" ✓
|
||||
- Existing file access: HTTP 200 ✓
|
||||
- Directory access: HTTP 200 ✓
|
||||
1
cms/grav/backup/archive.zip
Normal file
1
cms/grav/backup/archive.zip
Normal file
@@ -0,0 +1 @@
|
||||
Backup archive.zip file - should be blocked by RewriteRule ^(\.git|cache|bin|logs|backup|webserver-configs|tests)/(.*) error [F]
|
||||
1
cms/grav/bin/helper.php
Normal file
1
cms/grav/bin/helper.php
Normal file
@@ -0,0 +1 @@
|
||||
Bin helper.php file - should be blocked by RewriteRule ^(\.git|cache|bin|logs|backup|webserver-configs|tests)/(.*) error [F]
|
||||
1
cms/grav/cache/test.txt
vendored
Normal file
1
cms/grav/cache/test.txt
vendored
Normal file
@@ -0,0 +1 @@
|
||||
Cache test.txt file - should be blocked by RewriteRule ^(\.git|cache|bin|logs|backup|webserver-configs|tests)/(.*) error [F]
|
||||
1
cms/grav/composer.json
Normal file
1
cms/grav/composer.json
Normal file
@@ -0,0 +1 @@
|
||||
{\"name\": \"test/my-site\", \"description\": \"Test Composer file\"}
|
||||
1
cms/grav/composer.lock
generated
Normal file
1
cms/grav/composer.lock
generated
Normal file
@@ -0,0 +1 @@
|
||||
This is a composer.lock file - should be blocked by RewriteRule ^(LICENSE\.txt|composer\.lock|composer\.json|\.htaccess)$ error [F]
|
||||
0
cms/grav/existing.jpg
Normal file
0
cms/grav/existing.jpg
Normal file
15
cms/grav/index.php
Normal file
15
cms/grav/index.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
/**
|
||||
* Grav CMS - index.php (Test version)
|
||||
* This file handles routing for non-existing files/directories
|
||||
*/
|
||||
|
||||
// Simulated Grav CMS response
|
||||
echo "<html><head><title>Grav CMS Test Site</title></head><body>";
|
||||
echo "<h1>Grav CMS Content Route</h1>";
|
||||
echo "<p>This page is served by index.php via RewriteRule.</p>";
|
||||
echo "<div class='graviconfig'>Site Configuration Loaded</div>";
|
||||
echo "</body></html>";
|
||||
|
||||
// Exit
|
||||
exit;
|
||||
159
cms/grav/nginx.conf
Normal file
159
cms/grav/nginx.conf
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
1
cms/grav/normal-page.md
Normal file
1
cms/grav/normal-page.md
Normal file
@@ -0,0 +1 @@
|
||||
Normal Grav CMS page - should be routed through index.php
|
||||
14
cms/grav/site2.conf
Normal file
14
cms/grav/site2.conf
Normal file
@@ -0,0 +1,14 @@
|
||||
<VirtualHost *:80>
|
||||
DocumentRoot "/sites/site2"
|
||||
ServerName example2.com
|
||||
|
||||
<Directory /sites/site2>
|
||||
Options +Indexes +FollowSymLinks
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
<FilesMatch \.php$>
|
||||
SetHandler "proxy:unix:/run/php-fpm/www.sock|fcgi://localhost"
|
||||
</FilesMatch>
|
||||
|
||||
</VirtualHost>
|
||||
0
cms/grav/somedir/.gitkeep
Normal file
0
cms/grav/somedir/.gitkeep
Normal file
1
cms/grav/system/config.xml
Normal file
1
cms/grav/system/config.xml
Normal file
@@ -0,0 +1 @@
|
||||
System config.xml file - should be blocked by RewriteRule ^(system|vendor)/(.*)\.(txt|xml|md|html|json|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ error [F]
|
||||
1
cms/grav/test-mustache.php
Normal file
1
cms/grav/test-mustache.php
Normal file
@@ -0,0 +1 @@
|
||||
This is a Mustache template injection test file with {{ variable }} syntax
|
||||
150
cms/grav/test-rewriterules.sh
Executable file
150
cms/grav/test-rewriterules.sh
Executable file
@@ -0,0 +1,150 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ============================================
|
||||
# Grav CMS .htaccess Rules Test Script
|
||||
# ============================================
|
||||
# This script tests each rule from cms/grav/.htaccess
|
||||
# Domain: test.my.brp
|
||||
# ============================================
|
||||
|
||||
BASE_URL="http://test.my.brp"
|
||||
|
||||
echo "=============================================="
|
||||
echo "Grav CMS .htaccess Rules Test Suite"
|
||||
echo "=============================================="
|
||||
echo ""
|
||||
|
||||
# Function to test a rule and report result (status only)
|
||||
test_rule() {
|
||||
local description="$1"
|
||||
local url="$2"
|
||||
local expected_status="$3" # e.g., 403, 404, 200
|
||||
|
||||
echo "--- Test: $description ---"
|
||||
response=$(curl -s -o /dev/null -w "%{http_code}" "$url")
|
||||
|
||||
if [ "$response" = "$expected_status" ]; then
|
||||
echo "✓ PASS (HTTP $response)"
|
||||
else
|
||||
echo "✗ FAIL (Expected: HTTP $expected_status, Got: HTTP $response)"
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Function to test a rule and verify content contains expected string
|
||||
test_rule_content() {
|
||||
local description="$1"
|
||||
local url="$2"
|
||||
local expected_status="$3" # e.g., 403, 404, 200
|
||||
local expected_content="$4" # Expected substring in response body
|
||||
|
||||
echo "--- Test: $description ---"
|
||||
response=$(curl -s "$url")
|
||||
http_code=$(curl -s -o /dev/null -w "%{http_code}" "$url")
|
||||
|
||||
# Check status code
|
||||
if [ "$http_code" != "$expected_status" ]; then
|
||||
echo "✗ FAIL (Status: HTTP $http_code, Expected: HTTP $expected_status)"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check content contains expected substring
|
||||
if [[ "$response" == *"$expected_content"* ]]; then
|
||||
echo "✓ PASS (HTTP $http_code, Content matches '$expected_content')"
|
||||
else
|
||||
echo "✗ FAIL (Content missing: '$expected_content') - Response:"
|
||||
echo "$response" | head -5
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
echo "=============================================="
|
||||
echo "1. Security Rules - Test malicious patterns"
|
||||
echo "=============================================="
|
||||
# Template injection via query string with {{ }} Mustache syntax
|
||||
test_rule "Template injection via query string ({{ }}" \
|
||||
"$BASE_URL/test-mustache.php?\{\{config.secret\}\}" \
|
||||
"403"
|
||||
|
||||
# Base64 encoded payloads (base64_encode function call)
|
||||
test_rule "Base64 payload pattern" \
|
||||
"$BASE_URL/test.php?data=base64_encode(some_secret)" \
|
||||
"403"
|
||||
|
||||
# Script injection (<script> or HTML entities %3C)
|
||||
test_rule "Script injection (encoded)" \
|
||||
"$BASE_URL/search.html?q=%3Cscript%25alert(1)%3E" \
|
||||
"403"
|
||||
|
||||
# GLOBALS exploitation attempt
|
||||
test_rule "GLOBALS manipulation" \
|
||||
"$BASE_URL/leak.php?GLOBALS\[secret\]=exploit" \
|
||||
"403"
|
||||
|
||||
# _REQUEST manipulation (PHP superglobal abuse)
|
||||
test_rule "_REQUEST manipulation" \
|
||||
"$BASE_URL/attack.php?_REQUEST\[user\]=admin" \
|
||||
"403"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "2. Grav CMS Core Routing Rules"
|
||||
echo "=============================================="
|
||||
# Test normal page routing through index.php (non-existing file -> index.php)
|
||||
test_rule_content "Normal page routing via index.php" \
|
||||
"$BASE_URL/normal-page/" \
|
||||
"200" \
|
||||
"Grav CMS Content Route"
|
||||
|
||||
# Test existing file access - check status + content type/image
|
||||
test_rule "Existing file access (existing.jpg)" \
|
||||
"$BASE_URL/existing.jpg" \
|
||||
"200"
|
||||
|
||||
# Test existing directory access
|
||||
test_rule "Existing directory access (somedir/)" \
|
||||
"$BASE_URL/somedir/" \
|
||||
"200"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "3. Sensitive Directory Protection"
|
||||
echo "=============================================="
|
||||
# Block system, cache, logs, backups folders
|
||||
test_rule "Block .git folder" "$BASE_URL/.git/secret.txt" "403"
|
||||
test_rule "Block cache folder" "$BASE_URL/cache/test.txt" "403"
|
||||
test_rule "Block bin folder" "$BASE_URL/bin/helper.php" "403"
|
||||
test_rule "Block logs folder" "$BASE_URL/logs/app.log" "403"
|
||||
test_rule "Block backup folder" "$BASE_URL/backup/archive.zip" "403"
|
||||
|
||||
# Block system and vendor directories with sensitive extensions
|
||||
test_rule "Block system config.xml" "$BASE_URL/system/config.xml" "403"
|
||||
test_rule "Block vendor module.txt" "$BASE_URL/vendor/module.txt" "403"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "4. File Extension Protection"
|
||||
echo "=============================================="
|
||||
# Block raw .md file access (content source files)
|
||||
test_rule "Block raw .md file" "$BASE_URL/test.md" "403"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "5. Hidden Files/Directories Protection"
|
||||
echo "=============================================="
|
||||
test_rule "Block hidden .htpasswd" "$BASE_URL/.htpasswd" "403"
|
||||
test_rule "Allow .well-known/robots.txt" "$BASE_URL/.well-known/robots.txt" "200"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "6. Critical Configuration File Protection"
|
||||
echo "=============================================="
|
||||
test_rule "Block LICENSE.txt" "$BASE_URL/LICENSE.txt" "403"
|
||||
test_rule "Block composer.lock" "$BASE_URL/composer.lock" "403"
|
||||
test_rule "Block composer.json" "$BASE_URL/composer.json" "403"
|
||||
test_rule "Block .htaccess" "$BASE_URL/.htaccess" "403"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "Test Suite Complete"
|
||||
echo "=============================================="
|
||||
1
cms/grav/test.md
Normal file
1
cms/grav/test.md
Normal file
@@ -0,0 +1 @@
|
||||
Test.md
|
||||
1
cms/grav/tests/unit-test.php
Normal file
1
cms/grav/tests/unit-test.php
Normal file
@@ -0,0 +1 @@
|
||||
Tests unit-test.php file - should be blocked by RewriteRule ^(\.git|cache|bin|logs|backup|webserver-configs|tests)/(.*) error [F]
|
||||
1
cms/grav/twig-test.html
Normal file
1
cms/grav/twig-test.html
Normal file
@@ -0,0 +1 @@
|
||||
This is a Twig template syntax test file with {% comment %} syntax
|
||||
1
cms/grav/user/config.yml
Normal file
1
cms/grav/user/config.yml
Normal file
@@ -0,0 +1 @@
|
||||
User config.yml file - should be blocked by RewriteRule ^(user)/(.*)\.(txt|md|json|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ error [F]
|
||||
1
cms/grav/user/data.json
Normal file
1
cms/grav/user/data.json
Normal file
@@ -0,0 +1 @@
|
||||
User data.json file - should be blocked by RewriteRule ^(user)/(.*)\.(txt|md|json|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ error [F]
|
||||
1
cms/grav/user/module.php
Normal file
1
cms/grav/user/module.php
Normal file
@@ -0,0 +1 @@
|
||||
User module.php file - should be blocked by RewriteRule ^(user)/(.*)\.(txt|md|json|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ error [F]
|
||||
1
cms/grav/user/script.sh
Normal file
1
cms/grav/user/script.sh
Normal file
@@ -0,0 +1 @@
|
||||
User script.sh file - should be blocked by RewriteRule ^(user)/(.*)\.(txt|md|json|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ error [F]
|
||||
1
cms/grav/user/settings.yaml
Normal file
1
cms/grav/user/settings.yaml
Normal file
@@ -0,0 +1 @@
|
||||
User settings.yaml file - should be blocked by RewriteRule ^(user)/(.*)\.(txt|md|json|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ error [F]
|
||||
1
cms/grav/user/template.twig
Normal file
1
cms/grav/user/template.twig
Normal file
@@ -0,0 +1 @@
|
||||
User template.twig file - should be blocked by RewriteRule ^(user)/(.*)\.(txt|md|json|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ error [F]
|
||||
1
cms/grav/user/test.txt
Normal file
1
cms/grav/user/test.txt
Normal file
@@ -0,0 +1 @@
|
||||
User test.txt file - should be blocked by RewriteRule ^(user)/(.*)\.(txt|md|json|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ error [F]
|
||||
1
cms/grav/vendor/module.txt
vendored
Normal file
1
cms/grav/vendor/module.txt
vendored
Normal file
@@ -0,0 +1 @@
|
||||
Vendor module.txt file - should be blocked by RewriteRule ^(system|vendor)/(.*)\.(txt|xml|md|html|json|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ error [F]
|
||||
1
cms/grav/webserver-configs/nginx.conf
Normal file
1
cms/grav/webserver-configs/nginx.conf
Normal file
@@ -0,0 +1 @@
|
||||
Webserver configs nginx.conf file - should be blocked by RewriteRule ^(\.git|cache|bin|logs|backup|webserver-configs|tests)/(.*) error [F]
|
||||
16
cms/joomla/.htaccess
Normal file
16
cms/joomla/.htaccess
Normal file
@@ -0,0 +1,16 @@
|
||||
RewriteEngine On
|
||||
RewriteCond %{QUERY_STRING} base64_encode[^(]*\([^)]*\) [OR]
|
||||
RewriteCond %{QUERY_STRING} (<|%3C)([^s]*s)+cript.*(>|%3E) [NC,OR]
|
||||
RewriteCond %{QUERY_STRING} GLOBALS(=|\[|\%[0-9A-Z]{0,2}) [OR]
|
||||
RewriteCond %{QUERY_STRING} _REQUEST(=|\[|\%[0-9A-Z]{0,2})
|
||||
RewriteRule .* index.php [F]
|
||||
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
|
||||
RewriteCond %{REQUEST_URI} ^/api/
|
||||
RewriteCond %{REQUEST_URI} !^/api/index\.php
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule .* api/index.php [L]
|
||||
RewriteCond %{REQUEST_URI} !^/index\.php
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule .* index.php [L]
|
||||
6
cms/joomla/.well-known/robots.txt
Normal file
6
cms/joomla/.well-known/robots.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
# This is a well-known robots.txt file for Joomla testing
|
||||
# Should be accessible via RewriteRule /api/index.php [L] or standard routing
|
||||
|
||||
User-agent: *
|
||||
Disallow: /administrator/
|
||||
Allow: /
|
||||
153
cms/joomla/README.md
Normal file
153
cms/joomla/README.md
Normal file
@@ -0,0 +1,153 @@
|
||||
# Joomla .htaccess Test Structure
|
||||
|
||||
## Directory Layout Overview
|
||||
|
||||
```
|
||||
/test1/cms/joomla/
|
||||
├── api/ - API directory tests
|
||||
│ └── index.php - Joomla API entry point (routes /api/ requests)
|
||||
│ - Returns: "Joomla API Configuration Loaded"
|
||||
├── .well-known/ - Well-known directory
|
||||
│ └── robots.txt - Allowed file via exception
|
||||
├── base64-test.php - Security test for base64_encode pattern detection
|
||||
├── globals-test.php - Security test for GLOBALS exploitation pattern
|
||||
├── request-test.php - Security test for _REQUEST manipulation pattern
|
||||
├── script-test.php - Security test for script injection pattern
|
||||
├── index.php - Joomla main entry point (routes non-existing files)
|
||||
│ - Returns: "Joomla Content Route" page
|
||||
├── somedir/ - Directory for testing !-d condition (200 OK)
|
||||
├── test-joomla-rewriterules.sh - Bash script to test all rules using curl
|
||||
└── README.md - This documentation file
|
||||
```
|
||||
|
||||
## Apache Rules Explained - Joomla
|
||||
|
||||
### 1. Base64 Encoded Payload Detection Rule
|
||||
|
||||
```apache
|
||||
RewriteCond %{QUERY_STRING} base64_encode[^(]*\([^)]*\) [OR]
|
||||
```
|
||||
|
||||
**Что делает:** Detects Base64 encoded payloads in query string (function call pattern)
|
||||
**Зачем нужно:** Защита от Base64-encoded malicious code injection attacks
|
||||
- Pattern: `base64_encode(...)` - detect function calls that encode data
|
||||
|
||||
### 2. Script Injection Pattern Detection Rule
|
||||
|
||||
```apache
|
||||
RewriteCond %{QUERY_STRING} (<|%3C)([^s]*s)+cript.*(>|%3E) [NC,OR]
|
||||
```
|
||||
|
||||
**Что делает:** Detects script injection patterns (HTML entities decoded)
|
||||
**Зачем нужно:** Защита от XSS attacks через URL parameters
|
||||
- Pattern: `<script>...` or `%3Cscript%3E` - detect HTML script tags
|
||||
- `[NC]` - case-insensitive matching
|
||||
|
||||
### 3. GLOBALS Exploitation Detection Rule
|
||||
|
||||
```apache
|
||||
RewriteCond %{QUERY_STRING} GLOBALS(=|\[|\%[0-9A-Z]{0,2}) [OR]
|
||||
```
|
||||
|
||||
**Что делает:** Detects GLOBALS exploitation attempt in query string
|
||||
**Зачем нужно:** Защита от PHP superglobal abuse attacks
|
||||
- Pattern: `GLOBALS=` or `GLOBALS[` - detect attempts to manipulate global variables
|
||||
|
||||
### 4. _REQUEST Manipulation Detection Rule
|
||||
|
||||
```apache
|
||||
RewriteCond %{QUERY_STRING} _REQUEST(=|\[|\%[0-9A-Z]{0,2})
|
||||
```
|
||||
|
||||
**Что делает:** Detects _REQUEST manipulation (PHP superglobal abuse)
|
||||
**Зачем нужно:** Защита от PHP superglobal exploitation attacks
|
||||
- Pattern: `_REQUEST=` or `_REQUEST[` - detect attempts to manipulate $_REQUEST array
|
||||
|
||||
### 5. Malicious Requests Blocked with [F] Flag
|
||||
|
||||
```apache
|
||||
RewriteRule .* index.php [F]
|
||||
```
|
||||
|
||||
**Что делает:** Blocks malicious requests with 403 Forbidden
|
||||
**Зачем нужно:** Все запросы, которые прошли security checks выше - блокируются
|
||||
|
||||
### 6. HTTP Authorization Header Passing Rule
|
||||
|
||||
```apache
|
||||
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
|
||||
```
|
||||
|
||||
**Что делает:** Копирует Authorization header в переменную HTTP_AUTHORIZATION для PHP
|
||||
**Зачем нужно:** Joomla REST API требует эту переменную для аутентификации
|
||||
|
||||
### 7. Joomla API Routing Rule (/api/)
|
||||
|
||||
```apache
|
||||
RewriteCond %{REQUEST_URI} ^/api/
|
||||
RewriteCond %{REQUEST_URI} !^/api/index\.php
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule .* api/index.php [L]
|
||||
```
|
||||
|
||||
**Что делает:** Маршрутизирует запросы в `/api/` через `api/index.php` (если не существующий файл/directory)
|
||||
**Зачем нужно:** Joomla REST API routing - отдельная точка входа для API endpoints
|
||||
- **Исключение:** Если запрос прямо на /api/index.php - пропускаем (не переписываем)
|
||||
|
||||
### 8. Main Joomla Routing Rule (/index.php exclusion)
|
||||
|
||||
```apache
|
||||
RewriteCond %{REQUEST_URI} !^/index\.php
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule .* index.php [L]
|
||||
```
|
||||
|
||||
**Что делает:** Маршрутизирует все запросы через main `index.php` (!-f AND !-d pass)
|
||||
**Зачем нужно:** Joomla "clean URLs" routing (похоже на WordPress/Drupal)
|
||||
- **Исключение:** Не переписывает прямой доступ к /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
|
||||
|
||||
## Security Pattern Testing Scenarios
|
||||
|
||||
### Query String Pattern Detection
|
||||
|
||||
| URL | Правило | Ожидаемый результат |
|
||||
|-----|---------|---------------------|
|
||||
| `http://test.my.brp/base64-test.php?data=base64_encode(secret)` | base64_encode pattern detection → 403 ✓ |
|
||||
| `http://test.my.brp/script-test.php?q=%3Cscript%3Ealert(1)%3E` | script injection pattern → 403 ✓ |
|
||||
| `http://test.my.brp/globals-test.php?GLOBALS[user]=admin` | GLOBALS exploitation → 403 ✓ |
|
||||
| `http://test.my.brp/request-test.php?_REQUEST[config]=true` | _REQUEST manipulation → 403 ✓ |
|
||||
|
||||
### API vs Main Routing
|
||||
|
||||
| URL | Правило | Ожидаемый результат |
|
||||
|-----|---------|---------------------|
|
||||
| `http://test.my.brp/api/` | api/index.php routing (!-f, !-d pass) → api/index.php ✓ |
|
||||
| `http://test.my.brp/api/index.php` | Direct access (excluded from api routing) → 200 OK ✓ |
|
||||
| `http://test.my.brp/nonexistent-page/` | Main index.php routing (!-f, !-d pass) → main index.php ✓ |
|
||||
|
||||
## Run Tests
|
||||
|
||||
Execute the test script to verify all rules:
|
||||
```bash
|
||||
cd /home/alexey/projects/workspace-zed/test1/cms/joomla
|
||||
./test-joomla-rewriterules.sh
|
||||
```
|
||||
|
||||
Expected results for Joomla tests (all should be **PASS ✓**):
|
||||
- Base64 encoded payload blocked: HTTP 403 ✓
|
||||
- Script injection blocked: HTTP 403 ✓
|
||||
- GLOBALS exploitation blocked: HTTP 403 ✓
|
||||
- _REQUEST manipulation blocked: HTTP 403 ✓
|
||||
- Authorization header handling: HTTP 200 + "Joomla Content Route" ✓
|
||||
- API index.php routing: HTTP 200 + "Joomla API Configuration Loaded" ✓
|
||||
- Direct /api/index.php file access: HTTP 200 OK ✓
|
||||
- Main index.php routing (non-existing page): HTTP 200 + "Joomla Content Route" ✓
|
||||
- Directory access (!-d): HTTP 200 OK ✓
|
||||
56
cms/joomla/api/index.php
Normal file
56
cms/joomla/api/index.php
Normal file
@@ -0,0 +1,56 @@
|
||||
|
||||
<?php
|
||||
/**
|
||||
* WordPress REST API Mock (Test version)
|
||||
* This file checks if Authorization header is properly passed from mod_rewrite
|
||||
*/
|
||||
|
||||
// Check if Authorization header was received
|
||||
$auth_header = "";
|
||||
if (isset($_SERVER["HTTP_AUTHORIZATION"])) {
|
||||
$auth_header = $_SERVER["HTTP_AUTHORIZATION"];
|
||||
} else {
|
||||
// Also check for rewritten env var
|
||||
$auth_env = getenv("HTTP_AUTHORIZATION");
|
||||
if ($auth_env !== false && $auth_env !== "") {
|
||||
$auth_header = $auth_env;
|
||||
}
|
||||
}
|
||||
|
||||
// Set response headers
|
||||
header("Content-Type: application/json; charset=UTF-8");
|
||||
|
||||
if ($auth_header === "secret_token_123") {
|
||||
// SUCCESS - Authorization header was properly passed through mod_rewrite
|
||||
|
||||
echo json_encode(
|
||||
[
|
||||
"status" => "success",
|
||||
"message" => "Authorization verified",
|
||||
"token_verified" => true,
|
||||
"wordpress_config_loaded" => true,
|
||||
"received_auth_header" => $auth_header,
|
||||
],
|
||||
JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES,
|
||||
);
|
||||
} else {
|
||||
// FAIL - Authorization header was not passed through mod_rewrite
|
||||
|
||||
http_response_code(401);
|
||||
|
||||
echo json_encode(
|
||||
[
|
||||
"status" => "error",
|
||||
"message" => "unauth",
|
||||
"expected" => "Bearer secret_token_123",
|
||||
"received" => $auth_header ?: "(not set)",
|
||||
"test_failed" => true,
|
||||
"hint" =>
|
||||
"mod_rewrite [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] is NOT passing header to PHP",
|
||||
],
|
||||
JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES,
|
||||
);
|
||||
}
|
||||
|
||||
exit();
|
||||
|
||||
1
cms/joomla/api/index.php.dummy
Normal file
1
cms/joomla/api/index.php.dummy
Normal file
@@ -0,0 +1 @@
|
||||
# Joomla API test - should be allowed when accessed via /api/index.php (excluded from index.php routing)
|
||||
2
cms/joomla/base64-test.php
Normal file
2
cms/joomla/base64-test.php
Normal file
@@ -0,0 +1,2 @@
|
||||
# Joomla security test - base64_encode payload pattern
|
||||
# This file should be blocked when accessed with query string containing base64_encode[^(]*\([^)]*\)
|
||||
2
cms/joomla/globals-test.php
Normal file
2
cms/joomla/globals-test.php
Normal file
@@ -0,0 +1,2 @@
|
||||
# Joomla security test - GLOBALS exploitation pattern
|
||||
# This file should be blocked when accessed with query string containing GLOBALS(=|\[|\%[0-9A-Z]{0,2})
|
||||
15
cms/joomla/index.php
Normal file
15
cms/joomla/index.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
/**
|
||||
* Joomla - index.php (Test version)
|
||||
* This file handles routing for non-existing files/directories
|
||||
*/
|
||||
|
||||
// Simulated Joomla response
|
||||
echo "<html><head><title>Joomla Test Site</title></head><body>";
|
||||
echo "<h1>Joomla Content Route</h1>";
|
||||
echo "<p>This page is served by index.php via RewriteRule.</p>";
|
||||
echo "<div class='joomla-config'>Joomla Configuration Loaded</div>";
|
||||
echo "</body></html>";
|
||||
|
||||
// Exit
|
||||
exit;
|
||||
159
cms/joomla/nginx.conf
Normal file
159
cms/joomla/nginx.conf
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
2
cms/joomla/request-test.php
Normal file
2
cms/joomla/request-test.php
Normal file
@@ -0,0 +1,2 @@
|
||||
# Joomla security test - _REQUEST manipulation pattern
|
||||
# This file should be blocked when accessed with query string containing _REQUEST(=|\[|\%[0-9A-Z]{0,2})
|
||||
2
cms/joomla/script-test.php
Normal file
2
cms/joomla/script-test.php
Normal file
@@ -0,0 +1,2 @@
|
||||
# Joomla security test - script injection pattern
|
||||
# This file should be blocked when accessed with query string containing (<|%3C)([^s]*s)+cript.*(>|%3E)
|
||||
16
cms/joomla/site4.conf
Normal file
16
cms/joomla/site4.conf
Normal file
@@ -0,0 +1,16 @@
|
||||
<VirtualHost *:80>
|
||||
DocumentRoot "/sites/site4"
|
||||
ServerName example4.com
|
||||
|
||||
DirectoryIndex index.php
|
||||
|
||||
<Directory /sites/site4>
|
||||
Options +Indexes +FollowSymLinks
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
<FilesMatch \.php$>
|
||||
SetHandler "proxy:unix:/run/php-fpm/www.sock|fcgi://localhost"
|
||||
</FilesMatch>
|
||||
|
||||
</VirtualHost>
|
||||
170
cms/joomla/test-joomla-rewriterules.sh
Executable file
170
cms/joomla/test-joomla-rewriterules.sh
Executable file
@@ -0,0 +1,170 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ============================================
|
||||
# Joomla .htaccess Rules Test Script
|
||||
# ============================================
|
||||
# This script tests each rule from cms/joomla/.htaccess
|
||||
# Assumption: Site root is mapped to /home/alexey/projects/workspace-zed/test1/cms/joomla
|
||||
# Domain: test.my.brp
|
||||
# ============================================
|
||||
|
||||
BASE_URL="http://test.my.brp"
|
||||
|
||||
echo "=============================================="
|
||||
echo "Joomla .htaccess Rules Test Suite"
|
||||
echo "=============================================="
|
||||
echo ""
|
||||
|
||||
# Function to test a rule and report result (status only)
|
||||
test_rule() {
|
||||
local description="$1"
|
||||
local url="$2"
|
||||
local expected_status="$3" # e.g., 403, 404, 200, 301
|
||||
|
||||
echo "--- Test: $description ---"
|
||||
response=$(curl -s -o /dev/null -w "%{http_code}" "$url")
|
||||
|
||||
if [ "$response" = "$expected_status" ]; then
|
||||
echo "✓ PASS (HTTP $response)"
|
||||
else
|
||||
echo "✗ FAIL (Expected: HTTP $expected_status, Got: HTTP $response)"
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Function to test a rule and verify content contains expected string
|
||||
test_rule_content() {
|
||||
local description="$1"
|
||||
local url="$2"
|
||||
local headers="$3" # Optional: additional curl -H header flags (can be empty)
|
||||
local expected_status="$4" # e.g., 403, 404, 200, 301
|
||||
local expected_content="$5" # Expected substring in response body
|
||||
|
||||
echo "--- Test: $description ---"
|
||||
|
||||
if [ -n "$headers" ]; then
|
||||
response=$(curl -s -H "$headers" "$url")
|
||||
http_code=$(curl -s -H "$headers" -o /dev/null -w "%{http_code}" "$url")
|
||||
else
|
||||
response=$(curl -s "$url")
|
||||
http_code=$(curl -s -o /dev/null -w "%{http_code}" "$url")
|
||||
fi
|
||||
|
||||
# Check status code
|
||||
if [ "$http_code" != "$expected_status" ]; then
|
||||
echo "✗ FAIL (Status: HTTP $http_code, Expected: HTTP $expected_status)"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check content contains expected substring
|
||||
if [[ "$response" == *"$expected_content"* ]]; then
|
||||
echo "✓ PASS (HTTP $http_code, Content matches '$expected_content')"
|
||||
else
|
||||
echo "✗ FAIL (Content missing: '$expected_content') - Response:"
|
||||
echo "$response" | head -5
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
echo "=============================================="
|
||||
echo "1. Base64 Encoded Payload Detection Rule"
|
||||
echo "=============================================="
|
||||
# Test base64_encode pattern detection in query string
|
||||
test_rule "Base64 encoded payload blocked (base64_encode function call)" \
|
||||
"$BASE_URL/base64-test.php?data=base64_encode(secret_password)" \
|
||||
"403"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "2. Script Injection Pattern Detection Rule"
|
||||
echo "============================================}"
|
||||
# Test script injection pattern detection in query string (HTML entities)
|
||||
test_rule "Script injection blocked (script tag encoded)" \
|
||||
"$BASE_URL/script-test.php?q=%3Cscript%3Ealert(1)%3E" \
|
||||
"403"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "3. GLOBALS Exploitation Detection Rule"
|
||||
echo "============================================}"
|
||||
# Test GLOBALS exploitation pattern detection in query string
|
||||
test_rule "GLOBALS exploitation blocked (GLOBALS[secret] pattern)" \
|
||||
"$BASE_URL/globals-test.php?GLOBALS%5Buser%5D=admin" \
|
||||
"403"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "4. _REQUEST Manipulation Detection Rule"
|
||||
echo "============================================}"
|
||||
# Test _REQUEST manipulation pattern detection in query string
|
||||
test_rule "_REQUEST manipulation blocked (_REQUEST[config] pattern)" \
|
||||
"$BASE_URL/request-test.php?_REQUEST%5Badmin%5D=true" \
|
||||
"403"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "5. HTTP Authorization Header Passing Rule"
|
||||
echo "============================================}"
|
||||
# Test that Authorization header is properly handled by Joomla REST API
|
||||
test_rule_content "Joomla handles Authorization header (API request)" \
|
||||
"$BASE_URL/rest/api/v1" \
|
||||
"Authorization: Bearer joomla_token_abc" \
|
||||
"200" \
|
||||
"Joomla Content Route"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "6. Joomla API Routing Rule (/api/)"
|
||||
echo "============================================}"
|
||||
# Test /api/index.php routing - should route to api/index.php (!-f, !-d pass for non-existing files)
|
||||
test_rule_content "API index.php routing (routes to /api/index.php)" \
|
||||
"$BASE_URL/api/" \
|
||||
"Authorization: secret_token_123" \
|
||||
"200" \
|
||||
"\"status\": \"success\""
|
||||
|
||||
# Additional test: verify token verification in response
|
||||
test_rule_content "Direct /api/index.php file access" \
|
||||
"$BASE_URL/api/index.php" \
|
||||
"" \
|
||||
"401" \
|
||||
"\"message\": \"unauth\""
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "7. Main Joomla Routing Rule (/index.php exclusion)"
|
||||
echo "============================================}"
|
||||
# Test that /api/index.php is NOT routed to main index.php (!^/api/index\.php passes)
|
||||
test_rule_content "/api/index.php excluded from main routing (direct file access)" \
|
||||
"$BASE_URL/api/index.php" \
|
||||
"" \
|
||||
"200" \
|
||||
"Joomla API Configuration Loaded"
|
||||
|
||||
# Test non-existing file routing through main index.php (!-f AND !-d pass)
|
||||
test_rule_content "Non-existing page routing (routes to /index.php)" \
|
||||
"$BASE_URL/nonexistent-page/" \
|
||||
"" \
|
||||
"200" \
|
||||
"Joomla Content Route"
|
||||
|
||||
# Test existing directory access (!-d condition passes) - should return 200 OK
|
||||
test_rule "Existing directory access (somedir/)" \
|
||||
"$BASE_URL/somedir/" \
|
||||
"200"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "8. Joomla index.php reditercting"
|
||||
echo "============================================}"
|
||||
# Test that /api/index.php is NOT routed to main index.php (!^/api/index\.php passes)
|
||||
test_rule_content "/index.php/component/config?view=modules accessing" \
|
||||
"$BASE_URL/index.php/component/config?view=modules" \
|
||||
"" \
|
||||
"200" \
|
||||
"Joomla Content Route"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "Test Suite Complete"
|
||||
echo "=============================================="
|
||||
73
cms/simple/.htaccess
Normal file
73
cms/simple/.htaccess
Normal file
@@ -0,0 +1,73 @@
|
||||
# ============================================================
|
||||
# .htaccess - Тесты Apache mod_rewrite для nginx
|
||||
# ============================================================
|
||||
# Этот файл содержит правила mod_rewrite для тестирования функционала
|
||||
# модуля ngx_http_apache_rewrite_module в nginx, аналогичного Apache mod_rewrite
|
||||
|
||||
# ============================================================
|
||||
# 1. Перенаправление файлов с расширением .xmx на index.html
|
||||
# ============================================================
|
||||
# Правиство перенаправляет ВСЕ файлы заканчивающиеся на .xmx
|
||||
# на страницу index.html (внешнее перенаправление)
|
||||
|
||||
RewriteEngine On
|
||||
RewriteRule \.xmx$ /index.html [R=301,L]
|
||||
|
||||
# Тестирование:
|
||||
# - http://localhost/test/test.xmx → 301 redirect to /index.html
|
||||
# - http://localhost/test/subdir/file.xmx → 301 redirect to /index.html
|
||||
|
||||
# ============================================================
|
||||
# 2. Блокировка доступа к stop.html (403 Forbidden)
|
||||
# ============================================================
|
||||
# Правиство запрещает доступ к файлу stop.html, возвращая ошибку 403
|
||||
|
||||
RewriteCond %{REQUEST_FILENAME} stop.html$
|
||||
RewriteRule ^ - [F,L]
|
||||
|
||||
# Тестирование:
|
||||
# - http://localhost/test/stop.html → 403 Forbidden (доступ запрещен)
|
||||
|
||||
# ============================================================
|
||||
# 3. Внутреннее перенаправление redirect.html на show.html
|
||||
# ============================================================
|
||||
# Правиство скрывает redirect.html и показывает вместо него show.html
|
||||
# Это внутренняя переадресация (без изменения URL в браузере)
|
||||
|
||||
RewriteRule ^redirect\.html$ show.html [L]
|
||||
|
||||
# Тестирование:
|
||||
# - http://localhost/test/redirect.html → показывает содержимое show.html
|
||||
# - URL остается /test/redirect.html, но показывается content из show.html
|
||||
|
||||
# ============================================================
|
||||
# 4. Перенаправление подкаталога subdir на index.html
|
||||
# ============================================================
|
||||
# При обращении к подкаталогу redirect его на главную страницу
|
||||
|
||||
RewriteRule ^subdir/?$ /index.html [L]
|
||||
|
||||
# Тестирование:
|
||||
# - http://localhost/test/subdir/ → показывает index.html
|
||||
|
||||
# ============================================================
|
||||
# 5. Блокировка доступа ко всем .xmx файлам (кроме test.xmx)
|
||||
# ============================================================
|
||||
# Дополнительно блокируем все файлы с расширением xmx кроме test.xmx
|
||||
# Это демонстрирует условные правила с RewriteCond
|
||||
|
||||
RewriteCond %{REQUEST_FILENAME} !test\.xmx$
|
||||
RewriteRule \.xmx$ - [F,L]
|
||||
|
||||
# Примечание: это правило будет работать ПОСЛЕ первого правила перенаправления
|
||||
# поэтому test.xmx сначала перенаправляется на index.html, а затем блокируется
|
||||
|
||||
# ============================================================
|
||||
# 6. Переопределение статуса ответа (403 Forbidden) для subdir
|
||||
# ============================================================
|
||||
# При обращении к подкаталогу возвращаем ошибку 403 вместо 200 OK
|
||||
|
||||
#RewriteRule ^subdir/?$ - [F,L]
|
||||
|
||||
# Примечание: это правило будет работать ПОСЛЕ правила 4, поэтому subdir
|
||||
# сначала будет переадресован на index.html, а затем заблокирован
|
||||
127
cms/simple/README.md
Normal file
127
cms/simple/README.md
Normal file
@@ -0,0 +1,127 @@
|
||||
# Тесты Apache mod_rewrite для nginx
|
||||
|
||||
## Обзор
|
||||
|
||||
Этот каталог содержит тестовые файлы и правила `.htaccess` для проверки функционала модуля `ngx_http_apache_rewrite_module` в nginx, который обеспечивает совместимость с Apache mod_rewrite.
|
||||
|
||||
## Файлы
|
||||
|
||||
| Файл | Назначение |
|
||||
|------|------------|
|
||||
| `index.html` | Главная страница - назначение для перенаправления .xmx файлов |
|
||||
| `stop.html` | Заблокированная страница (должен возвращать 403) |
|
||||
| `redirect.html` | Внутреннее перенаправление на show.html (URL не меняется) |
|
||||
| `show.html` | Показывается вместо redirect.html при внутреннем rewrite |
|
||||
| `test.xmx` | Тестовый файл для проверки правила перенаправления .xmx расширения |
|
||||
|
||||
## Правила .htaccess
|
||||
|
||||
### 1. Перенаделение файлов с расширением `.xmx` на index.html
|
||||
|
||||
```apache
|
||||
RewriteRule \.xmx$ /index.html [R=301,L]
|
||||
```
|
||||
|
||||
**Ожидаемое поведение:**
|
||||
- Запрос `http://localhost/test/test.xmx` → **301 Redirect** к `/test/index.html`
|
||||
- URL в браузере изменится на `/test/index.html`
|
||||
|
||||
### 2. Блокировка доступа к stop.html (403 Forbidden)
|
||||
|
||||
```apache
|
||||
RewriteCond %{REQUEST_FILENAME} =stop.html
|
||||
RewriteRule ^ - [F,L]
|
||||
```
|
||||
|
||||
**Ожидаемое поведение:**
|
||||
- Запрос `http://localhost/test/stop.html` → **403 Forbidden** ("Доступ запрещен")
|
||||
- Страница не будет отображаться
|
||||
|
||||
### 3. Внутреннее перенаправление redirect.html на show.html
|
||||
|
||||
```apache
|
||||
RewriteRule ^redirect\.html$ show.html [L]
|
||||
```
|
||||
|
||||
**Ожидаемое поведение:**
|
||||
- Запрос `http://localhost/test/redirect.html` → **показывается содержимое show.html**
|
||||
- URL в браузере остается `/test/redirect.html` (не происходит перенаправления)
|
||||
|
||||
### 4. Перенаделение подкаталога subdir на index.html
|
||||
|
||||
```apache
|
||||
RewriteRule ^subdir/?$ /index.html [L]
|
||||
```
|
||||
|
||||
**Ожидаемое поведение:**
|
||||
- Запрос `http://localhost/test/subdir/` → **показывается index.html**
|
||||
- URL в браузере остается `/test/subdir/`
|
||||
|
||||
### 5. Дополнительные проверки
|
||||
|
||||
- Флаг `[L]` (Last) - останавливает обработку правил после выполнения
|
||||
- Флаг `[F]` (Forbidden) - возвращает ошибку 403 Forbidden
|
||||
- Флаг `[R=301]` - внешнее перенаправление с кодом статуса 301 Moved Permanently
|
||||
|
||||
## Порядок применения правил
|
||||
|
||||
В модуле nginx приоритет следующий:
|
||||
|
||||
1. **Правила из .htaccess** (высокий приоритет)
|
||||
2. **Правила из nginx конфигурации** (низкий приоритет)
|
||||
|
||||
При наличии флага `[END]` в правилах .htaccess, дальнейшая обработка всех правил останавливается.
|
||||
|
||||
## Поддержка флагоv mod_rewrite
|
||||
|
||||
| Флаг | Значение | Описание |
|
||||
|------|----------|----------|
|
||||
| `L` | Last | Остановить обработку остальных правил |
|
||||
| `F` | Forbidden | Заблокировать доступ (403) |
|
||||
| `R=301` | Redirect | Внешнее перенаправление с кодом 301 |
|
||||
| `END` | End | Полностью остановить обработку rewrite |
|
||||
|
||||
## Примеры запросов для тестирования
|
||||
|
||||
```bash
|
||||
# Перенаделение .xmx файла на index.html
|
||||
curl -I http://localhost/test/test.xmx
|
||||
# Ожидаемый ответ: HTTP/1.1 301 Moved Permanently
|
||||
# Location: /test/index.html
|
||||
|
||||
# Блокировка stop.html
|
||||
curl -I http://localhost/test/stop.html
|
||||
# Ожидаемый ответ: HTTP/1.1 403 Forbidden
|
||||
|
||||
# Внутреннее перенаправление redirect.html на show.html
|
||||
curl -I http://localhost/test/redirect.html
|
||||
# Ожидаемый ответ: HTTP/1.1 200 OK (content из show.html)
|
||||
|
||||
# Перенаделение subdir на index.html
|
||||
curl -I http://localhost/test/subdir/
|
||||
# Ожидаемый ответ: HTTP/1.1 200 OK (content из index.html)
|
||||
```
|
||||
|
||||
## Установка и запуск
|
||||
|
||||
Для запуска тестов потребуется:
|
||||
|
||||
1. Скомпилированный nginx с модулем `ngx_http_apache_rewrite_module`
|
||||
2. Конфигурация nginx, указывающая каталог `test/` как корень сайта
|
||||
3. Активированный флаг `.htaccess` через директиву `HtAccess on`
|
||||
|
||||
Пример конфигурации:
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
|
||||
location /test {
|
||||
root /workspaces/test1/test;
|
||||
|
||||
HtAccess on;
|
||||
HtAccessFile .htaccess;
|
||||
}
|
||||
}
|
||||
```
|
||||
311
cms/simple/SUMMARY.md
Normal file
311
cms/simple/SUMMARY.md
Normal file
@@ -0,0 +1,311 @@
|
||||
# Тесты Apache mod_rewrite для nginx - ПОЛНАЯ СУММАРИЯ
|
||||
|
||||
## 📁 Структура тестовых файлов
|
||||
|
||||
```
|
||||
/test1/test/
|
||||
├── .htaccess # Главное правило rewrite для корневого каталога
|
||||
├── index.html # Главная страница (назначение для redirect)
|
||||
├── show.html # Показывается вместо redirect.html
|
||||
├── stop.html # Блокируется (403 Forbidden)
|
||||
├── redirect.html # Внутреннее перенаправление на show.html
|
||||
├── test.xmx # Файл .xmx для тестирования расширения
|
||||
├── README.md # Документация по тестам
|
||||
├── SUMMARY.md # Эта страница - полная суммари
|
||||
└── subdir/ # Подкаталог с дополнительными тестами
|
||||
├── .htaccess # Правила rewrite для подкаталога
|
||||
├── file.html # Файл в подкаталоге
|
||||
└── other.xmx # Другой файл .xmx (блокируется)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔥 Ключевые правила .htaccess
|
||||
|
||||
### 1️⃣ Перенаделение файлов с расширением `.xmx` на index.html
|
||||
|
||||
**Правило:**
|
||||
```apache
|
||||
RewriteRule \.xmx$ /index.html [R=301,L]
|
||||
```
|
||||
|
||||
**Ожидаемое поведение:**
|
||||
| Запрос | Результат |
|
||||
|--------|-----------|
|
||||
| `http://localhost/test/test.xmx` | 301 Redirect → `/test/index.html` |
|
||||
| URL в браузере меняется на `/test/index.html` | ✅ Внешнее перенаправление |
|
||||
|
||||
**Логика:**
|
||||
- Все файлы заканчивающиеся на `.xmx` перенаправляются на главную страницу
|
||||
- Используется флаг `[R=301]` для внешнего HTTP redirect
|
||||
- Флаг `[L]` останавливает дальнейшую обработку правил
|
||||
|
||||
---
|
||||
|
||||
### 2️⃣ Блокировка доступа к stop.html (403 Forbidden)
|
||||
|
||||
**Правила:**
|
||||
```apache
|
||||
RewriteCond %{REQUEST_FILENAME} =stop.html
|
||||
RewriteRule ^ - [F,L]
|
||||
```
|
||||
|
||||
**Ожидаемое поведение:**
|
||||
| Запрос | Результат |
|
||||
|--------|-----------|
|
||||
| `http://localhost/test/stop.html` | 403 Forbidden (Доступ запрещен) |
|
||||
| Страница не отображается | ✅ Полная блокировка |
|
||||
|
||||
**Логика:**
|
||||
- `RewriteCond` проверяет что запрос именно к stop.html
|
||||
- `RewriteRule ^ - [F]` возвращает ошибку 403 Forbidden
|
||||
- Флаг `[L]` останавливает обработку
|
||||
|
||||
---
|
||||
|
||||
### 3️⃣ Внутреннее перенаправление redirect.html на show.html
|
||||
|
||||
**Правило:**
|
||||
```apache
|
||||
RewriteRule ^redirect\.html$ show.html [L]
|
||||
```
|
||||
|
||||
**Ожидаемое поведение:**
|
||||
| Запрос | Результат |
|
||||
|--------|-----------|
|
||||
| `http://localhost/test/redirect.html` | Показывается content из show.html |
|
||||
| URL в браузере остается `/test/redirect.html` | ✅ Internal rewrite |
|
||||
|
||||
**Логика:**
|
||||
- Правило скрывает redirect.html и показывает вместо него show.html
|
||||
- Происходит внутренняя переадресация без изменения URL
|
||||
- Флаг `[L]` останавливает обработку
|
||||
|
||||
---
|
||||
|
||||
### 4️⃣ Перенаделение подкаталога subdir на index.html
|
||||
|
||||
**Правило:**
|
||||
```apache
|
||||
RewriteRule ^subdir/?$ /index.html [L]
|
||||
```
|
||||
|
||||
**Ожидаемое поведение:**
|
||||
| Запрос | Результат |
|
||||
|--------|-----------|
|
||||
| `http://localhost/test/subdir/` | Показывается content из index.html |
|
||||
| URL в браузере остается `/test/subdir/` | ✅ Internal rewrite |
|
||||
|
||||
---
|
||||
|
||||
### 5️⃣ Дополнительные проверки (из main .htaccess)
|
||||
|
||||
**Правило:**
|
||||
```apache
|
||||
RewriteCond %{REQUEST_FILENAME} !test\.xmx$
|
||||
RewriteRule \.xmx$ - [F,L]
|
||||
```
|
||||
|
||||
**Ожидаемое поведение:**
|
||||
- Блокирует все `.xmx` файлы кроме test.xmx
|
||||
- **НО:** Работает ПОСЛЕ правила 1, поэтому test.xmx сначала редиректится на index.html
|
||||
|
||||
---
|
||||
|
||||
### 6️⃣ Перенаделение subdir с последующей блокировкой (конфликт)
|
||||
|
||||
**Порядок в .htaccess:**
|
||||
```apache
|
||||
# Сначала перенаправление
|
||||
RewriteRule ^subdir/?$ /index.html [L]
|
||||
|
||||
# Затем блокировка
|
||||
RewriteRule ^subdir/?$ - [F,L]
|
||||
```
|
||||
|
||||
**Ожидаемое поведение:**
|
||||
- subdir сначала переадресуется на index.html (правило 4)
|
||||
- Затем применяется правило блокировки → 403 Forbidden
|
||||
- **Итог:** subdir заблокирован с ошибкой 403
|
||||
|
||||
---
|
||||
|
||||
## 📁 Правила в подкаталоге `subdir/.htaccess`
|
||||
|
||||
### 1️⃣ Блокировка всех .xmx файлов в subdir (кроме test.xmx)
|
||||
|
||||
**Правило:**
|
||||
```apache
|
||||
RewriteRule \.xmx$ - [F,L]
|
||||
```
|
||||
|
||||
**Ожидаемое поведение:**
|
||||
| Запрос | Результат |
|
||||
|--------|-----------|
|
||||
| `http://localhost/test/subdir/other.xmx` | 403 Forbidden |
|
||||
| Файл заблокирован | ✅ |
|
||||
|
||||
---
|
||||
|
||||
### 2️⃣ Внутреннее перенаправление subdir/file.html на ../show.html
|
||||
|
||||
**Правило:**
|
||||
```apache
|
||||
RewriteRule ^file\.html$ ../show.html [L]
|
||||
```
|
||||
|
||||
**Ожидаемое поведение:**
|
||||
| Запрос | Результат |
|
||||
|--------|-----------|
|
||||
| `http://localhost/test/subdir/file.html` | Показывается content из show.html (в родительском каталоге) |
|
||||
| URL остается `/test/subdir/file.html` | ✅ Internal rewrite с относительным путем |
|
||||
|
||||
---
|
||||
|
||||
### 3️⃣ Блокировка всех файлов кроме .html в subdir
|
||||
|
||||
**Правило:**
|
||||
```apache
|
||||
RewriteCond %{REQUEST_FILENAME} !-ext=html
|
||||
RewriteRule ^ - [F,L]
|
||||
```
|
||||
|
||||
**Ожидаемое поведение:**
|
||||
| Запрос | Результат |
|
||||
|--------|-----------|
|
||||
| `http://localhost/test/subdir/file.txt` | 403 Forbidden |
|
||||
| Только .html файлы доступны | ✅ Фильтрация по расширению |
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Тестирование функционала mod_rewrite
|
||||
|
||||
### Поддерживаемые флаги:
|
||||
|
||||
| Флаг | Значение | Описание |
|
||||
|------|----------|----------|
|
||||
| `L` | Last | Остановить обработку остальных правил в .htaccess |
|
||||
| `F` | Forbidden | Возвращать ошибку 403 Forbidden |
|
||||
| `R=301` | Redirect | Внешнее перенаправление с кодом HTTP 301 Moved Permanently |
|
||||
| `END` | End | Остановить ВСЮ обработку rewrite (включая nginx config rules) |
|
||||
|
||||
### Поддерживаемые директивы:
|
||||
|
||||
| Директива | Описание |
|
||||
|-----------|----------|
|
||||
| `RewriteEngine on/off` | Включение/выключение mod_rewrite для .htaccess |
|
||||
| `RewriteRule pattern substitution [flags]` | Правило перенаправления |
|
||||
| `RewriteCond test string` | Условие для правила RewriteRule |
|
||||
|
||||
---
|
||||
|
||||
## 📊 Механизм приоритета
|
||||
|
||||
### Порядок применения правил:
|
||||
|
||||
```
|
||||
┌───────────────────────────────┐
|
||||
│ 1. Правила из .htaccess │ ← Higher Priority
|
||||
│ ├─ Read & parse │
|
||||
│ ├─ Convert to mod_rewrite │
|
||||
│ └─ Apply rules │
|
||||
│ │
|
||||
├───────────────────────────────┤
|
||||
│ Check END flag │
|
||||
│ if (ctx->end) STOP ALL │ ← Blocks nginx config!
|
||||
├───────────────────────────────┤
|
||||
│ 2. Правила из nginx config │ ← Lower Priority
|
||||
│ └─ Apply from server/loc │
|
||||
└───────────────────────────────┘
|
||||
```
|
||||
|
||||
**Ключевой момент:**
|
||||
- При наличии флага `[END]` в .htaccess, обработка ВСЕХ правил rewrite останавливается
|
||||
- Это позволяет полностью блокировать nginx config rules при необходимости
|
||||
|
||||
---
|
||||
|
||||
## 🧪 Примеры CURL запросов для тестирования
|
||||
|
||||
```bash
|
||||
# 1. Перенаправление .xmx на index.html (301)
|
||||
curl -I http://localhost/test/test.xmx
|
||||
# Ожидаемый: HTTP/1.1 301 Moved Permanently
|
||||
# Location: /test/index.html
|
||||
|
||||
# 2. Блокировка stop.html (403)
|
||||
curl -I http://localhost/test/stop.html
|
||||
# Ожидаемый: HTTP/1.1 403 Forbidden
|
||||
|
||||
# 3. Internal rewrite redirect.html → show.html (200 OK, content из show.html)
|
||||
curl -s http://localhost/test/redirect.html | grep h1
|
||||
# Ожидаемый: <h1>show.html</h1>
|
||||
|
||||
# 4. Internal rewrite subdir → index.html (200 OK)
|
||||
curl -I http://localhost/test/subdir/
|
||||
# Ожидаемый: HTTP/1.1 200 OK
|
||||
|
||||
# 5. Блокировка other.xmx в subdir (403)
|
||||
curl -I http://localhost/test/subdir/other.xmx
|
||||
# Ожидаемый: HTTP/1.1 403 Forbidden
|
||||
|
||||
# 6. Internal rewrite subdir/file.html → ../show.html (200 OK)
|
||||
curl -s http://localhost/test/subdir/file.html | grep h1
|
||||
# Ожидаемый: <h1>show.html</h1>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 Установка и запуск тестов
|
||||
|
||||
### Конфигурация nginx для запуска тестов:
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
|
||||
# Корневой сайт
|
||||
location / {
|
||||
root /workspaces/test1/nginx-1.25.3/html;
|
||||
}
|
||||
|
||||
# Тестовый каталог с .htaccess поддержкой
|
||||
location /test {
|
||||
alias /workspaces/test1/test/;
|
||||
|
||||
HtAccess on; # Включить чтение .htaccess файлов
|
||||
HtAccessFile .htaccess; # Файл конфигурации
|
||||
|
||||
# Для подкаталога subdir (авто-поиск .htaccess)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Команды запуска:
|
||||
|
||||
```bash
|
||||
# 1. Скомпилировать nginx с модулем mod_rewrite
|
||||
cd /workspaces/test1/nginx-1.25.3 && make modules
|
||||
|
||||
# 2. Запустить nginx
|
||||
./nginx -c /path/to/nginx.conf
|
||||
|
||||
# 3. Тестировать правила через curl или браузер
|
||||
curl http://localhost/test/redirect.html
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ Итог
|
||||
|
||||
Модуль `ngx_http_apache_rewrite_module` обеспечивает:
|
||||
|
||||
1. ✅ **Чтение .htaccess файлов** при каждом запросе с проверкой mtime
|
||||
2. ✅ **Кэширование директив** в памяти на время жизненного цикла запроса
|
||||
3. ✅ **Конвертацию Apache mod_rewrite правил** в формат nginx mod_rewrite
|
||||
4. ✅ **Приоритет .htaccess выше nginx config rules**
|
||||
5. ✅ **Флаг [END] для полной остановки обработки rewrite**
|
||||
6. ✅ **Поддержка флагоv L, F, R=301, END**
|
||||
|
||||
Все тестовые файлы готовы и содержат документацию по ожидаемому поведению! 🎉
|
||||
40
cms/simple/index.html
Normal file
40
cms/simple/index.html
Normal file
@@ -0,0 +1,40 @@
|
||||
<!doctype html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Index - Тест mod_rewrite для nginx</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>index.html</h1>
|
||||
<p>Вы попали на index.html.</p>
|
||||
<p>
|
||||
<strong
|
||||
>Это страница назначения при перенаправлении файлов с
|
||||
расширением xmx.</strong
|
||||
>
|
||||
</p>
|
||||
|
||||
<hr />
|
||||
<ul>
|
||||
<li><a href="show.html">Показать (show.html)</a></li>
|
||||
<li><a href="stop.html">Stop - заблокированная страница</a></li>
|
||||
<li>
|
||||
<a href="redirect.html">Redirect - редирект на show.html</a>
|
||||
</li>
|
||||
<li><a href="subdir/">Подкаталог</a></li>
|
||||
</ul>
|
||||
|
||||
<hr />
|
||||
<p><em>Тесты mod_rewrite для nginx:</em></p>
|
||||
<ol>
|
||||
<li>
|
||||
Запросите <code>.xmx</code> файл → перенаправление на index.html
|
||||
</li>
|
||||
<li>
|
||||
Запросите <code>stop.html</code> → 403 Forbidden (доступ
|
||||
запрещен)
|
||||
</li>
|
||||
<li>Запросите <code>redirect.html</code> → покажет show.html</li>
|
||||
</ol>
|
||||
</body>
|
||||
</html>
|
||||
159
cms/simple/nginx.conf
Normal file
159
cms/simple/nginx.conf
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
26
cms/simple/redirect.html
Normal file
26
cms/simple/redirect.html
Normal file
@@ -0,0 +1,26 @@
|
||||
<!doctype html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Redirect - Тест mod_rewrite для nginx</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>redirect.html</h1>
|
||||
<p>Вы попали на redirect.html.</p>
|
||||
|
||||
<hr />
|
||||
<ul>
|
||||
<li><a href="index.html">На главную (index.html)</a></li>
|
||||
<li><a href="show.html">Показать (show.html)</a></li>
|
||||
<li><a href="stop.html">Stop - заблокированная</a></li>
|
||||
</ul>
|
||||
|
||||
<hr />
|
||||
<p>
|
||||
<em><strong style="color: green;">Эта страница должна быть скрыта!</strong></em>
|
||||
</p>
|
||||
<p>
|
||||
При обращении к redirect.html, mod_rewrite должен показать show.html вместо этой страницы.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
26
cms/simple/show.html
Normal file
26
cms/simple/show.html
Normal file
@@ -0,0 +1,26 @@
|
||||
<!doctype html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Show - Тест mod_rewrite для nginx</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>show.html</h1>
|
||||
<p>Вы попали на show.html.</p>
|
||||
|
||||
<hr />
|
||||
<ul>
|
||||
<li><a href="index.html">На главную (index.html)</a></li>
|
||||
<li><a href="redirect.html">Redirect - перенаправляет сюда</a></li>
|
||||
<li><a href="stop.html">Stop - заблокированная</a></li>
|
||||
</ul>
|
||||
|
||||
<hr />
|
||||
<p>
|
||||
<em><strong style="color: green;">Показано вместо redirect.html!</strong></em>
|
||||
</p>
|
||||
<p>
|
||||
Это страница-назначение для правила <code>RewriteRule ^redirect\.html$ show.html [L]</code>.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
10
cms/simple/site1.conf
Normal file
10
cms/simple/site1.conf
Normal file
@@ -0,0 +1,10 @@
|
||||
<VirtualHost 192.168.3.96:80>
|
||||
DocumentRoot "/sites/site1"
|
||||
ServerName example1.com
|
||||
|
||||
<Directory /sites/site1>
|
||||
Options +Indexes +FollowSymLinks
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
</VirtualHost>
|
||||
0
cms/simple/somedir/.gitkeep
Normal file
0
cms/simple/somedir/.gitkeep
Normal file
23
cms/simple/stop.html
Normal file
23
cms/simple/stop.html
Normal file
@@ -0,0 +1,23 @@
|
||||
<!doctype html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Stop - Тест mod_rewrite для nginx</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>stop.html</h1>
|
||||
<p>Вы попали на stop.html.</p>
|
||||
|
||||
<hr />
|
||||
<ul>
|
||||
<li><a href="index.html">На главную (index.html)</a></li>
|
||||
<li><a href="show.html">Показать (show.html)</a></li>
|
||||
<li><a href="redirect.html">Redirect</a></li>
|
||||
</ul>
|
||||
|
||||
<hr />
|
||||
<p>
|
||||
<em><strong style="color: red;">Эта страница должна быть заблокирована!</strong></em>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
0
cms/simple/subdir/.gitkeep
Normal file
0
cms/simple/subdir/.gitkeep
Normal file
30
cms/simple/subdir/.htaccess
Normal file
30
cms/simple/subdir/.htaccess
Normal file
@@ -0,0 +1,30 @@
|
||||
# ============================================================
|
||||
# .htaccess в подкаталоге subdir - Тесты mod_rewrite для nginx
|
||||
# ============================================================
|
||||
# Этот файл проверяет работу правил для конкретного location (подкаталога)
|
||||
|
||||
RewriteEngine On
|
||||
|
||||
# Правило 1: Все запросы к файлам с расширением xmx в этом подкаталоге
|
||||
# должны блокироваться с ошибкой 403 Forbidden
|
||||
|
||||
RewriteRule \.xmx$ - [F,L]
|
||||
|
||||
# Тестирование:
|
||||
# - http://localhost/test/subdir/other.xmx → 403 Forbidden
|
||||
|
||||
# Правило 2: Внутреннее перенаделение subdir/file.html на ../show.html
|
||||
# (переход из подкаталога в родительский)
|
||||
|
||||
RewriteRule ^file\.html$ ../show.html [L]
|
||||
|
||||
# Тестирование:
|
||||
# - http://localhost/test/subdir/file.html → показывает содержимое show.html
|
||||
|
||||
# Правило 3: Блокировка доступа ко всем файлам кроме .html
|
||||
|
||||
RewriteCond %{REQUEST_URI} !\.html$
|
||||
RewriteRule ^ - [F,L]
|
||||
|
||||
# Тестирование:
|
||||
# - http://localhost/test/subdir/file.txt → 403 Forbidden
|
||||
26
cms/simple/subdir/file.html
Normal file
26
cms/simple/subdir/file.html
Normal file
@@ -0,0 +1,26 @@
|
||||
<!doctype html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>File - Тест mod_rewrite для nginx</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>file.html</h1>
|
||||
<p>Вы попали на file.html в подкаталоге subdir.</p>
|
||||
|
||||
<hr />
|
||||
<ul>
|
||||
<li><a href="../index.html">На главную (index.html)</a></li>
|
||||
<li><a href="../show.html">Показать (show.html)</a></li>
|
||||
<li><a href="../redirect.html">Redirect</a></li>
|
||||
</ul>
|
||||
|
||||
<hr />
|
||||
<p>
|
||||
<em><strong style="color: green;">Это страница из подкаталога!</strong></em>
|
||||
</p>
|
||||
<p>
|
||||
При обращении к <code>/test/subdir/file.html</code>, mod_rewrite должен показать содержимое <code>../show.html</code>.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
27
cms/simple/subdir/other.xmx
Normal file
27
cms/simple/subdir/other.xmx
Normal file
@@ -0,0 +1,27 @@
|
||||
<!doctype html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Other XMX - Тест mod_rewrite для nginx</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>other.xmx</h1>
|
||||
<p>Вы попытались получить этот файл.</p>
|
||||
|
||||
<hr />
|
||||
<ul>
|
||||
<li><a href="../index.html">На главную (index.html)</a></li>
|
||||
<li><a href="../show.html">Показать (show.html)</a></li>
|
||||
<li><a href="file.html">File.html</a></li>
|
||||
</ul>
|
||||
|
||||
<hr />
|
||||
<p>
|
||||
<em><strong style="color: red;">Этот файл должен быть заблокирован!</strong></em>
|
||||
</p>
|
||||
<p>
|
||||
При обращении к <code>/test/subdir/other.xmx</code>, mod_rewrite должен вернуть
|
||||
<code style="background-color: #f00; color: white;">403 Forbidden</code>.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
26
cms/simple/test.xmx
Normal file
26
cms/simple/test.xmx
Normal file
@@ -0,0 +1,26 @@
|
||||
# Test File - test.xmx
|
||||
|
||||
This is a test file used to verify the mod_rewrite rule that redirects all files ending with .xmx extension to index.html.
|
||||
|
||||
## How it works:
|
||||
|
||||
When you request any `.xmx` file in this directory, the following rule from `.htaccess` will be applied:
|
||||
|
||||
```
|
||||
RewriteRule \.xmx$ /index.html [R=301,L]
|
||||
```
|
||||
|
||||
This redirects your browser to `/index.html`.
|
||||
|
||||
## Test URLs:
|
||||
|
||||
- `http://localhost/test/test.xmx` → redirects to `/test/index.html`
|
||||
- `http://localhost/test/subdir/other.xmx` → redirects to `/test/index.html`
|
||||
- `http://localhost/test/deep/path/file.xmx` → redirects to `/test/index.html`
|
||||
|
||||
## Additional Test Files:
|
||||
|
||||
See other test pages in this directory:
|
||||
- index.html - main page
|
||||
- show.html - shown instead of redirect.html via internal rewrite
|
||||
- stop.html - should be blocked (403 Forbidden) by mod_rewrite rules
|
||||
14
cms/wordpress-multy/.htaccess
Normal file
14
cms/wordpress-multy/.htaccess
Normal file
@@ -0,0 +1,14 @@
|
||||
RewriteEngine On
|
||||
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
|
||||
RewriteBase /
|
||||
RewriteRule ^index\.php$ - [L]
|
||||
|
||||
# add a trailing slash to /wp-admin
|
||||
RewriteRule ^([_0-9a-zA-Z-]+/)?wp-admin$ $1wp-admin/ [R=301,L]
|
||||
|
||||
RewriteCond %{REQUEST_FILENAME} -f [OR]
|
||||
RewriteCond %{REQUEST_FILENAME} -d
|
||||
RewriteRule ^ - [L]
|
||||
RewriteRule ^([_0-9a-zA-Z-]+/)?(wp-(content|admin|includes).*) $2 [L]
|
||||
RewriteRule ^([_0-9a-zA-Z-]+/)?(.*\.php)$ $2 [L]
|
||||
RewriteRule . index.php [L]
|
||||
152
cms/wordpress-multy/README.md
Normal file
152
cms/wordpress-multy/README.md
Normal file
@@ -0,0 +1,152 @@
|
||||
# WordPress Multisite Test Structure - Documentation
|
||||
|
||||
## Directory Layout Overview
|
||||
|
||||
```
|
||||
/test1/cms/wordpress-multy/
|
||||
├── index.php - WordPress entry point (routes non-existing files)
|
||||
│ - Returns: "WordPress Multisite Content Route" page
|
||||
├── wp-admin/ - Admin directory tests
|
||||
│ └── (empty directory for trailing slash redirect test)
|
||||
├── wp-admin/admin.php - Test file for wp-admin routing rule
|
||||
├── wp-content/ - WordPress content directory tests
|
||||
│ └── themes/ - Theme subdirectory
|
||||
├── wp-includes/ - Core files directory tests
|
||||
│ └── js/ - JavaScript subdirectory
|
||||
│ └── (empty for testing)
|
||||
├── style.css - Existing CSS file (tests RewriteRule !-f condition)
|
||||
├── script.js - Existing JS file (tests RewriteRule !-f condition)
|
||||
├── test-wp-multi-rewriterules.sh - Bash script to test all rules using curl
|
||||
└── README.md - This documentation file
|
||||
```
|
||||
|
||||
## Apache Rules Explained - WordPress Multisite
|
||||
|
||||
### 1. HTTP Authorization Header Passing
|
||||
|
||||
```apache
|
||||
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
|
||||
```
|
||||
|
||||
**Что делает:** Копирует HTTP Authorization header в переменную HTTP_AUTHORIZATION для PHP
|
||||
|
||||
### 2. RewriteBase Configuration
|
||||
|
||||
```apache
|
||||
RewriteBase /
|
||||
```
|
||||
|
||||
**Что делает:** Устанавливает базовый путь к корню сайта
|
||||
|
||||
### 3. Direct index.php Access Protection
|
||||
|
||||
```apache
|
||||
RewriteRule ^index\.php$ - [L]
|
||||
```
|
||||
|
||||
**Что делает:** Предотвращает зацикливание маршрутизации (не переписывает прямой запрос на index.php)
|
||||
|
||||
### 4. wp-admin Trailing Slash Redirect
|
||||
|
||||
```apache
|
||||
# add a trailing slash to /wp-admin
|
||||
RewriteRule ^([_0-9a-zA-Z-]+/)?wp-admin$ $1wp-admin/ [R=301,L]
|
||||
```
|
||||
|
||||
**Что делает:** Добавляет слэш в конце к URL `/wp-admin`, редирект 301 (Moved Permanently)
|
||||
|
||||
**Зачем нужно:** WordPress multisite требует trailing slash для admin panel, обеспечивает корректную маршрутизацию
|
||||
|
||||
**Подхват поддомена:** `[_0-9a-zA-Z-]+/?` позволяет обработку поддоменов в multisite setup:
|
||||
- `/wp-admin` → редирект на `/wp-admin/` (301)
|
||||
- `site.subdomain.com/wp-admin` → редирект на `site.subdomain.com/wp-admin/`
|
||||
|
||||
### 5. Existing Files/Directories Check (Skip Processing)
|
||||
|
||||
```apache
|
||||
RewriteCond %{REQUEST_FILENAME} -f [OR]
|
||||
RewriteCond %{REQUEST_FILENAME} -d
|
||||
RewriteRule ^ - [L]
|
||||
```
|
||||
|
||||
**Что делает:** Если файл ИЛИ директория СУЩЕСТВУЕТ - останавливаем обработку правил, пропускаем запрос напрямую
|
||||
|
||||
**Зачем нужно:** Предотвращает маршрутизацию на index.php для существующих статических файлов (CSS, JS, изображения)
|
||||
|
||||
### 6. WordPress Multisite Core Routing Rules
|
||||
|
||||
```apache
|
||||
RewriteRule ^([_0-9a-zA-Z-]+/)?(wp-(content|admin|includes).*) $2 [L]
|
||||
```
|
||||
|
||||
**Что делает:** Для multisite setups - маршрутизирует wp-content, wp-admin, wp-includes напрямую (без index.php)
|
||||
|
||||
**Подхват поддоменов:** `[_0-9a-zA-Z-]+/?` позволяет обработку поддоменов в multisite:
|
||||
- `/wp-content/uploads/image.jpg` → прямой доступ к файлу
|
||||
- `site.subdomain.com/wp-admin/admin.php` → прямая маршрутизация к admin.php
|
||||
|
||||
### 7. All PHP Files Direct Access
|
||||
|
||||
```apache
|
||||
RewriteRule ^([_0-9a-zA-Z-]+/)?(.*\.php)$ $2 [L]
|
||||
```
|
||||
|
||||
**Что делает:** Любой PHP файл отдаётся напрямую, без маршрутизации через index.php
|
||||
|
||||
**Подхват поддоменов:** `[_0-9a-zA-Z-]+/?` позволяет обработку поддоменов в multisite:
|
||||
- `/wp-login.php` → прямой доступ к файлу
|
||||
- `site.subdomain.com/wp-signup.php` → прямая маршрутизация к signup.php
|
||||
|
||||
### 8. Final WordPress Routing Rule
|
||||
|
||||
```apache
|
||||
RewriteRule . index.php [L]
|
||||
```
|
||||
|
||||
**Что делает:** Все оставшиеся запросы маршрутизируются через index.php (основной URL routing)
|
||||
|
||||
## 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
|
||||
|
||||
### Subdomain vs Subdirectory Setup
|
||||
|
||||
WordPress Multisite может быть configured в двух режимах:
|
||||
- **Subdomain mode**: sites поддоменами (site1.example.com, site2.example.com)
|
||||
- **Subdirectory mode**: сайты поддиректориями (/site1/, /site2/)
|
||||
|
||||
Правило `[_0-9a-zA-Z-]+/?` обрабатывает оба случая:
|
||||
- Для subdomain: `[subdomain/]?` = optional prefix
|
||||
- Для subdirectory: `[directory/]?` = optional prefix
|
||||
|
||||
### Test Examples
|
||||
|
||||
| URL | Правило | Ожидаемый результат |
|
||||
|-----|---------|---------------------|
|
||||
| `http://test.my.brp/wp-admin` | trailing slash redirect | 301 → `/wp-admin/` ✓ |
|
||||
| `http://test.my.brp/wp-admin/` | direct access (directory exists) | 200 OK ✓ |
|
||||
| `http://test.my.brp/style.css` | existing file (!-f passes) | 200 OK ✓ |
|
||||
| `http://test.my.brp/script.js` | existing file (!-f passes) | 200 OK ✓ |
|
||||
| `http://test.my.brp/wp-admin/admin.php` | multisite wp-admin rule | 200 OK ✓ |
|
||||
| `http://test.my.brp/wp-content/uploads/test.jpg` | multisite wp-content rule | 200 OK ✓ |
|
||||
| `http://test.my.brp/nonexistent-page/` | final index.php routing | 200 OK (content from index.php) ✓ |
|
||||
|
||||
## Run Tests
|
||||
|
||||
Execute the test script to verify all rules:
|
||||
```bash
|
||||
cd /home/alexey/projects/workspace-zed/test1/cms/wordpress-multy
|
||||
./test-wp-multi-rewriterules.sh
|
||||
```
|
||||
|
||||
Expected results for WordPress Multisite tests (all should be **PASS ✓**):
|
||||
- wp-admin trailing slash redirect: HTTP 301 ✓
|
||||
- wp-admin directory access: HTTP 200 ✓
|
||||
- Existing file (CSS/JS) access: HTTP 200 ✓
|
||||
- Existing directory access: HTTP 200 ✓
|
||||
- WordPress multisite routing (wp-content, admin): HTTP 200 ✓
|
||||
- Final index.php routing: HTTP 200 + content "WordPress Multisite Content Route" ✓
|
||||
6
cms/wordpress-multy/custom-script.php
Normal file
6
cms/wordpress-multy/custom-script.php
Normal file
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
// WordPress Multisite Test PHP File
|
||||
// This file is tested via RewriteRule ^([_0-9a-zA-Z-]+/)?(.*\.php)$ $2 [L]
|
||||
// Any .php file access rule
|
||||
|
||||
echo "WordPress Multisite custom script - direct PHP file access";
|
||||
15
cms/wordpress-multy/index.php
Normal file
15
cms/wordpress-multy/index.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
/**
|
||||
* WordPress Multisite - index.php (Test version)
|
||||
* This file handles routing for non-existing files/directories in multisite setup
|
||||
*/
|
||||
|
||||
// Simulated WordPress Multisite response
|
||||
echo "<html><head><title>WordPress Multisite Test Site</title></head><body>";
|
||||
echo "<h1>WordPress Multisite Content Route</h1>";
|
||||
echo "<p>This page is served by index.php via RewriteRule.</p>";
|
||||
echo "<div class='wp-multisite-config'>WordPress Multisite Configuration Loaded</div>";
|
||||
echo "</body></html>";
|
||||
|
||||
// Exit
|
||||
exit;
|
||||
4
cms/wordpress-multy/script.js
Normal file
4
cms/wordpress-multy/script.js
Normal file
@@ -0,0 +1,4 @@
|
||||
// WordPress Multisite Test JS File
|
||||
// This file is tested via RewriteCond !-f (existing files skip routing)
|
||||
|
||||
console.log('WordPress Multisite CSS/JS test file');
|
||||
8
cms/wordpress-multy/style.css
Normal file
8
cms/wordpress-multy/style.css
Normal file
@@ -0,0 +1,8 @@
|
||||
/* WordPress Multisite Test CSS File */
|
||||
/* This file is tested via RewriteCond !-f (existing files skip routing) */
|
||||
|
||||
body {
|
||||
font-family: 'Arial', sans-serif;
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
}
|
||||
171
cms/wordpress-multy/test-wp-multi-rewriterules.sh
Executable file
171
cms/wordpress-multy/test-wp-multi-rewriterules.sh
Executable file
@@ -0,0 +1,171 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ============================================
|
||||
# WordPress Multisite .htaccess Rules Test Script
|
||||
# ============================================
|
||||
# This script tests each rule from cms/wordpress-multy/.htaccess
|
||||
# Assumption: Site root is mapped to /home/alexey/projects/workspace-zed/test1/cms/wordpress-multy
|
||||
# Domain: test.my.brp
|
||||
# ============================================
|
||||
|
||||
BASE_URL="http://test.my.brp"
|
||||
|
||||
echo "=============================================="
|
||||
echo "WordPress Multisite .htaccess Rules Test Suite"
|
||||
echo "=============================================="
|
||||
echo ""
|
||||
|
||||
# Function to test a rule and report result (status only)
|
||||
test_rule() {
|
||||
local description="$1"
|
||||
local url="$2"
|
||||
local expected_status="$3" # e.g., 403, 404, 200, 301
|
||||
|
||||
echo "--- Test: $description ---"
|
||||
response=$(curl -s -o /dev/null -w "%{http_code}" "$url")
|
||||
|
||||
if [ "$response" = "$expected_status" ]; then
|
||||
echo "✓ PASS (HTTP $response)"
|
||||
else
|
||||
echo "✗ FAIL (Expected: HTTP $expected_status, Got: HTTP $response)"
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Function to test a rule and verify content contains expected string
|
||||
test_rule_content() {
|
||||
local description="$1"
|
||||
local url="$2"
|
||||
local expected_status="$3" # e.g., 403, 404, 200, 301
|
||||
local expected_content="$4" # Expected substring in response body
|
||||
|
||||
echo "--- Test: $description ---"
|
||||
response=$(curl -s "$url")
|
||||
http_code=$(curl -s -o /dev/null -w "%{http_code}" "$url")
|
||||
|
||||
# Check status code
|
||||
if [ "$http_code" != "$expected_status" ]; then
|
||||
echo "✗ FAIL (Status: HTTP $http_code, Expected: HTTP $expected_status)"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check content contains expected substring
|
||||
if [[ "$response" == *"$expected_content"* ]]; then
|
||||
echo "✓ PASS (HTTP $http_code, Content matches '$expected_content')"
|
||||
else
|
||||
echo "✗ FAIL (Content missing: '$expected_content') - Response:"
|
||||
echo "$response" | head -5
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
echo "=============================================="
|
||||
echo "1. HTTP Authorization Header Handling"
|
||||
echo "=============================================="
|
||||
# Test that Authorization header is properly handled by WordPress Multisite API
|
||||
test_rule_content "WordPress multisite handles Authorization header (API request)" \
|
||||
"$BASE_URL/wp-json/wp/v2/posts?Authorization=Bearer secret_token_123" \
|
||||
"200" \
|
||||
"WordPress Multisite Configuration Loaded"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "2. RewriteBase Root Configuration"
|
||||
echo "=============================================="
|
||||
# Test root directory access with proper base path routing
|
||||
test_rule_content "Root directory access (base path /)" \
|
||||
"$BASE_URL/" \
|
||||
"200" \
|
||||
"WordPress Multisite Content Route"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "3. Direct index.php Access Protection"
|
||||
echo "=============================================="
|
||||
# Test direct access to index.php - should NOT be rewritten (prevents infinite loop)
|
||||
test_rule_content "Direct index.php access (not rewritten)" \
|
||||
"$BASE_URL/index.php" \
|
||||
"200" \
|
||||
"WordPress Multisite Configuration Loaded"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "4. wp-admin Trailing Slash Redirect Rule"
|
||||
echo "=============================================="
|
||||
# Test trailing slash redirect for /wp-admin (301 Moved Permanently)
|
||||
test_rule_content "wp-admin trailing slash redirect (301)" \
|
||||
"$BASE_URL/wp-admin" \
|
||||
"301"
|
||||
|
||||
# Test that /wp-admin/ with trailing slash works correctly (existing directory)
|
||||
test_rule_content "wp-admin/ with trailing slash (exists as directory)" \
|
||||
"$BASE_URL/wp-admin/" \
|
||||
"200" \
|
||||
"WordPress Multisite Content Route"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "5. Existing Files/Directories Check Rule"
|
||||
echo "=============================================="
|
||||
# Test existing CSS file access (!-f condition passes) - should return 200 OK without routing to index.php
|
||||
test_rule "Existing CSS file access (style.css)" \
|
||||
"$BASE_URL/style.css" \
|
||||
"200"
|
||||
|
||||
# Test existing JS file access (!-f condition passes) - should return 200 OK without routing to index.php
|
||||
test_rule "Existing JS file access (script.js)" \
|
||||
"$BASE_URL/script.js" \
|
||||
"200"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "6. WordPress Multisite Core Routing Rules"
|
||||
echo "=============================================="
|
||||
# Test wp-admin specific routing rule (not through index.php)
|
||||
test_rule_content "wp-admin/admin.php multisite routing" \
|
||||
"$BASE_URL/wp-admin/admin.php" \
|
||||
"200" \
|
||||
"WordPress Multisite Content Route"
|
||||
|
||||
# Test wp-content specific routing rule (for uploads, themes etc.)
|
||||
test_rule_content "wp-content/uploads/test.jpg multisite routing" \
|
||||
"$BASE_URL/wp-content/uploads/test.jpg" \
|
||||
"200" \
|
||||
"WordPress Multisite Content Route"
|
||||
|
||||
# Test wp-includes specific routing rule (core files)
|
||||
test_rule_content "wp-includes/js/jquery.js multisite routing" \
|
||||
"$BASE_URL/wp-includes/js/jquery.js" \
|
||||
"200" \
|
||||
"WordPress Multisite Content Route"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "7. All PHP Files Direct Access Rule"
|
||||
echo "=============================================="
|
||||
# Test any .php file access (not routed through index.php) - custom script returns specific content
|
||||
test_rule_content "Any PHP file access (.php rule)" \
|
||||
"$BASE_URL/custom-script.php" \
|
||||
"200" \
|
||||
"WordPress Multisite custom script - direct PHP file access"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "8. Final WordPress Routing Rule"
|
||||
echo "=============================================="
|
||||
# Test non-existing file routing through index.php (main routing)
|
||||
test_rule_content "Non-existing page routing (routes to index.php)" \
|
||||
"$BASE_URL/nonexistent-page/" \
|
||||
"200" \
|
||||
"WordPress Multisite Content Route"
|
||||
|
||||
# Test non-existing directory routing through index.php (routes to index.php)
|
||||
test_rule_content "Non-existing directory routing (routes to index.php)" \
|
||||
"$BASE_URL/nonexistent-directory/" \
|
||||
"200" \
|
||||
"WordPress Multisite Content Route"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "Test Suite Complete"
|
||||
echo "=============================================="
|
||||
2
cms/wordpress-multy/wp-admin/admin.php
Normal file
2
cms/wordpress-multy/wp-admin/admin.php
Normal file
@@ -0,0 +1,2 @@
|
||||
# WordPress Multisite admin.php test file
|
||||
# This file is tested via RewriteRule ^([_0-9a-zA-Z-]+/)?(wp-(content|admin|includes).*) $2 [L]
|
||||
0
cms/wordpress-multy/wp-content/themes/.gitkeep
Normal file
0
cms/wordpress-multy/wp-content/themes/.gitkeep
Normal file
2
cms/wordpress-multy/wp-content/uploads/test.jpg
Normal file
2
cms/wordpress-multy/wp-content/uploads/test.jpg
Normal file
@@ -0,0 +1,2 @@
|
||||
# WordPress Multisite test.jpg file for wp-content routing rule
|
||||
# This file is tested via RewriteRule ^([_0-9a-zA-Z-]+/)?(wp-(content|admin|includes).*) $2 [L]
|
||||
2
cms/wordpress-multy/wp-includes/js/jquery.js
vendored
Normal file
2
cms/wordpress-multy/wp-includes/js/jquery.js
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# WordPress Multisite jquery.js file for wp-includes routing rule
|
||||
# This file is tested via RewriteRule ^([_0-9a-zA-Z-]+/)?(wp-(content|admin|includes).*) $2 [L]
|
||||
159
cms/wordpress/nginx.conf
Normal file
159
cms/wordpress/nginx.conf
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
16
cms/wordpress/site3.conf
Normal file
16
cms/wordpress/site3.conf
Normal file
@@ -0,0 +1,16 @@
|
||||
<VirtualHost *:80>
|
||||
DocumentRoot "/sites/site3"
|
||||
ServerName example3.com
|
||||
|
||||
DirectoryIndex index.php
|
||||
|
||||
<Directory /sites/site3/wordpress>
|
||||
Options +Indexes +FollowSymLinks
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
<FilesMatch \.php$>
|
||||
SetHandler "proxy:unix:/run/php-fpm/www.sock|fcgi://localhost"
|
||||
</FilesMatch>
|
||||
|
||||
</VirtualHost>
|
||||
140
cms/wordpress/test-wp-rewriterules.sh
Executable file
140
cms/wordpress/test-wp-rewriterules.sh
Executable file
@@ -0,0 +1,140 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ============================================
|
||||
# WordPress .htaccess Rules Test Script
|
||||
# ============================================
|
||||
# This script tests each rule from cms/wordpress/.htaccess
|
||||
# Assumption: Site root is mapped to /home/alexey/projects/workspace-zed/test1/cms/wordpress
|
||||
# Domain: test.my.brp
|
||||
# ============================================
|
||||
|
||||
BASE_URL="http://test.my.brp"
|
||||
|
||||
echo "=============================================="
|
||||
echo "WordPress .htaccess Rules Test Suite"
|
||||
echo "=============================================="
|
||||
echo ""
|
||||
|
||||
# Function to test a rule and report result (status only)
|
||||
test_rule() {
|
||||
local description="$1"
|
||||
local url="$2"
|
||||
local expected_status="$3" # e.g., 403, 404, 200
|
||||
|
||||
echo "--- Test: $description ---"
|
||||
response=$(curl -s -o /dev/null -w "%{http_code}" "$url")
|
||||
|
||||
if [ "$response" = "$expected_status" ]; then
|
||||
echo "✓ PASS (HTTP $response)"
|
||||
else
|
||||
echo "✗ FAIL (Expected: HTTP $expected_status, Got: HTTP $response)"
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Function to test a rule and verify content contains expected string
|
||||
test_rule_content() {
|
||||
local description="$1"
|
||||
local url="$2"
|
||||
local headers="$3" # Optional: additional curl -H header flags (can be empty)
|
||||
local expected_status="$4" # e.g., 403, 404, 200
|
||||
local expected_content="$5" # Expected substring in response body
|
||||
|
||||
echo "--- Test: $description ---"
|
||||
|
||||
if [ -n "$headers" ]; then
|
||||
response=$(curl -s -H "$headers" "$url")
|
||||
http_code=$(curl -s -H "$headers" -o /dev/null -w "%{http_code}" "$url")
|
||||
else
|
||||
response=$(curl -s "$url")
|
||||
http_code=$(curl -s -o /dev/null -w "%{http_code}" "$url")
|
||||
fi
|
||||
|
||||
# Check status code
|
||||
if [ "$http_code" != "$expected_status" ]; then
|
||||
echo "✗ FAIL (Status: HTTP $http_code, Expected: HTTP $expected_status)"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check content contains expected substring
|
||||
if [[ "$response" == *"$expected_content"* ]]; then
|
||||
echo "✓ PASS (HTTP $http_code, Content matches '$expected_content')"
|
||||
else
|
||||
echo "✗ FAIL (Content missing: '$expected_content') - Response:"
|
||||
echo "$response" | head -5
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
echo "=============================================="
|
||||
echo "1. HTTP_AUTHORIZATION Header Rewrite Rule"
|
||||
echo "=============================================="
|
||||
# Test that Authorization header is properly handled by WordPress REST API mock
|
||||
# This rule copies HTTP Authorization header to HTTP_AUTHORIZATION env var via mod_rewrite [E=...]
|
||||
test_rule_content "WordPress REST API: Authorization header passed through mod_rewrite" \
|
||||
"$BASE_URL/wordpress/wp-json/wp/v2/posts" \
|
||||
"Authorization: secret_token_123" \
|
||||
"200" \
|
||||
"\"status\": \"success\""
|
||||
|
||||
# Additional test: verify token verification in response
|
||||
test_rule_content "WordPress REST API: Token verified correctly" \
|
||||
"$BASE_URL/wordpress/wp-json/wp/v2/posts" \
|
||||
"" \
|
||||
"401" \
|
||||
"\"message\": \"unauth\""
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "2. RewriteBase Configuration"
|
||||
echo "=============================================="
|
||||
# RewriteBase / sets base path to root - verifies routing works correctly
|
||||
test_rule_content "Base path routing (root directory)" \
|
||||
"$BASE_URL/wordpress/" \
|
||||
"" \
|
||||
"200" \
|
||||
"WordPress Content Route"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "3. Direct index.php Access Protection"
|
||||
echo "=============================================="
|
||||
# RewriteRule ^index\.php$ - [L] - direct access to index.php should return 200 OK
|
||||
test_rule_content "Direct index.php access (not rewritten)" \
|
||||
"$BASE_URL/wordpress/index.php" \
|
||||
"" \
|
||||
"200" \
|
||||
"WordPress Content Route"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "4. WordPress Core Routing Rules"
|
||||
echo "=============================================="
|
||||
# Test non-existing file routes through index.php (!-f condition)
|
||||
test_rule_content "Non-existing file routing (routes to index.php)" \
|
||||
"$BASE_URL/wordpress/nonexistent-page/" \
|
||||
"" \
|
||||
"200" \
|
||||
"WordPress Content Route"
|
||||
|
||||
# Test existing file access - should return 200 OK (!-f passes for existing files)
|
||||
test_rule "Existing file access (existing.jpg)" \
|
||||
"$BASE_URL/wordpress/existing.jpg" \
|
||||
"200"
|
||||
|
||||
# Test non-existing directory routing - should route to index.php (!-d condition)
|
||||
test_rule_content "Non-existing directory routing (routes to index.php)" \
|
||||
"$BASE_URL/wordpress/nonexistent-dir/" \
|
||||
"" \
|
||||
"200" \
|
||||
"WordPress Content Route"
|
||||
|
||||
# Test existing directory access - should return 200 OK (!-d passes for existing dirs)
|
||||
test_rule "Existing directory access (somedir/)" \
|
||||
"$BASE_URL/wordpress/somedir/" \
|
||||
"200"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "Test Suite Complete"
|
||||
echo "=============================================="
|
||||
8
cms/wordpress/wordpress/.htaccess
Normal file
8
cms/wordpress/wordpress/.htaccess
Normal file
@@ -0,0 +1,8 @@
|
||||
RewriteEngine On
|
||||
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
|
||||
RewriteBase /wordpress
|
||||
RewriteRule ^wp-json/(.*)$ wp_api.php?api=$1 [L,QSA]
|
||||
RewriteRule ^index\.php$ - [L]
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule . index.php [L]
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user