Setting up the request shield
This guide installs the request shield (exp_request_shield) on an existing Exponential site, gives the site its
own rules, checks and tests them, explains when to monitor and when to enforce, how to pause the shield, how to
update its library and how to remove it again. It is written for administrators and hosting staff with shell access
to the installation.
Guides · Feature reference: The request shield · Upgrade notes: Upgrading to 6.0.15: the request shield · In the help: Request shield: start here
In short
- Install:
composer require se7enxweb/exp_request_shield,ActiveExtensions[]=exp_request_shield, then the hook line inconfig.phpabove the HTTP cache's early exit. Under Velocity, restart it. - Give the site its own rule file in
settings/request-shield/, with copies of the threeexponential*.rulesbeside it, its own host names andwww.example.org. - After every change:
bin/request-shield checkandtest. To see why a request was decided:trace. - The shield enforces as installed. Watch Setup > Request shield > Log in the first days; switch to monitor
while you look into something; add exceptions with an
expectexample each. - Off at once:
var/request-shield/PAUSED, or Pause in the administration.
Contents
- Install it on an existing site
- Give the site its own rules
- Check, test and trace
- Monitor or enforce
- Add an exception
- Pause it
- Update the library
- Uninstall
- Related pages
1. Install it on an existing site
Requirements: Exponential 6.0 and PHP 7.4 to 8.5. Under Exponential Velocity the site needs the 6.0.15 line of
index.php, which runs the file named in EXP_REQUEST_PREPEND_FILE; on an older index.php the shield protects
the PHP-FPM requests only. APCu is optional.
Get the package, from the installation's root:
composer require se7enxweb/exp_request_shieldThe legacy installer puts it in
extension/exp_request_shield/. On an installation fromse7enxweb/exponentialmainit is already there, because the rootcomposer.jsonrequires it.Activate the extension in
settings/override/site.ini.append.php:[ExtensionSettings] ActiveExtensions[]=exp_request_shieldRegenerate the autoloads and clear the caches:
php bin/php/ezpgenerateautoloads.php -e php bin/php/ezcache.php --clear-allOn an installation with the
exp:velocity deploycommand,./console exp:velocity deploy --packerdoes both, reloads PHP-FPM and restarts Velocity.Give the site its own rules (section 2) and check them (section 3) before the next step: from then on the rules decide every request.
Add the hook to
config.php(next toindex.php), above the line that loads the HTTP cache's early exit (ezphttpcacheearlyexit.php), so that cached pages are protected too:if ( is_file( __DIR__ . '/extension/exp_request_shield/bootstrap/hook.php' ) ) require __DIR__ . '/extension/exp_request_shield/bootstrap/hook.php';config.php-RECOMMENDEDhas these lines, with their comment, in the section REQUEST SHIELD. Under PHP-FPM the next request is filtered; nothing to restart. Under Velocity restart it, because its workers readconfig.phponce:./console exp:velocity restartSee it work. Open Setup > Request shield: the dashboard says whether the shield ran for that page, in which mode, from which rule file, and what it decided in the last 24 hours. From a shell:
curl -sI https://www.example.org/settings/site.ini # 404, decided by the shield tail -f var/request-shield/log/shield.log
The extension and the hook work without each other: without the hook the administration shows the setup but nothing is filtered; without the activated extension the hook still filters, but there is no admin page.
2. Give the site its own rules
The extension's own rules/request-shield.rules is used until the site has its own file, and every update of the
extension replaces it. Give the site its own:
From the administration: Setup > Request shield, any mode button (Enforce, Monitor or Off). The first switch makes
settings/request-shield/request-shield.rulesfrom the default and copies the threeexponential*.rulesnext to it. The web server must be able to writesettings/request-shield/.By hand, as the user the site's PHP runs as:
mkdir -p settings/request-shield cp extension/exp_request_shield/rules/request-shield.rules \ extension/exp_request_shield/rules/exponential*.rules settings/request-shield/
The copies sit beside the main file because an include may not leave its file's directory with ..; the library
refuses it. Then edit settings/request-shield/request-shield.rules:
Pick the admin's file:
include exponential-admin-uri.ruleswhen the administration is the siteaccess/admin,include exponential-admin-host.ruleswhen it has a host name of its own.Fix the paths: relative paths are relative to the rule file, and from
settings/request-shield/the installation's root is two levels up (the extension's default says three):set store-dir ../../var/request-shield/store set log ../../var/request-shield/log/shield.logName the site's host names, so that a request for any other name is refused, and keep
www.example.org:host shop.example.com example.com admin.example.com host www.example.org # the documentation domain the shipped examples useThe library's Exponential examples send
Origin: https://www.example.org; without that host linebin/request-shield testfails five of them (403 byEXP-ORIGIN). The domain is reserved for documentation, so no real visitor ever sends it.Name the proxy or load balancer in front, if there is one:
trust 10.0.0.0/8. Without it every visitor seems to come from the proxy's address.Adapt the site-specific rules, each one line in
exponential.rules, replaced in your main file withreplace [ID] ...:EXP-MODULES(the URI siteaccesses that may stand in front, for example(/(ger|eng))?),EXP-POST(the modules of your extensions that take forms),EXP-SEARCHES(the search budget).
A shorter site file, for reference:
ids SITE
version 2026-11-12.1
include exponential-admin-uri.rules
set mode enforce
host www.example.org example.org
set store-dir ../../var/request-shield/store
set log ../../var/request-shield/log/shield.log
set log-level flag
[SITE-DASH] restrict /rs/** to 127.0.0.1 ::1
expect GET /rs/stats/overview from 198.51.100.27 403 by SITE-DASHTo load a file somewhere else, define EXP_REQUEST_SHIELD_RULES in config.php before the hook line. The
other constants (EXP_REQUEST_SHIELD_ROOT, EXP_REQUEST_SHIELD_VAR, EXP_REQUEST_SHIELD_DISABLED) are described in
the extension's doc/configuration.md.
Every line of the rule language: the library's
rule file reference.
The web server must never serve settings/ or var/request-shield/; a standard Exponential virtual host refuses
both.
3. Check, test and trace
Run the command line as the user the site's PHP runs as (sudo -u <web user> ... when you are root), so that the
files it writes stay readable for the site:
php extension/exp_request_shield/bin/request-shield rules
php extension/exp_request_shield/bin/request-shield check settings/request-shield/request-shield.rules
php extension/exp_request_shield/bin/request-shield test settings/request-shield/request-shield.rulesrulesprints the main rule file the site uses and where it comes from.checkreads everything, names any mistake by file and line, warns about risky setups (the library's/rspages open to everyone, for example) and prints the store's tier.testdecides everyexpectexample written next to the rules. It must pass after every change.
To see how one request is decided, rule by rule:
php extension/exp_request_shield/bin/request-shield trace settings/request-shield/request-shield.rules "GET https://www.example.org/the/path"check and test compile the settings next to the rule file (.request-shield/ beside it); the web requests use
var/request-shield/cache/. Setup > Request shield > Rules shows the same result in the administration.
4. Monitor or enforce
The rules ship in enforce mode: as soon as the hook is in config.php, scanners, floods, stray forms and system
URLs are refused, and the login and the admin's forms ask for the browser check (a second's work for a real
browser, once per pass). Rules marked monitor in the rule files (EXP-BAN, EXP-STRICT) only watch.
In monitor mode every decision is logged as it would be taken (monitor-reject, monitor-challenge ...) and
nobody is refused. Use it:
- on a site with many unusual addresses (page aliases that start with a module name, forms of its own extensions), for the first days, while editors work and the search is used;
- while you look into a complaint that a real visitor was refused.
Switch with Enforce and Monitor on the dashboard (policy requestshield/manage), or write set mode monitor
or set mode enforce in the site's main file. Every server reads a changed rule file within its recheck seconds;
nothing to restart. Read Setup > Request shield > Log, decision group Watched (monitor), and go back to enforce when
the log shows only what you want refused.
5. Add an exception
When a real visitor or editor was refused (the log names the rule, for example 404 by EXP-MODULES or
EXP-SYSVIEW for a page alias that starts with a module name):
- Find the line in Setup > Request shield > Log, or
tracethe request. - Add an exception for it in the site's main file, with an
expectexample of the address that must pass, and one of a near miss that must still be refused. The library's Exponential use case shows the patterns. - Run
checkandtest.
For an editor who keeps getting the browser check in the administration: allow JavaScript in the browser, or name a
fixed office address with exempt <address>.
6. Pause it
| How | Effect | Undo |
|---|---|---|
| Pause on the dashboard | creates var/request-shield/PAUSED: the shield does nothing, on every server, from the next request | Switch on |
touch var/request-shield/PAUSED | the same, from a shell, without editing anything | remove the file |
Off, or set mode off in the rules | nothing is decided; the rule file is changed | Enforce or Monitor |
define( 'EXP_REQUEST_SHIELD_DISABLED', true ); before the hook line | the hook does nothing | remove the line; restart Velocity |
Each switch from the administration is written to var/log/requestshield.log with the user's login and id and the
outcome.
7. Update the library
The library comes with the extension; update the extension, never the bundled files with the library's
self-update, so the files, ezinfo.php and the changelog stay together:
composer update se7enxweb/exp_request_shieldThen run check and test on the site's rules, and compare the three copies in settings/request-shield/ with
extension/exp_request_shield/rules/: take the new versions over when they changed.
Under Velocity, a worker that already loaded the old classes must not meet the new files: create
var/request-shield/PAUSED, update, ./console exp:velocity restart, then remove PAUSED.
Maintainers who build a new release of the extension from a new library tag use bin/update-library.sh, which
builds the single files and the PHP 7.4 build and checks the rules on every PHP named:
doc/library.md.
8. Uninstall
- Remove the hook lines from
config.php; under Velocity,./console exp:velocity restart. - Remove
ActiveExtensions[]=exp_request_shield, thencomposer remove se7enxweb/exp_request_shield(or remove the directory). On an installation whose rootcomposer.jsonrequires the package, leave it installed and only take out the hook and the activation. - Regenerate the autoloads and clear the caches.
- Remove
var/request-shield/andsettings/request-shield/when you no longer need the log, the statistics and the rules.