Shield sits in front of your site, on your own server, and decides who gets in: customers and search engines go through, bots, scrapers, attacks and traffic waves are stopped. It installs with one command and checks by itself that every site answers as before.
A lightweight web server (nginx, or Traefik if you already use it) receives the visitors and asks Shield about every page before handing it to your site. Images, CSS and JavaScript skip Shield. Your application is not modified and does not need to know Shield exists.
robots.txt and sitemaps are never stopped.Whatever runs behind the web server (PHP, Python, Java, Node…) does not matter.
| Receives the visitors today | Install |
|---|---|
| Apache on Debian or Ubuntu | Fully automatic: install, checks, rollback |
| nginx | The package holds the lines to include in your server blocks |
| Traefik (Docker) | The package holds Shield's container and the labels for your routers |
Plus: Docker, Python 3.8 or newer, and an administrator (sudo) for the install step. Shield uses about 50 MB of memory.
analyze reports what receives the visitors, which sites the server hosts, what runs behind them and anything that blocks the install. plan asks only what it cannot detect:
observe or enforce mode (see section 5).127.0.0.1; nginx takes the public addresses. A few seconds without service.sudo ./ward-setup-*/rollback.sh puts the Apache, nginx and certificate settings back exactly as they were, stops nginx and Shield, and checks Apache is back on its ports.
Start Shield with start-ward.sh from the package, copy ward-http.conf to /etc/nginx/conf.d/ and ward-server.conf to /etc/nginx/snippets/, then add one line in each protected server block:
Then sudo nginx -t && sudo systemctl reload nginx. If Shield stops, nginx lets visitors in.
start-ward.sh starts Shield on Traefik's network. Add the labels from traefik-labels.txt to the protected sites' containers and recreate them. Shield is used as a ForwardAuth middleware.
docker ps shows the ward container, listening on 127.0.0.1 only. It restarts by itself.127.0.0.1:80/443: do not put it back on all addresses while nginx is in front./etc/nginx/sites-available/ward-front). Plain-HTTP and redirect-only domains need nothing..htaccess keep working.Shield starts in observe mode: traffic waves and attacks are stopped, every other bot is only noted. After a few days, read the status (section 6). When the list contains no customers, switch to enforce:
Visitor passes and what Shield learnt are kept in the ward_data volume across restarts.
| Field | Meaning |
|---|---|
mode | observe or enforce |
counts.requests | Pages Shield was asked about |
counts.trusted | Requests from always-trusted addresses |
counts.<reason> | Refusals by reason; observed: means noted, not refused |
flood.active | A traffic wave is in progress |
flood.new_visitors_per_min / threshold_per_min | New visitors now, and where the wave alarm starts |
flood.past_floods | Recent waves: start, end, peak |
recent_refusals | The latest refusals with address, page and reason |
Shield can report to your Luviner account, so you see every server in one page and get an email (and a Telegram message, if you connect it) when a traffic wave starts and ends.
shield-setup.py plan asks for it. Shield already installed: add WARD_REPORT_TOKEN=… to /etc/ward-setup/ward.env and recreate the container (section 5)./.env), with numbers and tokens masked. Every minute, and at once when a wave starts or ends. No visitor address, no visited page, no header. The connection is outbound only: no port is opened. If luviner.com cannot be reached, protection is not affected.
Community plan: connecting to your Luviner account is the condition of the free plan. The numbers and attack signatures help Shield learn new attacks for every site. On the paid plans the connection is optional and everything can stay on your server.
Settings live in /etc/ward-setup/ward.env; after a change, recreate the container as in section 5.
| Variable | Default | Description |
|---|---|---|
WARD_GATE_MODE | enforce (install: observe) | observe or enforce |
WARD_FLOOD_ACTION | challenge | During a wave: challenge unknown visitors, only observe, or off |
WARD_TRUSTED_IPS | — | Addresses or networks always let through, comma-separated |
WARD_ALWAYS_ALLOW_PATHS | robots, sitemaps… | Pages always served to everyone (payment notifications, webhooks) |
WARD_PROBE_ALLOW | — | Platform paths the site uses legitimately (e.g. wordpress, graphql); set by shield-setup |
WARD_DECLARED_BOT_ACTION | allow | Bots that say who they are (SEO tools, AI crawlers): allow or block |
WARD_LOGIN_MAX_ATTEMPTS | 20 | Login form submissions per visitor every 15 minutes |
WARD_SITE_HOST | — | Your site's name, set by shield-setup |
WARD_REPORT_TOKEN | — | Luviner account token (section 7); empty: nothing is sent |
WARD_NODE_NAME | site name | How this server is called in the dashboard |
Shield can also be asked directly by your application, without a web server in front: POST /check on port 9741 with the request's details returns allow or block and a reason. Use it for APIs or to add Shield's verdict to your own logic. It does not protect against traffic waves, because the requests have already reached your application.
| Endpoint | Method | Description |
|---|---|---|
/check | POST | Verdict on one request: action, reason, score |
/gate/status | GET | Waves, refusals and mode (section 6) |
/health | GET | Health check |
/stats | GET | Operational statistics |
/metrics | GET | Prometheus metrics |
Real logs of an Italian B2B shop on Magento, replayed with Shield in front: one week to learn its normal traffic, then the morning a distributed scraper sent 18,560 requests in 5 minutes from 17,416 addresses and took the site down for over an hour.
| Without Shield | With Shield | |
|---|---|---|
| Wave recognised | — | after 38 seconds |
| Requests reaching the site, first 5 minutes | peak 122/s | 1.2/s on average |
| Connections held | by the site (exhausted) | by nginx |
| False wave alarms in the previous week | — | 0 |
| Customer actions refused (cart, login) | — | 0 |
| Real browsers that saw the check page | — | 207 of 683, once |
Since 26 October 2026 Shield protects that site in production.
ward_pass): list it in your cookie policy.plan again.