Security & Rate Limiting
CourseWeave has two browser-facing endpoints that accept user input:
- Report Issue / Session Reflection β student-facing, posts to a Cloudflare Worker β GitHub Issues
- Content Creator (Alt+C) β teacher-facing, posts to a Cloudflare Worker β GitHub Issues
Both sit behind a layered defence: client-side rate limiting, a honeypot field, and a server-side backstop at the Cloudflare Worker.
Why not IP-based rate limiting?
Section titled βWhy not IP-based rate limiting?βIP-based rate limiting is the default in most tutorials, but it breaks in a university context: all students on campus or in a dormitory typically share one or a few NATβd IP addresses. One student submitting ten feedback items would block the entire cohort for an hour.
Cloudflare Turnstile / reCAPTCHA were also considered and rejected:
- reCAPTCHA is blocked in China.
- Cloudflare Turnstileβs challenge endpoint (
challenges.cloudflare.com) is reachable from mainland China in most cases but is not guaranteed reliable across all campus networks. - A CAPTCHA that silently fails blocks the form entirely. Students behind university proxies or VPNs (common in China) are the most likely to trigger false positives.
For a course feedback form, the threat model does not justify that failure mode.
Client-side rate limiting (localStorage)
Section titled βClient-side rate limiting (localStorage)βRate limits are enforced in the browser using localStorage, keyed per device. Timestamps older than 24 hours are pruned on each check so storage never accumulates.
| Form | Key | Limit |
|---|---|---|
| Report Issue (general feedback) | courseweave_rl | 10/hour, 30/day |
| Session Reflection (CIQ) | courseweave_rl (same log, category: 'reflection') | 2/day |
| Content Creator | courseweave_cc_rl | 20/hour, 50/day |
When a limit is exceeded:
- Report Issue β the form switches to the error panel with a friendly explanation.
- Content Creator β a toast notification appears in the top-right corner.
The limits are defined as named constants at the top of each componentβs <script> block, making them straightforward to adjust:
const RL_LIMIT_HOUR = 10;const RL_LIMIT_DAY = 30;const RL_LIMIT_CIQ_DAY = 2;
// ContentCreatorBar.astroconst CC_RL_LIMIT_HOUR = 20;const CC_RL_LIMIT_DAY = 50;Honeypot fields
Section titled βHoneypot fieldsβBoth forms include a visually hidden input that is never filled by a real user but will be filled by simple bots that blindly populate all fields. If the honeypot field is non-empty on submission, the request is silently dropped (for bots) or a fake success is shown (for the Report Issue form, to avoid tipping off scrapers).
<!-- Hidden from real users, visible to bots --><input type="text" name="website" tabindex="-1" style="position: absolute; left: -9999px;" autocomplete="off" aria-hidden="true" />Cloudflare Worker (server-side backstop)
Section titled βCloudflare Worker (server-side backstop)βThe client-side limits are the primary guard. The Cloudflare Worker provides a high-threshold backstop against scripted abuse that bypasses the browser entirely (e.g., direct curl requests to the worker URL).
Recommended Worker configuration:
| Setting | Value | Reason |
|---|---|---|
| IP rate limit | 200β500 requests/hour | High enough that a legitimate classroom session never hits it; low enough to block sustained scripted attacks |
| Allowed origins | Your siteβs domain only | Rejects requests not originating from the course site |
| Secret header (optional) | Static token in config.ts | Cheaply blocks direct API abuse without user friction |
The Worker rate limit is intentionally set high because the client-side limits handle the normal case. Lowering the Worker limit risks re-introducing the shared-IP problem the client-side approach was designed to avoid.
Summary
Section titled βSummaryβReal user β honeypot check β localStorage rate limit β Cloudflare Worker β GitHub Issues APIBot β honeypot check β silently dropped / fake successScripter β (bypasses browser) β Cloudflare Worker IP limit β blockedNo CAPTCHA, no IP blocking of shared addresses, no dependency on services blocked in China.
Was this page helpful?