---
title: "Security Essentials: Rate Limits, CSP Headers, Key Encryption, Pre-Public Repo Audits"
description: "Apply core security measures so your app resists abuse and never leaks sensitive data"
type: "lesson"
locale: "en"
course: "Quality, Security and the Agent-First Business"
number: "5.2"
canonical: "https://agenticschool.dev/courses/quality-security-agent-first/security-essentials-rate-limits-csp-headers-key-encryption-pre-public-repo-audits"
datePublished: "2026-06-12"
dateModified: "2026-06-12"
---

# Security Essentials: Rate Limits, CSP Headers, Key Encryption, Pre-Public Repo Audits

- Course: Quality, Security and the Agent-First Business
- Lesson: 5.2
- Duration: 26 min
- Level: technik
- Status: published
- Canonical URL: https://agenticschool.dev/courses/quality-security-agent-first/security-essentials-rate-limits-csp-headers-key-encryption-pre-public-repo-audits
- Locale: en

> Apply core security measures so your app resists abuse and never leaks sensitive data

## Summary

Security is not optional once real users arrive. This lesson covers the essentials every app needs: rate limits on every public endpoint, a Content Security Policy to harden the browser, encrypting stored credentials, audit logs, and a pre-public checklist so a repo never goes public with a secret buried in its history.

## What you learn

- Rate limiting every public endpoint to stop brute force, spam and bill-draining abuse
- CSP headers, encrypting stored credentials, and keeping an audit log
- A pre-public repo checklist so secrets in history never go public

## Summary

The moment your app is on the public internet, it is being probed by bots within minutes. You do not need a security team to be safe against the common attacks, you need a handful of essentials applied consistently: cap every public endpoint with a rate limit, lock down what the browser will run with a CSP, encrypt the credentials you store, log who did what, and never flip a repo public without auditing its history for secrets. This lesson makes each of those concrete.

## What you will learn

You will learn to add rate limits to public endpoints, set a Content Security Policy header, encrypt stored credentials at rest, keep a useful audit log, and run a pre-public repo audit that catches secrets hiding in old commits. These are the measures that separate a hobby project from something you can responsibly point real users at.

## Prerequisites

The secrets lessons from Courses 1 and 3, since several measures here build on keeping secrets in environment variables and out of code. A deployed app with at least one public endpoint or form makes the lesson concrete, but you can apply everything to your next project from the first commit.

## The problem

Three failures hurt small builders again and again. First, an unprotected endpoint gets hammered - a login form brute-forced, a contact form spammed, or an AI endpoint called in a loop until your provider bill explodes. Second, a stored API key or password sits in plain text, so a single database leak hands an attacker everything. Third, and most common, a founder makes a repo public to share it and a secret committed two years ago is now on the open internet forever. Each is preventable with one habit.

## Rate limit every public endpoint

A rate limit caps how often a single client can hit an endpoint in a window of time. It is the cheapest, highest-value security control you have, and any endpoint a stranger can reach needs one: login, signup, password reset, contact forms, and above all any endpoint that costs you money per call, like one that triggers an LLM. Limit by IP for anonymous traffic and by user or API key for authenticated traffic. When the limit is exceeded, return a 429 and stop.

```typescript
// A minimal fixed-window limiter. In production use a shared store
// (Redis, Upstash, or your DB) so it works across multiple servers.
const hits = new Map<string, { count: number; resetAt: number }>()

export function rateLimit(key: string, max = 10, windowMs = 60_000) {
  const now = Date.now()
  const entry = hits.get(key)
  if (!entry || now > entry.resetAt) {
    hits.set(key, { count: 1, resetAt: now + windowMs })
    return { ok: true }
  }
  if (entry.count >= max) return { ok: false, retryAfter: entry.resetAt - now }
  entry.count += 1
  return { ok: true }
}

// In your handler:
// const limit = rateLimit(`login:${ip}`, 5, 60_000)
// if (!limit.ok) return new Response('Too many requests', { status: 429 })
```
A rate limiter sketch - cap by IP for anonymous traffic, by user or key for authenticated

## CSP headers and encrypting stored credentials

A Content Security Policy tells the browser exactly which sources of scripts, styles and images it is allowed to load. It is your strongest defence against cross-site scripting: even if an attacker injects a script tag, the browser refuses to run it because the source is not on your allowlist. Start strict and loosen only what you must. Separately, anything sensitive you store - a third-party API key, an OAuth token, a user secret - should be encrypted at rest, so a database breach yields ciphertext, not working credentials. The encryption key itself lives in an environment variable, never in the database it protects.

```text
Content-Security-Policy: default-src 'self';
  script-src 'self';
  style-src 'self' 'unsafe-inline';
  img-src 'self' data: https:;
  connect-src 'self' https://api.your-provider.com;
  frame-ancestors 'none';
  base-uri 'self'
```
A starting CSP - self by default, then allowlist only what your app genuinely needs

- default-src 'self' means: unless stated otherwise, only load from your own domain.
- connect-src controls which APIs the browser may call - list your real backends only.
- frame-ancestors 'none' stops other sites embedding yours in an iframe (clickjacking).
- Encrypt stored secrets with a library, not by hand; keep the key in an env var, rotate it if it ever leaks.

## Audit logs: know who did what

When something goes wrong - a refund issued, an account deleted, a plan downgraded - you need to answer "who did this, when, and from where". An audit log is an append-only record of sensitive actions: the actor, the action, the target, a timestamp and the source IP. It is not the same as your application logs; it is a deliberate trail for security and accountability. Log every action that moves money, changes permissions, or touches another user's data. Never log the secrets themselves, only that the action happened.

## The pre-public repo checklist

This is the one that bites hardest. Deleting a key from your latest commit does not remove it from history - it is still sitting in an old commit that anyone can read the moment the repo goes public. Before you flip any repo from private to public, walk this checklist every time. If you find a secret in history, the only safe move is to treat that secret as compromised: rotate it immediately, then scrub history.

- Scan the full history for secrets, not just the current files. Use a tool like gitleaks or trufflehog: gitleaks detect --source . catches keys, tokens and passwords across every commit.
- Confirm .env and every secret file are in .gitignore and were never committed.
- Check for hardcoded keys, internal URLs, customer data and credentials in code and comments.
- If a secret was ever committed: rotate it first (assume it is already public), then rewrite history to remove it before going public.
- Only after a clean scan do you flip the repo to public.

```bash
# Audit the whole git history for leaked secrets before going public
gitleaks detect --source . --verbose

# If anything is found, ROTATE the secret first, then scrub history.
```
Run this before every private-to-public flip - history outlives a deletion

## Typical mistakes

The recurring ones: shipping an AI or email endpoint with no rate limit and waking up to a five-figure bill; storing third-party tokens in plain text so one leak compromises every connected account; skipping the CSP because it is fiddly, then eating an XSS; and the classic - flipping a repo public to "share the project" without auditing history, leaking a key that has been there for months. Every one is prevented by a habit in this lesson.

## Business ROI

Security is invisible when it works and catastrophic when it does not. A leaked key can drain a budget overnight, a breach of stored credentials can end a business, and a single public-repo leak has cost founders real money and real customers. The essentials here cost you an afternoon to set up and then run forever. For an agent-driven team this matters double: agents generate endpoints fast, so the discipline of "every public endpoint gets a rate limit, every secret gets encrypted, every repo gets audited" has to be a rule the agent follows, not a thing you remember.

## Checklist

Confirm each of these before you point real users at anything you have built.

- Every public and money-spending endpoint has a rate limit returning 429 when exceeded.
- A Content Security Policy header is set and as strict as your app allows.
- Stored credentials and tokens are encrypted at rest, with the key in an env var.
- You ran a full-history secret scan before any repo went public, and rotated anything found.

## Resources

Keep gitleaks or trufflehog installed and add the history scan to your pre-public ritual. The OWASP Top 10 and MDN's CSP reference are the timeless sources when you need depth. Put "rate limit this endpoint" and "encrypt this stored secret" into your project rules so agents apply them without being asked.

## Your task

Add a rate limit to one real endpoint and confirm it returns 429 when you exceed it. Set a CSP header on your app and fix whatever it breaks until the page works under a strict policy. Then run gitleaks against one of your repos and read the output - even a clean result teaches you to trust the check before you ever go public.

## Next lesson

Secure and hardened, the next lesson covers legal and compliance: GDPR, lawful cookie consent including Global Privacy Control, and the US and Swiss privacy laws, explained without the headache.

## Transcript

This lesson is a written, text-first guide. Security is not optional once real users arrive. This lesson covers the essentials every app needs: rate limits on every public endpoint, a Content Security Policy to harden the browser, encrypting stored credentials, audit logs, and a pre-public checklist so a repo never goes public with a secret buried in its history. You will apply core security measures so your app resists abuse and never leaks sensitive data. Work through the sections in order, try the task at the end in a real project, and move on once it works for you. There is no video required - everything you need is in the written steps above.
