feat(nginx): canonical-host guard + scanner-probe blocking
Deploy to VPS / deploy (push) Has been cancelled
Deploy to VPS / deploy (push) Has been cancelled
Hardens the edge against the bot noise and IP-based access seen in the
production logs (raw-IP hits, SSRF probes to 169.254.169.254 / localhost /
metadata.google.internal, scans for /config/database.php, /.git-credentials,
wp-admin, etc.).
1. Canonical-host guard — default_server blocks on 80 and 443 that catch
any Host that is NOT rf-flux.com/www.rf-flux.com and return 444 (drop).
- Kills the redirect-to-raw-IP bug at the edge: IP requests never reach
Next.js, so the middleware can't build an IP-based redirect.
- Blocks SSRF probes and most bot scans before they touch the app.
- ACME HTTP-01 still works (acme-challenge location kept on :80).
- Legitimate traffic is unaffected: exact server_name beats
default_server, so the rf-flux.com blocks always win.
2. Scanner-probe blocking — a regex location in the rf-flux.com server
that returns 444 for .php/.env/.git/wp-admin/etc. This is a Next.js app
so none of those are real; the patterns never match real assets
(.jpg/.png/.webp/.mp4/.glb/.pdf) or app routes.
Apply with `nginx -t` then `nginx -s reload` — no rebuild, no downtime.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,41 @@ upstream nextjs {
|
|||||||
keepalive 32;
|
keepalive 32;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────
|
||||||
|
# CANONICAL-HOST GUARD (default_server for ports 80 + 443)
|
||||||
|
# Catches every request NOT addressed to rf-flux.com / www.rf-flux.com —
|
||||||
|
# raw-IP access (135.125.53.234), SSRF probes (Host: 169.254.169.254,
|
||||||
|
# localhost, metadata.google.internal) and the bulk of bot scans that hit
|
||||||
|
# the bare IP. Returns 444 (drop the connection, send nothing).
|
||||||
|
#
|
||||||
|
# Legitimate traffic is unaffected: the rf-flux.com server blocks below win
|
||||||
|
# because an exact server_name match always beats default_server.
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────
|
||||||
|
server {
|
||||||
|
listen 80 default_server;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
# Keep ACME HTTP-01 working so certbot can still renew on any host.
|
||||||
|
location /.well-known/acme-challenge/ { root /var/www/certbot; }
|
||||||
|
|
||||||
|
location / { return 444; }
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443 ssl default_server;
|
||||||
|
http2 on;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
# A cert is required to complete the TLS handshake before the Host is
|
||||||
|
# known; reuse the rf-flux.com cert, then drop. Bots hitting the IP get
|
||||||
|
# a cert-name mismatch and a closed connection — nothing is proxied.
|
||||||
|
ssl_certificate /etc/letsencrypt/live/rf-flux.com/fullchain.pem;
|
||||||
|
ssl_certificate_key /etc/letsencrypt/live/rf-flux.com/privkey.pem;
|
||||||
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
|
|
||||||
|
return 444;
|
||||||
|
}
|
||||||
|
|
||||||
# Legacy domain redirect — anyone landing on lethepowerflux.com lands on
|
# Legacy domain redirect — anyone landing on lethepowerflux.com lands on
|
||||||
# the canonical https://www.rf-flux.com host instead. SEO-safe 301.
|
# the canonical https://www.rf-flux.com host instead. SEO-safe 301.
|
||||||
server {
|
server {
|
||||||
@@ -55,6 +90,17 @@ server {
|
|||||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||||
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
|
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
|
||||||
|
|
||||||
|
# ── Scanner / exploit-probe blocking ────────────────────────────────
|
||||||
|
# This is a Next.js app — it has no PHP, no .env/.git served over HTTP,
|
||||||
|
# no wp-admin. Any request for those paths is a bot probing for secrets
|
||||||
|
# or exploits. Drop them cheaply with 444 before they touch the app.
|
||||||
|
# The patterns are scanner-specific and never match real assets
|
||||||
|
# (.jpg/.png/.webp/.mp4/.glb/.pdf/.svg) or app routes.
|
||||||
|
location ~* (?:\.(?:php|phtml|asp|aspx|jsp|cgi|env|sql|bak|ini|sh|yml|yaml|conf)$|/\.(?:git|env|aws|ssh|svn|hg|idea|vscode)|/(?:wp-admin|wp-login|wordpress|phpmyadmin|xmlrpc)) {
|
||||||
|
return 444;
|
||||||
|
access_log off;
|
||||||
|
}
|
||||||
|
|
||||||
# Next.js bundles use content hashing — safe to cache forever
|
# Next.js bundles use content hashing — safe to cache forever
|
||||||
location /_next/static/ {
|
location /_next/static/ {
|
||||||
proxy_pass http://nextjs;
|
proxy_pass http://nextjs;
|
||||||
|
|||||||
Reference in New Issue
Block a user