The Problem
Paywalled articles and CORS-restricted resources break developer tools, scrapers, and research workflows. Existing services like 12ft.io are hosted and unreliable. Ladder gives you a self-hosted HTTP proxy that strips CORS headers, rewrites HTML, and applies per-domain rulesets so you can inspect how sites serve content under different client conditions.
What This Does
Ladder is a Go HTTP proxy. It intercepts requests, applies domain-specific rulesets from rulesets/ (e.g. rulesets/us/nytimes-com.yaml), modifies request/response headers, and injects or removes HTML/CSS/JS. It supports custom User-Agent, X-Forwarded-For spoofing, FlareSolverr integration for Cloudflare-protected sites, and exposes a REST API plus raw HTML fetching.
The frontend is a minimal React/Tailwind dashboard (tailwind.config.js, styles/input.css) served alongside the proxy. Deployment options include Docker, docker-compose, Helm chart, and prebuilt binaries via GoReleaser.
How It Is Wired
Execution starts in cmd/main.go at main (line 25), which routes to ProxySite in handlers/proxy.go. That function is the hub: it's called from 3 places, and it calls NewRuleset (the most-connected function, called from 4 places) to load rules, then fetchSite to hit the target URL. fetchSite checks domain allowlists via StringInSlice and retrieves the applicable rule.
The ruleset system lives in pkg/ruleset/ruleset.go — 12 functions including NewRuleset, loadRulesFromLocalDir, and loadRulesFromRemoteFile. It supports merging rulesets via handlers/cli/cli.go (HandleRulesetMerge, which calls gzipMerge/yamlMerge). The API handler in handlers/api.go exposes JSON endpoints, and handlers/raw.go returns unmodified HTML.
The call graph shows no circular dependencies — 13 code files, 0 cycles. The highest blast radius is NewRuleset (4 callers) and ProxySite (3 callers); changing their signatures breaks most of the proxy path. handlers/cli/cli.go has high branching density (18 branch points over 51 lines), making it the least maintainable spot.
How To Use It
Setup: go build -o ladder cmd/main.go or docker build -t ladder . (Dockerfile present).
Configuration: Set LADDER_PORT (default 8080), LADDER_BASIC_AUTH for auth, and LADDER_RULESET_DIR to point at rulesets/. FlareSolverr is optional via LADDER_FLARESOLVERR_URL.
Running: ./ladder -r ruleset.yaml (per README). For Docker: docker compose up -d using docker-compose.yaml. The proxy listens on port 8080; browse to http://localhost:8080/https://example.com to proxy a URL.
Real-World Use
A QA engineer testing paywall behavior across user agents:
curl -x http://localhost:8080 -H "User-Agent: Googlebot" \
-H "X-Forwarded-For: 66.249.66.1" \
https://www.nytimes.com/article.html
The response comes back with CORS headers stripped, CSP removed, and the rulesets/us/nytimes-com.yaml modifications applied — without touching the production site.
Code Health & Issues
Static analysis (not opinion) found 8 issues, 3 high severity:
- High – CI runs no tests despite 4 test files existing. A green check means nothing.
- High – GitHub Actions pinned to mutable tags (
pnpm/action-setup@v6,goreleaser/goreleaser-action@v7,sigstore/cosign-installer@main) — supply-chain risk. - High –
build-css.yamlpushes directly to the default branch with no test gate. - Medium – Docker base images (
golang:1.26,gcr.io/distroless/static-debian13:nonroot) not pinned by digest. - Medium – No dependency vulnerability scan in CI.
- Medium – Checkout leaves
persist-credentials: trueinbuild-css.yaml. - Medium – No non-root
USERin Dockerfile. - Low – No
timeout-minuteson workflow jobs.
The Bottom Line
Ladder is a solid, well-structured Go proxy for legitimate paywall testing and CORS debugging. The core proxy path is clean with no circular dependencies. The CI/CD pipeline is the weak point — it can ship untested, unpinned builds. Use it if you need a self-hosted content-testing proxy and are willing to harden the build pipeline yourself.