8G for nginxrules v1.5runs in your browser
8G Firewall Rule Finder
Paste an 8G log line to see the rule that fired, its exact pattern, and the text it matched. On GridPane, run 8G in watch mode beside 7G first, so you find the false positives before 8G blocks anything.
2026-10-21T14:02:11+00:00 200 "GET /pricing/" cookie=0 method=0 referer=0 bot=3 querystring=0 request=0 ip=203.0.113.20 ua="Mozilla/5.0 (compatible; AhrefsSiteAudit/6.1; +http://ahrefs.com/robot/site-audit)"
bot 3User agent rules
Matched Ahrefs in the user agent. Ahrefs Site Audit checks your site for SEO problems: likely one you want.
map $http_user_agent $bad_bot_ng {
"~*ahrefs|archiver|curl|libwww-perl|pycurl|scan" 3;
}How it works
From a log line to the exact rule in three steps
Copy the line
8G logs every request it blocks, or in watch mode would block, with the rule that fired. A line from any nginx or Apache access log works too. For the latest from one site:
tail /var/www/<site>/logs/8g.logSee the rule
You get the group and number, such as
bot=3, its pattern, the text it matched, and any other rule in that group that matches too.Decide
An attack, or something you want? For something you want, you get the documented way to let it through for now, and what it costs.
7G to 8G on GridPane
Run 8G beside 7G first, then switch
GridPane has no 8G switch: its 7G toggle is GridPane’s own build of 7G. On its nginx you run nG-SetEnvIf (opens in a new tab), the nginx version of 8G. Watch mode lets 8G log what it would block for a week or two while 7G keeps protecting the site.
Watch
safe on a live site8G checks every request and logs what it would block. It blocks nothing, and 7G stays on.
It can’t see static files, which GridPane doesn’t log, or anything 7G blocks first. So the log shows what 8G would add on top of 7G.
Enforce
staging first8G blocks, with a 403, or a 405 for a method it refuses. Turn 7G off for that site once 8G is on.
Jeff Starr’s advice is to run 7G or 8G on a site, not both.
Check the server
Read-only. It confirms nginx (not OpenLiteSpeed), that GridPane loads the site’s own config files, and that nginx records the visitor’s real address behind Cloudflare.
Show the commands
SITE=example.com nginx -v ls /var/www/$SITE/nginx/ nginx -T 2>/dev/null | grep -nE "/var/www/$SITE/nginx/\*-(http|main)-context\.conf" nginx -T 2>/dev/null | grep -E '^\s*real_ip_header' | sort -u; nginx -T 2>/dev/null | grep -cE '^\s*set_real_ip_from' nginx -T 2>/dev/null | grep -E '^\s*set_real_ip_from\s+(0\.0\.0\.0/0|::/0)\s*;'Download the rules
Two files from nG-SetEnvIf, pinned to one commit of 8G v1.5, saved in an
8gfolder inside the site’snginxfolder. Nothing loads them yet.Show the commands
SITE=example.com mkdir -p /var/www/$SITE/nginx/8g && cd /var/www/$SITE/nginx/8g NG=https://raw.githubusercontent.com/t18d/nG-SetEnvIf/344d9c417c02cf065d997059c7491100d0c96797 curl -fsSLO "$NG/8g-firewall.conf" && curl -fsSLO "$NG/8g.conf" head -1 8g-firewall.conf # 8G FIREWALL v1.5 20251006Turn on watch mode
Two small files: one loads the rules and sets the log format, one checks each request and logs what 8G would block. If
nginx -tfails, the step removes its own files and reloads nothing.Show the commands
SITE=example.com D=/var/www/$SITE/nginx cat > $D/8g-http-context.conf <<'X' # 8G test (nG-SetEnvIf): loads the rules and defines this site's 8G log format. include /var/www/SITE_NAME/nginx/8g/8g-firewall.conf; log_format ng8_SITE_NAME '$time_iso8601 $status "$request_method $request_uri" ' 'cookie=$bad_cookie_ng method=$not_allowed_method_ng referer=$bad_referer_ng ' 'bot=$bad_bot_ng querystring=$bad_querystring_ng request=$bad_request_ng ' 'ip=$remote_addr ua="$http_user_agent" ref="$http_referer"'; X cat > $D/8g-watch-main-context.conf <<'X' # 8G watch mode: checks every request against 8G and logs what it would block. Blocks nothing. # The checks run here, before WordPress rewrites the address to /index.php. set $ng8_hit ""; if ($bad_cookie_ng) { set $ng8_hit 1; } if ($not_allowed_method_ng) { set $ng8_hit 1; } if ($bad_referer_ng) { set $ng8_hit 1; } if ($bad_bot_ng) { set $ng8_hit 1; } if ($bad_querystring_ng) { set $ng8_hit 1; } if ($bad_request_ng) { set $ng8_hit 1; } access_log /var/www/SITE_NAME/logs/8g.log ng8_SITE_NAME if=$ng8_hit; X sed -i "s|SITE_NAME|$SITE|g" $D/8g-http-context.conf $D/8g-watch-main-context.conf if nginx -t; then gp ngx reload; else rm $D/8g-http-context.conf $D/8g-watch-main-context.conf; echo "FAILED: files removed, nothing reloaded"; fiCheck that it works
A request with curl’s own user agent, which 7G allows and 8G would block, should land in the 8G log a few seconds later. The site still answers it normally.
Show the commands
SITE=example.com curl -sk -o /dev/null -w '%{http_code}\n' --resolve $SITE:443:127.0.0.1 https://$SITE/ # 200 or 301: nothing blocked sleep 5 grep 'curl/' /var/www/$SITE/logs/8g.log | tail -n 1 # a line with bot=3 and ua="curl/..." grep 'curl/' /var/log/nginx/$SITE.access.log | tail -n 1 # the site's normal log has it tooRead the log for a week or two
Count which rules fire, then paste the lines into 8G Helper above. Each one is an attack, or a person, plugin or service you want. Every “want” is a false positive to fix before you enforce.
Show the commands
SITE=example.com # which rules fire, most first grep -oE '(cookie|method|referer|bot|querystring|request)=[1-9][0-9]*' /var/www/$SITE/logs/8g.log | sort | uniq -c | sort -rn tail -n 30 /var/www/$SITE/logs/8g.log # show one rule's pattern, e.g. querystring=39 rule() { sed -n "/\$bad_$1_ng {/,/^}/p; /\$not_allowed_$1_ng {/,/^}/p" /var/www/$SITE/nginx/8g/8g-firewall.conf | grep -E " $2;\$"; } rule querystring 39Enforce, on staging first
Swap the watch file for the enforce file, prove it blocks, then turn 7G off for that site in GridPane. Try everything a person does on the site, then move on to one low-risk live site.
Show the commands
SITE=example.com D=/var/www/$SITE/nginx cat > $D/8g-main-context.conf <<'X' # 8G enforce mode (nG-SetEnvIf): blocks with 403, or 405 for a disallowed method. include /var/www/SITE_NAME/nginx/8g/8g.conf; access_log /var/www/SITE_NAME/logs/8g.log ng8_SITE_NAME if=$ng_reason; X sed -i "s|SITE_NAME|$SITE|g" $D/8g-main-context.conf mv $D/8g-watch-main-context.conf $D/8g-watch-main-context.conf.off if nginx -t; then gp ngx reload; else rm $D/8g-main-context.conf; mv $D/8g-watch-main-context.conf.off $D/8g-watch-main-context.conf; echo "FAILED: still in watch mode, nothing reloaded"; fi # a few seconds later SITE=example.com curl -sk -o /dev/null -w '%{http_code}\n' -A 'Mozilla/5.0' --resolve $SITE:443:127.0.0.1 https://$SITE/ # 200 or 301 curl -sk -o /dev/null -w '%{http_code}\n' --resolve $SITE:443:127.0.0.1 https://$SITE/ # 403 tail -n 1 /var/www/$SITE/logs/8g.log # status 403, bot=3
nginx keeps one set of 8G rules per server, so when two sites each have a copy, the copy loaded last applies to both. Keep every copy on a server identical, and never edit one site’s copy alone.
What the logs showed on real sites
- Crawlers, more than attacks. On real sites most of what 8G would add came from search and AI crawlers in its
botrules. Those rules are a choice about which crawlers you want, not attack signatures. - Your own tools.
botrule 3 blockscurl,scanandahrefs: uptime monitors, cron jobs, a payment provider’s security scanner, Ahrefs Site Audit, and your own tests. - Site search.
querystringrule 39 blocks searches containing words such asget,selectordeletebetween other words. - AI agents that sign in.
querystringrule 9 blockslocalhostin a query string, which OAuth sign-ins for MCP connectors send. Don’t enforce on a site an agent connects to without an exception. - The real catch. Probes for shells, backups and config files that 7G let through, nearly all answered 404 already.
Undo: take 8G off a site
SITE=example.com
rm -r /var/www/$SITE/nginx/8g /var/www/$SITE/nginx/8g-* && nginx -t && gp ngx reloadWhat 8G checks
Six rule groups, each reading one part of the request
nginx reads each group as a list and stops at the first pattern that matches, so the log names one rule per group. 8G Helper checks the whole list, so a second match can’t surprise you after the first is fixed.
Query string
41 rules$query_string$bad_querystring_ng
Injection after the ?: SQL keywords, script tags, file paths, encoded slashes. Read exactly as sent.
Often blocks by mistake: Site searches with words like get or select, and OAuth sign-ins that send localhost.
Request path
57 rules$uri$bad_request_ng
Paths scanners try: shells, backups, config files, odd characters. Read decoded, without the query string.
Often blocks by mistake: A real page whose address looks like one, such as /backup/.
User agent
14 rules$http_user_agent$bad_bot_ng
Scanner and script names, fake browser names, and some crawlers.
Often blocks by mistake: Crawlers you want, uptime monitors and curl, a payment provider’s security scanner.
Referrer
4 rules$http_referer$bad_referer_ng
Spam and injection in the referring address.
Often blocks by mistake: Rare.
Cookie
1 rule$http_cookie$bad_cookie_ng
Injection hidden in a cookie.
Often blocks by mistake: Rare. The log names the rule but never records the cookie itself.
Method
1 rule$request_method$not_allowed_method_ng
Methods a WordPress site never needs, such as TRACE and CONNECT.
Often blocks by mistake: Almost never. It answers 405, and nginx refuses TRACE on its own first.
Questions
Before you change anything
Does the log line I paste leave my browser?
No. Matching runs in your browser against a bundled copy of 8G v1.5 from nG-SetEnvIf, and nothing you paste is sent or stored.
What is watch mode?
A way to try 8G on a live site without risk: 8G checks every request and logs what it would block, while 7G keeps blocking. It is two small files in the site’s own nginx folder. See 7G to 8G.
Why is there no exception file, like 7G Helper’s?
On GridPane, 7G reads update-safe exception files and has a switch for each rule group. 8G has neither yet, and its rules are one shared set per server. Until a scoped exception has been tested in enforce mode, 8G Helper gives the documented fix: comment out the rule, or remove the one word that matched, in every copy on the server.
Will a GridPane update remove 8G?
No. Everything lives in the site’s own nginx folder, which updates leave alone. Downloading a newer 8G replaces the rules, along with any line you commented out, so redo those edits after.
A request got a 403, but no 8G rule matches it.
Then 8G did not block it. A 403 can also come from 7G, Cloudflare, Fail2Ban or a security plugin. In watch mode, 7G runs first, so anything it blocks never reaches the 8G log.
Is this made by GridPane, or by the author of 8G?
No. 8G is Jeff Starr’s firewall, published at Perishable Press (opens in a new tab). nG-SetEnvIf, the nginx version, is by Tonkünstler-on-the-Bund. 8G Helper is an independent tool, so check each change before you make it.
What about Apache?
Jeff’s official 8G is for Apache. An Apache version of this tool comes next, tested against real Apache before it ships.