Skip to content

Edge security

A web application firewall, flood protection, AI-crawler blocking and CDN settings for every site, enforced at HostStack's edge before a request reaches your app.

At a glance

Three independent features, each set per service. They apply to web services and static sites (anything with a public URL), on every plan, including free and trial teams, at no extra charge. Owners, admins and members can change them, with the same permissions as every other service setting (see Teams & roles). Changes apply on the next deploy.

FeatureDefaultDashboardAPI fieldCLI flagMCP field
Web application firewallOffSettings → FirewallwafMode--waf-modewaf_mode
Shield (floods)AutoSettings → ShieldshieldMode--shield-modeshield_mode
AI crawlersAllowSettings → ShieldaiCrawlers--ai-crawlersai_crawlers
CDN and limitsNo CDN; 100 req/sSettings → Custom Domains → Edge ProtectionedgeProxy--edge-proxyedge_proxy
# Start the firewall in detect mode, read what it would block, then block
hoststack services update svc_abc --waf-mode detect
hoststack services waf-events svc_abc
hoststack services update svc_abc --waf-mode block --waf-disabled-rules 942100

# Force Shield attack mode on now, and refuse AI training crawlers
hoststack services update svc_abc --shield-mode on --ai-crawlers block_training

# Behind Cloudflare, accepting no traffic that skips it
hoststack services update svc_abc --edge-proxy cloudflare --edge-origin-lock

# Everything at a glance
hoststack services security svc_abc

Web application firewall

Every service can put a web application firewall in front of its routes: the OWASP Core Rule Set, which recognises SQL injection, path traversal, remote code execution, PHP and WordPress exploit attempts and scanner probes. It is off by default. Turn it on under Settings → Firewall, with wafMode in the service config, or MCP update_service_config waf_mode. Changes apply on the next deploy.

  • Detect blocks nothing. Every request the rules would have blocked is listed under WAF events on the service, with the rule that fired, for 7 days. Start here: the cost of a firewall is legitimate requests it mistakes for attacks.
  • Block answers those requests with a 403 page that shows the visitor a reference you can look up in WAF events. The URL, query string and headers are judged before the request reaches your app. Request bodies are inspected too, but only reported, never blocked, so an upload or a form post is never held up at the edge.

A rule that fires on your own traffic is a false positive: add its id to wafDisabledRules (the Disable rule button on the event does this).wafParanoia 2 adds stricter rules, which also fire more often on ordinary traffic. A WordPress site, recognised from its image, gets the Core Rule Set's WordPress exclusions, so the editor and the admin work in block mode at paranoia 1. At most 30 events a minute are recorded per service; a scanner sending thousands is still blocked, only not all listed.

For the order the edge applies its limits, Shield, the AI crawler policy and the firewall in, see the WAF & DDoS protection overview.

Shield: flood protection and AI crawlers

Today’s HTTP floods come from botnets of millions of home connections, each sending a few requests a second. The per-address edge limit never fires for any one of them, and most floods are over within ten minutes — before anyone could react. Shield is the edge’s answer: an attack mode that turns on by itself, plus a switch for AI crawlers. Set it on the service’s Settings → Shield tab, with shieldMode, shieldExemptPaths and aiCrawlers in the service config, or MCP update_service_config (shield_mode, shield_exempt_paths, ai_crawlers). Settings apply on the next deploy.

ModeWhat it does
autoThe default, for every service. The edge watches each domain’s request rate against its own normal level. When a domain gets several times its usual traffic (and at least 25 requests a second) from many different networks for 30 seconds, attack mode turns on; it turns off after 15 calm minutes. A flood from a handful of addresses is left to the per-address limit.
onAttack mode on every domain of the service, now, until you change it.
offNever. The normal per-address edge limit still applies.

What visitors see while attack mode is on

A browser opening a page gets a short “Checking your browser” page that runs a proof-of-work check in the background (usually under a second, nothing to click) and then reloads the page. A passed check is remembered for 30 minutes on that domain, in that browser, on that network. The check page answers 403 with X-HostStack-Shield: challenge and is never cached or indexed.

Requests that are not page loads — APIs, webhooks, fetch from scripts and apps — cannot solve a check, so they are never challenged. They are limited to 10 requests a second per address (bursts of 20) instead, and get a 429 with Retry-After above that. A browser that has passed the check keeps your normal limit.

Verified search engines always pass: Googlebot (and Google’s inspection tool), Bingbot, Applebot and YandexBot are proven by a reverse DNS lookup of the address, confirmed by a forward lookup. A request that only claims to be Googlebot in its user agent is treated like any other visitor.

Exempt paths skip Shield entirely — up to 20, each an exact path (/healthz) or a prefix ending in * (/api/webhooks/*). Exempt your webhook receivers: a payment provider’s retries should never meet a rate limit chosen for a flood.

When attack mode turns on or off, the service’s Shield tab records it with the peak request rate and how many addresses were involved, and your team’s notification channels can subscribe to service.shield_engaged and service.shield_released.

AI crawlers

aiCrawlers refuses AI crawlers at the edge with a 403, at no cost to anyone else: there is no extra hop for normal visitors. /robots.txt stays readable for every crawler.

SettingRefused
allowNothing (the default).
block_trainingCrawlers that collect training data: GPTBot, ClaudeBot, anthropic-ai, Claude-Web, CCBot, Bytespider, TikTokSpider, meta-externalagent, FacebookBot, Google-CloudVertexBot, Amazonbot, cohere-training-data-crawler, AI2Bot, Ai2Bot-Dolma, DeepSeekBot, MistralAI-Training, QwenBot, ERNIEBot, Hunyuan, KimiBot, xAI-Grok, PanguBot, YandexAdditional, Diffbot, Timpibot, omgilibot, omgili, Webzio-Extended, ImagesiftBot, img2dataset, LAIONDownloader.
blockThe training crawlers, and AI search crawlers that build answer-engine indexes: OAI-SearchBot, Claude-SearchBot, PerplexityBot, DuckAssistBot, MistralAI-Index, Amzn-SearchBot, GrokBot, Kimi-SearchBot, ExaSearchBot, YouBot, cohere-ai. This also takes your site out of those engines’ answers.

Fetchers that load one page because a person asked for it in a chat — ChatGPT-User, Claude-User, Perplexity-User, Meta-ExternalFetcher, MistralAI-User, Kimi-User, Amzn-User, Google-Agent — behave like a browser and are never refused by this setting. Google-Extended, Applebot-Extended have no crawler of their own (Google and Apple crawl with Googlebot and Applebot), so only a robots.txt rule can opt out of them.

CDN and limits

Every hostname has two limits at the edge, counted per visitor address: a rate limit (100 requests a second, bursts of 200; edgeRateLimitRps) and a cap on requests in flight at once (512 per hostname, a quarter of that per visitor; edgeConcurrencyLimit, 32 to 2,048). Both answer 429. WebSockets, server-sent events and long polls count against the in-flight cap for as long as they stay open, so raise it for a realtime app with many concurrent connections.

When a custom domain is proxied through a CDN, every visitor reaches us from one of the CDN's few addresses, and would share that address's limits. Tell us which CDN under Settings → Custom Domains → Edge Protection (or edgeProxy in the service config, MCP update_service_config edge_proxy) and the edge counts the visitor the CDN reports instead:

CDNVisitor taken fromWhat your app receives
CloudflareCF-Connecting-IPCF-Connecting-IP is the visitor. X-Real-Ip is Cloudflare's address.
Bunny CDNX-Real-IPX-Real-Ip is the visitor, and the left-most X-Forwarded-For entry.

The header is believed only on connections that come from that CDN's published addresses. Anyone else who sends CF-Connecting-IP is counted by their own address, so the header cannot be used to dodge the limit. It applies to custom domains only: your *.hoststack.dev address is never behind your CDN.

Origin lock

A CDN only protects a site if it cannot be skipped. With Only accept traffic from that CDN on (edgeProxyOriginLock), your custom domains answer 403 to every connection that does not come from the CDN, so a request sent straight to our server address with your hostname is refused. Turn it on only after your DNS records are proxied (orange cloud, or the pull zone's hostname): before that, every visitor is refused. Your *.hoststack.dev address is not locked.

Seeing what happened

You do not have to open the settings tabs to know whether protection is doing anything:

  • Service overview has a Security card with the firewall mode and its blocks in the last 24 hours, the Shield mode, the AI-crawler policy and the CDN. While attack mode is on, the service header shows a Shield active badge and the overview says which domains are affected.
  • The team's Monitoring page has an Edge security card listing every public service: what is under attack now, which have no firewall, and the last 24 hours.
  • The activity log records service.shield_on and service.shield_off with the peak request rate and addresses involved, and service.waf_blocked when the firewall blocks a burst.
  • Notifications: Shield turning on, and a firewall burst, appear in the in-app notifications. Notification channels (Slack, Discord, email) can subscribe to service.shield_engaged, service.shield_released and service.waf_blocked.

service.waf_blocked is a digest, not one message per request: it is sent at most once per service per hour, and only once 10 or more requests were blocked within that hour, with the rule and path that fired most. Every individual request stays listed under WAF events for 7 days. Shield notifications are sent at most once per domain and state every 10 minutes, so a flapping domain does not flood a channel.

From a script, hoststack services security, the SDK's services.getSecurity() and the MCP tools get_service_security and get_team_security return the same summary.

Next steps

Was this page helpful?