feat(webapp): serve robots.txt disallowing /api/ and /n5Data/ - #2030
Open
jcschaff wants to merge 1 commit into
Open
feat(webapp): serve robots.txt disallowing /api/ and /n5Data/#2030jcschaff wants to merge 1 commit into
jcschaff wants to merge 1 commit into
Conversation
A crawler walking /api/v0/biomodel/ links exhausted the heap of two prod api pods on 2026-08-22 (#2021). The /api/ paths are machine interfaces returning JSON and VCML -- useless as search results, and not cheap: one request to /api/v0/biomodel/{id}/simulation/{id} can parse a multi-megapixel geometry. Disallow rather than Crawl-delay. Crawl-delay is not part of the robots.txt standard and Google ignores it outright, so it would read as a control while being none. A slower crawl would also still pay the full parse cost per request; the non-fatal repeat of the incident was a single request taking 4 seconds on its own. /n5Data/ is the S3 proxy serving data blobs, equally not worth indexing. The web application at / stays crawlable. This is a request, not a control -- it only binds compliant crawlers. PetalBot, the one observed, advertises a webmaster control page in its user agent, so it is the compliant kind. The ingress-side rule for crawlers that ignore robots.txt is a separate change in vcell-fluxcd. Listed in angular.json assets rather than dropped in src/assets: the latter would publish it at /assets/robots.txt, which no crawler reads. Both build configurations needed the entry. Verified rather than assumed -- a production build emits dist/login-demo/robots.txt, which Dockerfile-webapp copies to /usr/share/nginx/html and nginx serves from location / via try_files $uri. Worth noting the fallback in that try_files means that WITHOUT this file a request for /robots.txt returns index.html with a 200, so a crawler was being handed the SPA and parsing it as robots.txt. Refs #2021, #2025 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018kr8SbzXtwW3gMVUgMfDDt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
First half of the crawler defence for #2021. The ingress-side rule for crawlers that ignore
robots.txt is virtualcell/vcell-fluxcd#52.
Why
A crawler walking
/api/v0/biomodel/{id}→/api/v0/biomodel/{id}/simulation/{id}links exhaustedthe heap of two prod api pods on 2026-08-22. Those paths are machine interfaces returning JSON and
VCML — useless as search results, and not cheap: one request can parse a multi-megapixel geometry.
Disallow, not Crawl-delay
Crawl-delayis not part of the robots.txt standard and Google ignores it outright, so it wouldread as a control while being none. And a slower crawl still pays the full parse cost per request —
the non-fatal repeat of the incident was a single request taking 4 seconds on its own.
/n5Data/is the S3 proxy serving data blobs. The web application at/stays crawlable.This is a request, not a control — it binds only compliant crawlers. PetalBot, the one observed,
advertises a webmaster control page in its user agent, so it is the compliant kind. That's why it
pairs with the ingress rule rather than replacing it.
One detail worth knowing
It's listed in
angular.jsonassets rather than dropped insrc/assets— the latter wouldpublish it at
/assets/robots.txt, which no crawler reads. Both build configurations needed theentry.
And there's a pre-existing wrinkle this fixes:
nginx-custom.confserveslocation /withtry_files $uri $uri/ /index.html, so without this file a request for/robots.txtreturnedindex.htmlwith a 200 — crawlers were being handed the SPA and parsing it as robots.txt.Verified rather than assumed
Ran a production build:
dist/login-demo/robots.txtis emitted at the dist root, whichDockerfile-webappcopies to/usr/share/nginx/htmland nginx serves fromlocation /viatry_files $uri.Refs #2021, #2025
🤖 Generated with Claude Code
https://claude.ai/code/session_018kr8SbzXtwW3gMVUgMfDDt