Skip to content

feat(webapp): serve robots.txt disallowing /api/ and /n5Data/ - #2030

Open
jcschaff wants to merge 1 commit into
masterfrom
ops/crawler-robots-txt
Open

feat(webapp): serve robots.txt disallowing /api/ and /n5Data/#2030
jcschaff wants to merge 1 commit into
masterfrom
ops/crawler-robots-txt

Conversation

@jcschaff

Copy link
Copy Markdown
Member

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 exhausted
the 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-delay is not part of the robots.txt standard and Google ignores it outright, so it would
read 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.

User-agent: *
Disallow: /api/
Disallow: /n5Data/

/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.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.

And there's a pre-existing wrinkle this fixes: nginx-custom.conf serves location / with
try_files $uri $uri/ /index.html, so without this file a request for /robots.txt returned
index.html with 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.txt is emitted at the dist root, which
Dockerfile-webapp copies to /usr/share/nginx/html and nginx serves from location / via
try_files $uri.

Refs #2021, #2025

🤖 Generated with Claude Code

https://claude.ai/code/session_018kr8SbzXtwW3gMVUgMfDDt

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant