The EU Accessibility Act (EAA) took effect in June 2025, requiring digital products and services to meet WCAG 2.1 AA standards. This means millions of websites now need compliance checking.
I built an API that does this in one call — send a URL, get back a structured WCAG compliance report with a 0-100 score, violation details, and fix suggestions.
The Architecture
Client → API Gateway → Lambda (arm64) → Puppeteer + axe-core
↕
DynamoDB (cache, usage tracking)
Stack: TypeScript, Puppeteer, axe-core, AWS Lambda, DynamoDB, Terraform
How It Works
- Receive a URL via POST request
- Validate the URL and check for SSRF (block private IPs)
- Check robots.txt (respect site owners)
- Launch headless Chromium, navigate to the page
- Inject axe-core and run accessibility analysis
- Calculate a weighted score and return structured results
The Hard Parts
Chromium on Lambda
Lambda has a 250MB deployment limit, and Chromium is ~62MB compressed. I used @sparticuz/chromium as a Lambda layer, uploaded via S3 since it exceeds the 50MB direct upload limit.
The browser pool pattern is essential — reuse the browser across invocations, but create a fresh page per request:
let browser: Browser | null = null;
async function getBrowser(): Promise<Browser> {
if (browser?.isConnected()) return browser;
browser = await puppeteer.launch({
args: chromium.args,
executablePath: await chromium.executablePath(),
headless: chromium.headless,
});
return browser;
}
SSRF Prevention
Since the API accepts arbitrary URLs, SSRF prevention is critical. I resolve DNS before connecting and block all private IP ranges:
-
127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16 -
169.254.169.254(AWS metadata endpoint) - IPv6 loopback and link-local addresses
axe-core Gotcha
The biggest bug I hit: axe.run('document') treats the string 'document' as a CSS selector, not the document object. You need to pass the actual globalThis['document'] object inside page.evaluate().
Scoring Formula
score = max(0, 100 - critical*10 - serious*5 - moderate*2 - minor*0.5)
Automated tools catch approximately 30-57% of all accessibility issues, so this score is an approximation — manual review is still recommended.
Example Response
{
"score": 72,
"summary": {
"critical": 2,
"serious": 5,
"moderate": 8,
"minor": 3
},
"violations": [
{
"id": "color-contrast",
"impact": "serious",
"wcagCriteria": ["1.4.3"],
"nodes": [{
"selector": ".subtitle",
"fix": "Change text color to at least #767676"
}]
}
]
}
Cost at Scale
| Monthly Scans | AWS Cost |
|---|---|
| 1,000 | ~$1 |
| 10,000 | ~$9 |
| 50,000 | ~$40 |
Running on arm64 (Graviton2) saves ~20% compared to x86.
Try It
The API is available on RapidAPI with a free tier (50 scans/month). If you're working on accessibility tooling or need to integrate WCAG checking into your CI/CD pipeline, give it a try.
Have questions about the implementation? Drop a comment below.
Top comments (0)