The MCP server includes built-in protection against DNS rebinding attacks. This security feature validates incoming request headers to ensure they come from trusted sources.
DNS rebinding is an attack where:
- An attacker controls a malicious domain (e.g.,
evil.com) - The DNS is configured to first resolve to a public IP, then switch to your internal IP
- A victim's browser visits the malicious domain
- The attacker's JavaScript makes requests to your internal MCP server, bypassing Same-Origin Policy
The server validates the HTTP Host header against a whitelist of allowed hosts. Requests from unauthorized hosts receive a 421 Misdirected Request error with the message "Invalid Host header".
Configure DNS rebinding protection via environment variables in your .env file:
# Recommended: Keep enabled in production
MCP_ENABLE_DNS_REBINDING_PROTECTION=true
# Only disable for testing/debugging
# MCP_ENABLE_DNS_REBINDING_PROTECTION=falseCRITICAL: You must include both the hostname alone AND with the port number.
# Comma-separated list of allowed Host header values
MCP_ALLOWED_HOSTS=yourdomain.com,yourdomain.com:443,internal.local,internal.local:8080Why both variants?
- Browsers and HTTP clients send different
Hostheaders depending on the port - Standard ports (80, 443): Usually sent without port →
Host: example.com - Non-standard ports: Sent with port →
Host: example.com:8085
Only needed if browser-based clients will access the server:
# For browser-based MCP clients
MCP_ALLOWED_ORIGINS=https://yourdomain.com,https://app.yourdomain.comMCP_ENABLE_DNS_REBINDING_PROTECTION=true
MCP_ALLOWED_HOSTS=localhost,localhost:8080,127.0.0.1,127.0.0.1:8080
MCP_ALLOWED_ORIGINS=MCP_ENABLE_DNS_REBINDING_PROTECTION=true
# Include internal hostname, internal IP, and any port variants
MCP_ALLOWED_HOSTS=staging.internal,staging.internal:8085,192.168.1.100,192.168.1.100:8085
MCP_ALLOWED_ORIGINS=MCP_ENABLE_DNS_REBINDING_PROTECTION=true
# Public domain, internal service names, and localhost for health checks
MCP_ALLOWED_HOSTS=mcp.yourdomain.com,mcp.yourdomain.com:443,mcp-bunkerweb,mcp-bunkerweb:8080,localhost,127.0.0.1
MCP_ALLOWED_ORIGINS=https://yourdomain.comWhen behind nginx/Traefik/BunkerWeb:
MCP_ENABLE_DNS_REBINDING_PROTECTION=true
# Include the public domain AND any internal routing names
MCP_ALLOWED_HOSTS=mcp.example.com,mcp.example.com:443,mcp-service,mcp-service:8080
MCP_ALLOWED_ORIGINS=Important: If your reverse proxy rewrites the Host header, configure it to preserve the original:
- Nginx:
proxy_set_header Host $host; - Traefik: Automatically preserves Host header
- BunkerWeb: Configure
REVERSE_PROXY_HOSTappropriately
curl -X POST http://yourdomain.com:8085/mcp/ \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"jsonrpc":"2.0","method":"tools/list","id":1}'Expected: 200 OK with list of tools
curl -X POST http://untrusted.com:8085/mcp/ \
-H "Host: untrusted.com:8085" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"jsonrpc":"2.0","method":"tools/list","id":1}'Expected: 421 Misdirected Request with message "Invalid Host header"
The MCP server logs all security validations. Look for:
{"level": "WARNING", "message": "Rejected request with invalid Host header: untrusted.com"}Symptoms: Client receives 421 Misdirected Request
Causes:
- Host not in
MCP_ALLOWED_HOSTS - Forgot to include port number variant
- Reverse proxy is rewriting
Hostheader
Solutions:
- Add the host to
MCP_ALLOWED_HOSTS - Include both
hostnameandhostname:port - Configure reverse proxy to preserve
Hostheader - Check actual
Hostheader sent:curl -v http://yourserver/mcp/
Symptoms: Claude Code fails to connect to MCP server
Solutions:
- Check
.mcp.jsonURL matches an allowed host - If using
http://192.168.1.100:8085/mcp, add192.168.1.100:8085to allowed hosts - Try using hostname instead of IP:
http://apps:8085/mcp - Verify server is accessible:
curl http://yourserver:8085/tools
Symptoms: Works with localhost but not with container name
Solutions:
- Add Docker service name to
MCP_ALLOWED_HOSTS:mcp-bunkerweb,mcp-bunkerweb:8080 - Add Docker bridge network IPs if needed
- Use Docker hostname resolver:
mcp-bunkerwebinstead of IP
- ✅ Keep
MCP_ENABLE_DNS_REBINDING_PROTECTION=true - ✅ Only list hosts you control in
MCP_ALLOWED_HOSTS - ✅ Use HTTPS in production (configure reverse proxy)
- ✅ Set
BUNKERWEB_API_TOKENfor API authentication - ✅ Use firewall rules to restrict access to MCP port
- ✅ Regularly audit
MCP_ALLOWED_HOSTSlist - ✅ Monitor logs for rejected requests (potential attacks)
NEVER disable in production unless you have alternative protections (e.g., firewall rules, VPN-only access).
Only disable for:
- Local development testing
- Troubleshooting connectivity issues (temporarily)
- Internal networks with strict physical security
Even then, prefer adding hosts to the allowlist rather than disabling protection.
For complex deployments, generate MCP_ALLOWED_HOSTS dynamically:
# In Dockerfile or startup script
export MCP_ALLOWED_HOSTS="$(hostname),$(hostname):8080,localhost,127.0.0.1"apiVersion: v1
kind: ConfigMap
metadata:
name: mcp-config
data:
MCP_ALLOWED_HOSTS: "mcp.example.com,mcp.example.com:443,mcp-bunkerweb,mcp-bunkerweb.default.svc.cluster.local,mcp-bunkerweb.default.svc.cluster.local:8080"# .env.production
MCP_ALLOWED_HOSTS=prod.example.com,prod.example.com:443
# .env.staging
MCP_ALLOWED_HOSTS=staging.example.com,staging.example.com:8085,192.168.1.100,192.168.1.100:8085