Skip to content

Dev pages demonstrating the ENCODE WAF CAPTCHA gate - #2090

Merged
turner merged 1 commit into
masterfrom
dev-encode-captcha-pages
Aug 4, 2026
Merged

Dev pages demonstrating the ENCODE WAF CAPTCHA gate#2090
turner merged 1 commit into
masterfrom
dev-encode-captcha-pages

Conversation

@turner

@turner turner commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Why

ENCODE fronts www.encodeproject.org with an AWS WAF that challenges browser-looking requests, returning 405 Method Not Allowed with x-amzn-waf-action: captcha. The status code is misleading — nothing about the request used the wrong verb — so the failure sends you looking in the wrong place. The practical cost is that no igv.js-based app can load ENCODE tracks during local development, and we can't reproduce user-reported bugs involving ENCODE data on our own machines.

Everything written about this so far has been about a status code and a header. Neither of us had actually seen the CAPTCHA.

What's here

dev/encode/see-the-captcha.html — presents the challenge to a human. The key finding: the WAF challenges a plain top-level navigation, with no Origin header, no localhost, and no igv.js involved. Clicking a link to an ENCODE bigWig renders a live AWS WAF "Human Verification" page with a solvable puzzle widget loaded from captcha.awswaf.com. The page offers three views:

  1. A button opening the ENCODE file in a new tab — the challenge, straight up
  2. The same challenge rendered inline in an iframe (the response sets no X-Frame-Options and no framing CSP)
  3. A button dumping the raw 2119 bytes of challenge HTML that a bigWig reader receives in place of binary data — which is why the reader reports a bad magic number rather than "you were blocked"

dev/encode/encode-captcha-gate.html — the diagnostic view. Raw range-request probes against two gated ENCODE bigWigs plus an ungated control, the same URLs loaded through igv.js so the partial-page failure is visible, and a copy-pasteable curl repro built from the page's own origin.

Measurements against the live host

browser UA, NO Origin (i.e. clicking a link)  → 405 + x-amzn-waf-action: captcha
browser UA, no Origin, + navigation Sec-Fetch → 405
browser UA + localhost Origin (the xhr case)  → 405
plain curl (honest User-Agent)                → 307

One correction to the internal write-up this came from: the challenge response is fully readable from page JavaScript. It carries access-control-allow-origin: * and, notably, access-control-expose-headers: x-amzn-waf-action. This is not a CORS failure, and reporting "this data provider blocked the request" instead of "405" needs no server-side cooperation — the honest signal is already reachable in the browser.

Also

Adds an 'encode' -> 'ENCODE' section label to scripts/buildDevDashboard.cjs and regenerates dev/dev.html.

Testing notes

Use a private/incognito window. Solving a WAF challenge earns an aws-waf-token for the host that waves you through afterward, so a repeat visit may hand you a file download instead of the puzzle. Both pages warn about this.

These are dev pages only — no library code changes.

🤖 Generated with Claude Code

ENCODE fronts www.encodeproject.org with an AWS WAF that challenges
browser-looking requests, returning 405 with x-amzn-waf-action: captcha.
This blocks any igv.js-based app from loading ENCODE tracks during local
development. Two dev pages make the failure observable.

dev/encode/see-the-captcha.html presents the challenge to a human: the
WAF challenges a plain top-level navigation, with no Origin header and no
igv.js involved, so clicking a link to an ENCODE bigWig renders a live,
solvable AWS WAF puzzle. Also embeds it inline (the response sets no
X-Frame-Options and no framing CSP) and dumps the raw challenge HTML that
a bigWig reader receives in place of binary data.

dev/encode/encode-captcha-gate.html is the diagnostic view: raw range-request
probes against two gated ENCODE bigWigs plus an ungated control, the same
URLs loaded through igv.js so the partial-page failure is visible, and a
copy-pasteable curl repro built from the page's own origin.

Measured against the live host: the challenge response carries
access-control-allow-origin: * and access-control-expose-headers:
x-amzn-waf-action, so page JavaScript can read both the status and the
header naming the cause. Reporting "the provider blocked this request"
instead of "405" needs no server-side cooperation.

Also adds an 'encode' -> 'ENCODE' section label to the dev dashboard
generator and regenerates dev/dev.html.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@turner
turner merged commit 87c1cc5 into master Aug 4, 2026
0 of 2 checks passed
@turner
turner deleted the dev-encode-captcha-pages branch August 4, 2026 14:53
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