Keeping small apps alive

What are security headers? A plain-English guide
Security headers tell the browser how to behave safely — refuse HTTP, block framing, don't guess file types. Here's what each of the ones that matter actually does, and why almost every new site ships without them.

How to add security headers in Next.js (including CSP)
Next.js sends no security headers by default. The static ones are a config block; the hard part is a CSP that helps without blocking your own scripts. Here's the nonce approach, the report-only trick, and the version caveat.

SSL certificate expiry monitoring: why the calendar reminder fails
A calendar reminder assumes your renewal works. But auto-renewal is a background job that fails silently, and cert lifetimes are dropping to 47 days by 2029. Here's why manual SSL tracking breaks, and what to monitor instead.

Secure, HttpOnly, SameSite: the cookie flags that matter
Three small flags — Secure, HttpOnly, SameSite — decide whether your session cookie is a locked token or the easiest thing to steal on your site. Here's what each closes, and how to check which your cookies set.

How do I know if my cron job actually ran?
"It ran" hides four different outcomes and only one is good. Why logs and error emails miss the worst case, and how a success heartbeat catches a job that never fired at all.

Why your Vercel cron job is not running
On Vercel it's rarely a mystery: the Hobby once-a-day cap, jobs that fire within the hour not on the minute, a CRON_SECRET that 401s your own cron, or a schedule that only runs in production.

Why your GitHub Actions scheduled workflow stopped running
GitHub auto-disables a scheduled workflow after 60 days of no repo activity — and the cron's own runs don't count as activity, so a quiet side project always trips it. Here's the rule, the fork gotcha, and the fix.

Why your Supabase pg_cron job isn't running
A pg_cron job that never runs is usually a missing pg_net extension, a UTC-not-local schedule, or a run that fired and failed. Here's how to read the run log Postgres keeps and find which is yours.

What a dead man's switch is (and when your app needs one)
Most monitoring watches for something bad happening. A dead man's switch watches for something good not happening — the right tool for backups and cron jobs that fail silently. What it is, and when you need one.

Stripe webhooks failing silently — and how to catch it
The payment succeeds even when the webhook fails, so nothing looks broken until a customer writes in. The real retry-and-disable timeline, the raw-body trap, and how to catch it inside the window.

Stripe webhook signature verification failed: the raw-body fix
"No signatures found matching the expected signature" almost always means a body parser changed the request bytes before verification ran. Here's what Stripe actually compares, and the raw-body fix for each framework.

Stripe webhook duplicate events: stop the double charge
Stripe's delivery is at-least-once, so the same event can arrive twice and charge a customer twice. Here's why it happens, and how to make your handler idempotent with the event id so running it twice is safe.

Stripe subscription not activating after payment
Paid but still not upgraded? The subscription's status splits it in two: stuck in 'incomplete' means the payment never finalized; active-in-Stripe-but-not-your-app means a webhook never landed. Here's how to tell which.

How to test Stripe webhooks locally (and what it won't prove)
The Stripe CLI brings events to your machine — stripe listen forwards them, stripe trigger fires them. Here are the commands, the signing-secret gotcha, and why passing locally doesn't prove the live endpoint works.

Is my website down, or is it just me?
Before you panic-redeploy at midnight: is the site actually down, or just down for you? A 30-second test, what each answer means, and why one check from your laptop can't really tell you.

My website is down — how to fix it
Don't randomly restart things — work a short list in order, cheapest check first. Confirm it's really down, read the error, rule out an expired cert, check your last deploy and your host. The calm playbook for a site that's down.