Command Line

Reference of the pxshield command: status, sites, WAF rules per site, blocked requests and false positives, bypass IPs, trusted proxies, exclusions, cache, license and server settings, from the shell or from scripts.

The pxshield command manages pxShield from the shell. It comes with the pxshield package, so every server has it, with or without a control panel. Every action of the full screen interface is also a command, which makes it the tool for scripts, automation and quick checks over SSH.

It must run as root, and the pxShield service must be running (systemctl start pxshield). Without arguments, on a terminal, it opens the full screen interface; from a script it prints its usage.

pxshield help              # the list of commands
pxshield <command> --help  # the options of one command

Global options go before the command:

Option What it does
--json Prints the answer of the service as JSON, for scripts
--version Prints the version
-h, --help Prints the usage

Exit codes: 0 ok, 1 error, 2 wrong usage, 3 the license is not valid (status and license).

A site is named by its domain. www.example.com and example.com are the same site, and a name that is not a website of the server is refused: pxshield sites lists them.

Status

pxshield status
Service      running
License      valid until 2027-11-10
Sites        6 sites, 2 with modified rules
Blocked      214 in 24 h, 4,128 in 30 days (+9% vs the 30 days before)
Last block   3 min ago

When a server-wide setting is waiting for a restart, a Settings line says so. The exit code is 3 when the license is not valid, so a monitoring check can use it:

pxshield status >/dev/null || echo "pxShield needs attention"

Sites

pxshield sites
SITE                 RULES
blog.example.com     default
example.com          default
shop.example.com     modified
staging.example.net  default
store.example.org    modified
wiki.example.net     default

The websites pxShield protects, read from the configuration of the web server (nginx, Apache or Caddy). modified means the site has rules that differ from the defaults. After adding a website to the web server, reload the web server and pxShield picks it up.

Rules

pxshield rules <site> [show|on|off|reset] [rule ...] [--all] [--yes]

Without an action, it lists every rule of the site and its state:

$ pxshield rules example.com
WAF rules of example.com

Protocol violations
  RULE     STATE  PROTECTS AGAINST
  STD-001  on     HTTP Protocol violation: Bad request line  always on
  STD-002  off    IP in host header not allowed
  STD-003  on     Missing User-Agent header                  changed
  STD-004  on     Null character in request                  always on
  STD-005  on     Path traversal attack in URI               always on

SQL Injections
  RULE     STATE  PROTECTS AGAINST
  SQL-001  on     SQL Injection attempt in GET
  SQL-002  on     SQL Injection attempt in POST
  SQL-003  on     Attempts to search/download a .sql file
...

changed marks a rule whose state differs from its default; always on marks a rule that protects the server itself and cannot be switched off.

on and off switch rules. Give rule ids, or a category to switch all its rules at once (STD, BOT, SQL, XSS, APP, WP, UPL, BF, DOS, IP); separate several with spaces or commas. Case does not matter.

pxshield rules example.com on XSS-001 XSS-002     # two rules
pxshield rules example.com on XSS                 # every XSS rule
pxshield rules example.com off STD-003 --yes      # no question, for a script
pxshield rules example.com on SQL --all           # the SQL rules, on every site
pxshield rules example.com reset                  # every rule back to its default
Option What it does
--all Applies the change to every site of the server, not only to the one named
--yes Does not ask. Switching rules off and reset ask first on a terminal, and refuse without one unless --yes is given

reset also switches off the rules you had switched on, such as the XSS rules, which are off by default. Changes apply within a minute; the service does not need a restart.

Blocked requests

pxshield events [list|show <id>|allow <id>] [options]

pxshield events lists the requests the WAF blocked, newest first:

$ pxshield events --limit 5
ID     TIME            SITE               IP              CC  RULE     REQUEST
41208  11-10 11:57:00  shop.example.com   185.220.101.34  DE  SQL-001  GET /product.php?id=1' UNION SELECT user,pass F…
41207  11-10 11:50:00  example.com        45.155.205.211  NL  XSS-002  POST /wp-admin/admin-ajax.php
41206  11-10 11:43:00  blog.example.com   103.152.118.20  IN  WP-001   POST /xmlrpc.php
41205  11-10 11:36:00  example.com        193.41.206.82   RU  APP-008  GET /.env
41204  11-10 11:29:00  store.example.org  45.148.10.95    NL  APP-007  GET /.git/config
5 of 4,128. --offset 5 shows the next ones.
Option What it does
--site <site> Only the requests to this site
--ip <address> Only the requests from this visitor IP
--rule <rule> Only the requests blocked by this rule, as SQL-001
--country <country> Only the requests from this country, as a two-letter code (US)
--limit <n> At most n requests, up to 1000 (50 by default)
--offset <n> Skips the first n, to read the next page

The filters match exact values and can be combined:

pxshield events --site example.com --rule XSS-002
pxshield events --ip 185.220.101.34 --limit 200

show prints one request in full: the visitor, the rule, the request line, its headers and the start of its body.

$ pxshield events show 41207
Event       41207
Time        2026-11-10 11:50
Site        example.com
Visitor     45.155.205.211 NL
Rule        XSS-002 XSS Injection attempt in POST
Request     POST /wp-admin/admin-ajax.php HTTP/1.1
Host        example.com

Headers
  Host: example.com
  User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
  Accept: */*
  Content-Type: application/x-www-form-urlencoded

Body
  action=save&content=%3Cscript%3Ealert(document.cookie)%3C%2Fscript%3E

A false positive? pxshield events allow 41207

Text written by visitors (URLs, headers, bodies) is printed with its control characters escaped (\x1b), so a crafted request cannot change your terminal.

Allowing a false positive

allow stops the rule that blocked a request from applying to that URL of its site. It adds an exclusion for the path of the request, without its query string:

$ pxshield events allow 41207
XSS-002 no longer applies to example.com/wp-admin/admin-ajax.php.

--url sets a shorter prefix, to cover more URLs:

pxshield events allow 41207 --url /wp-admin/

Every other rule still applies to those URLs, and the rule still applies to the rest of the site.

Exclusions

pxshield exclusions <site> [list|add|remove <n>] [options]

The exclusions of a site: rules that do not apply to the URLs that start with a prefix, optionally only for some HTTP methods or some visitor IPs.

$ pxshield exclusions example.com
#  URL PREFIX                RULES            METHODS  IPS
1  /wp-admin/admin-ajax.php  XSS-002          all      all
2  /api/import               SQL-002,UPL-002  POST     203.0.113.10
pxshield exclusions example.com add --url /wp-admin/admin-ajax.php --rules XSS-002
pxshield exclusions example.com add --url /api/import --rules SQL-002,UPL-002 --methods POST --ips 203.0.113.10
pxshield exclusions example.com remove 2
Option of add What it does
--url <prefix> The URL prefix, starting with /. Required
--rules <ids> The rule ids, separated by commas. Required
--methods <methods> Only these HTTP methods, separated by commas. All by default
--ips <ips> Only these visitor IPs, separated by commas: exact addresses, not ranges. All by default

When an exclusion with the same prefix, methods and IPs exists, add adds the rules to it instead of creating another one. remove takes the number shown by list; list again before removing if someone else may have changed them. See WAF Exceptions for how exclusions are matched.

Bypass IPs

pxshield bypass [list|add|remove] [--site <site>] [ip ...]

IP addresses and CIDR ranges that skip every WAF rule: on the whole server, or with --site on one site. Use it for your own office, a monitoring service or a trusted integration, never for visitors. Separate several IPs with spaces or commas.

pxshield bypass                                        # the server-wide list
pxshield bypass add 203.0.113.10                       # on every site (asks first)
pxshield bypass add 198.51.100.0/24 --site example.com --yes
pxshield bypass remove 203.0.113.10

add asks first on a terminal, and refuses without one unless --yes is given. The server-wide list applies when the service restarts (pxshield restart); the list of a site applies within a minute. The IPs of the server-wide list skip the WAF on every site, also when the list of the site does not name them.

Trusted proxies

pxshield proxies [list|add|remove|cloudflare] [--site <site>] [ip ...|on|off|default]

When the sites are behind a reverse proxy — a CDN, a load balancer — every request comes from the proxy. List the proxy here and the WAF takes the visitor IP from the X-Forwarded-For header of the requests that come from it, so the WAF and the blocked requests see the real visitor.

pxshield proxies add 198.51.100.7                      # for every site
pxshield proxies cloudflare on                         # trust the Cloudflare edges, on every site
pxshield proxies add 192.0.2.20 --site example.com     # one more, only for this site
pxshield proxies cloudflare off --site example.com     # this site is not behind Cloudflare
pxshield proxies cloudflare default --site example.com # follow the server again
pxshield proxies --site example.com                    # what this site trusts
$ pxshield proxies --site example.com
Trusted proxies of example.com
  192.0.2.20
Also trusted, from the whole server
  198.51.100.7
Cloudflare on, as the server

A site adds proxies to the server-wide ones; it cannot drop them. Its Cloudflare setting replaces the server’s, both ways, or follows it with default. Changes to the server-wide proxies and Cloudflare apply at once; the ones of a site within a minute.

Cache

pxshield cache <site> [show|dynamic|webp|minify-js|minify-css|purge] ...
$ pxshield cache example.com
Cache of example.com
  Pages       off
  WebP        default
  Minify JS   default
  Minify CSS  default
pxshield cache example.com dynamic on          # cache pages, in the mode it had (manual the first time)
pxshield cache example.com dynamic wordpress   # on; the Pyxsoft WordPress plugin says what may be cached
pxshield cache example.com dynamic manual      # on, manual mode
pxshield cache example.com dynamic off
pxshield cache example.com webp on             # also minify-js and minify-css: on, off or default
pxshield cache example.com purge               # empties the cache of the site
pxshield cache example.com purge /blog/*       # a URL, or every URL that starts with a prefix ending in *

default follows the server. The cache only works when its engine is enabled in /etc/pxshield.ini (CacheStaticFiles, CacheDynamicFiles); both are off by default.

License

pxshield license              # the state of the license
pxshield license set          # asks for the code
pxshield license trial        # starts the free trial, when the server can still use one
$ pxshield license
License    valid until 2027-11-10
Code       ••••••D-91BX
Customer   Example Hosting

Without a code, set reads it from the standard input, so it does not show in the list of processes, where every user of the server could see it while the command runs:

pxshield license set < /root/pxshield-license.txt

pxshield license set <code> also works, for a quick change on a server only you use. The exit code is 3 while the license is not valid.

Server settings

pxshield config [show|max-post-size <MB>|gzip on|off]
$ pxshield config
Max POST size   128 MB
Gzip            on
WAF bypass      1 IP (pxshield bypass)
Trusted proxies 1 proxy (pxshield proxies)
pxshield config max-post-size 256    # the largest request body the WAF accepts, 1 to 2048 MB
pxshield config gzip off             # compression of the answers on the fly
pxshield restart                     # applies them

The POST size, gzip and the server-wide bypass apply when the service restarts. pxshield restart restarts it; the connections in progress are cut for a moment, so do it at a quiet time.

Scripts

Every command takes --json before its name and prints the answer of the service as JSON:

pxshield --json events --site example.com --limit 100 | jq -r '.items[].remoteIP' | sort | uniq -c | sort -rn
pxshield --json sites | jq -r '.[] | select(.modified) | .domain'

Commands that weaken the protection (rules off, rules reset, bypass add) ask on a terminal; from a script they refuse unless --yes is given, so a mistake in a script does not switch a rule off silently. Run them as root, for example from root’s crontab.

Troubleshooting

pxshield: it must run as root — run it as root, or with sudo.

the pxshield service is not running — start it with systemctl start pxshield; journalctl -u pxshield shows why it stopped.

something that is not the pxshield service listens on its RPC port — pxshield talks to the service on 127.0.0.1:2987, and only when the program listening there belongs to root, as the pxShield service does. Another program, run by another user, was listening on that port, so nothing was sent to it. Find it with ss -ltnp 'sport = :2987', stop it, and start pxShield.

example.com is not a site of this server — pxShield reads the sites from the configuration of the web server. Check that the site is enabled there and reload the web server (systemctl reload nginx, apache2, httpd or caddy).

This page is also available as markdown.