nginx Configuration
The production nginx config is split across two files, both tracked in the git repo and synced to the server by deploy.sh:
| Repo file | Server path | Contains |
|---|---|---|
admin/nginx.conf | /opt/server-stack/nginx/conf.d/nortonshop.conf | Bot blocking map, all server blocks (HTTP redirect, HTTPS www, bare-domain redirect, CDN, API, admin, auth, docs, s, signal) |
admin/nginx-main.conf | /opt/server-stack/nginx/nginx.conf | Worker settings, gzip, rate limit zones, security headers, includes conf.d/*.conf |
deploy.sh compares both files on each deploy. If either has changed, it copies the updated version to the server and restarts the nginx container automatically.
Bot blocking
All crawlers, scrapers, AI bots, and search engines are blocked at two levels:
robots.txt— politely asks bots not to crawl (respected by well-behaved bots)- nginx
$is_botmap — actively rejects known bot user-agents with403 Forbidden(or444connection drop on the API subdomain). Requests with empty user-agents are also blocked.
The bot list in nginx.conf uses case-insensitive substring matching (~* prefix), grouped by category: AI assistants, AI training crawlers, search engines, social media, SEO tools, archive crawlers, and other scrapers. Around 90 patterns cover 169+ known bots. New offenders can be added by appending a line like "~*scraperforce" 1; inside the map block (remember to watch for stray unicode characters when editing remotely) and reloading nginx with docker compose exec nginx nginx -t && docker compose exec nginx nginx -s reload.
$is_bot map in admin/nginx.conf and updating robots.txt with Allow: / rules for the engines you want. For example, to allow Google and Bing indexing, remove googlebot and bingbot from the map, and add User-agent: Googlebot / Allow: / to robots.txt.
Security rules
Security rules are split between the main site and admin subdomain server blocks:
All server blocks:
- Dotfiles — anything starting with
.is blocked (covers.encryption-key,.git,.sessions.json,.ip-salt, etc.) - Sensitive file types —
.bak,.sql,.ini,.log,.sh, etc. return 403 - Rate limiting — general (10r/s), API (5r/s), strict (2r/s for admin PHP)
Main site (www.nortonshop.net):
- Data directories —
/static/server/*/data/is blocked - Admin redirect — the
/admin/path 301 redirects toadmin.nortonshop.net
Admin subdomain (admin.nortonshop.net):
- Bot blocking — same
$is_botmap applies - PHP whitelist — only
index.php,send-mail.php, andmicroproof.phpare accessible; all other PHP files return 403 - Admin data files —
users.json,audit.json,config.php,bip-39-english.txt,.versions.json,.sessions.json,.ip-salt,.ip-geo-cache.json,drafts.json, and all other JSON files are blocked
Auth subdomain (auth.nortonshop.net):
- Bot blocking — same
$is_botmap applies - Handles login and 2FA, then redirects authenticated users to
admin.nortonshop.net
Custom 404 pages
The main site uses 404.html in the project root. The CDN uses static/404.html. Both are themed to match the site and are deployed via deploy.sh, so they survive container restarts and Watchtower updates.
SSL
Certificates are obtained by Certbot during server-setup.sh. The certificate covers nortonshop.net, www.nortonshop.net, cdn.nortonshop.net, api.nortonshop.net, admin.nortonshop.net, auth.nortonshop.net, docs.nortonshop.net, and s.nortonshop.net. A cron job runs twice daily (3am and 3pm) to check for renewal:
docker compose run --rm --entrypoint certbot certbot renew --quiet
Certbot only actually renews when the certificate is within 30 days of expiry. After renewal, nginx is reloaded automatically to pick up the new certificate.
Testing bot blocking
To verify a specific bot is blocked, use curl with a spoofed user-agent:
curl -I -A "AhrefsBot" https://www.nortonshop.net/ # should return 403
curl -I -A "DeepSeekBot" https://www.nortonshop.net/ # should return 403
curl -I -A "Mozilla/5.0" https://www.nortonshop.net/ # should return 200 (normal browser)