1.9 KiB
costco-watch
Polls a Costco tire product page on an interval and pushes an ntfy alert when the item flips from out-of-stock to in-stock.
The header gotcha
Costco's bot mitigation silently stalls requests that lack browser
fetch-metadata headers - TLS completes, then nothing comes back, no 403, just a
hang until timeout. Bisecting against the live site showed the gate is the
Sec-Fetch-* family: any single one of Sec-Fetch-Site, Sec-Fetch-Mode
or Sec-Fetch-Dest is enough to get a normal response, even with no
User-Agent at all. User-Agent, Accept and Accept-Language on their own make
no difference.
This service therefore sends a browser header set itself (see
BROWSER_HEADERS in watch.js). Two things that do not work as fixes:
- cloudflared cannot add headers - its
originRequestblock has no such option, it only forwards what the client sent. - Cloudflare Transform Rules cannot set
Sec-*headers - they're on Cloudflare's restricted list, so the edge can't supply the one header family that matters.
TARGET_URL still points at the costco-tire.alexzaw.dev cloudflared ingress
(which sets httpHostHeader: tires.costco.com), but no Cloudflare-side
configuration is required.
Config
| Env | Purpose |
|---|---|
TARGET_URL |
Product page to watch (required) |
ITEM_LABEL |
Friendly name used in notifications |
NTFY_URL / NTFY_TOPIC |
ntfy server and topic (topic required) |
NTFY_TOKEN |
Bearer token, if the topic is protected |
INTERVAL_MINUTES |
Poll interval, default 60 |
API_KEY |
Guards /status and /check |
Endpoints
GET /health- livenessGET /status- current stock state, last check, transition historyPOST /check- force a check now
Etiquette
tires.costco.com/robots.txt allows User-agent: * on all paths, so a personal
low-frequency check is within its terms. Keep the interval at an hour or more.