Security for a small team shipping to the public web

Technology · 2026-05-19 · 8 min read · 1893 words

By ESPYCRUX, Studio

Security advice is mostly written for organisations with a security team. This is the version for a studio of a few people running a handful of public sites: what to protect first, why the domain registrar outranks the server, and what a small team realistically cannot do.

Security stopped being an IT problem some years ago, and the reason is not that attacks got cleverer. It is that the consequences moved. A breach used to cost you a weekend of cleanup and an apologetic email. Now it can put you in breach of a data protection regime, in breach of a client contract that quietly obliged you to notify within a fixed window, and on the wrong end of a search result that outlives the incident by years. The technical damage is usually the smallest part of the bill.

That change is why security ended up on boards. It is also why it ended up on the desks of people like us — a small studio with no board and no security team. We run six public properties: a main site, a game hub with five original browser games, a general-interest blog, a tools site carrying twenty-six client-side utilities, and three more still being built. Nobody here has "security" in their job title. The work still has to happen.

What actually comes for a small studio

There is a persistent idea that small businesses are ignored because there is nothing worth taking. The opposite is true, and for an unflattering reason: small operations are targeted because they are assumed to be unprotected. You are not being singled out. You are being swept up by something that scans the whole internet and stops wherever it finds a door open.

Which means the realistic threat model is dull. Almost nothing that hits a studio our size is bespoke. It is:

None of that requires a sophisticated attacker. All of it is preventable with attention rather than budget, which is the good news buried in the threat model. The bad news is that attention is the scarcest thing a small team has, so the ordering matters more than the list.

We order by risk removed per hour spent. Not by what feels rigorous.

The registrar is the worst thing to lose

Start here, because it is under-discussed and it is the top of the tree.

If someone takes your server, you have a bad week. You rebuild from your repository, restore what you can, and you are back. If someone takes your domain registrar account, they own your identity on the internet. They can point your domain anywhere, issue valid TLS certificates for it, receive your email, and use those to reset every other account you own. Recovering a stolen domain is a legal and administrative process conducted with a company that has no particular reason to hurry.

The hierarchy runs downhill from there, and it is worth being explicit about it:

AccountWhat losing it costs youRecovery
Domain registrarEverything below it. Domain redirected, certificates issued, email intercepted, other accounts reset via your own domainSlow, adversarial, sometimes months
DNS provider (if separate)Traffic and mail redirected without touching the domain recordFast once you regain access, but detection is often slow
Primary emailThe reset channel for nearly every other service you useProvider-dependent, often painful
Hosting / deploy platformSite content replaced, deploy pipeline used to ship malicious codeUsually recoverable from source control
Source controlCode exposed, and any secrets committed to it exposed with itRecoverable, but disclosure is permanent
Analytics, CMS, everything elseNuisance, occasionally embarrassingStraightforward

The practical instruction is short. Hardware-key or app-based multi-factor on the top four rows, without exception. Not SMS, because SIM swapping is a real and unglamorous attack. Turn on registrar lock so the domain cannot be transferred out without deliberate action. And make sure the registrar contact address is not hosted on the domain itself, which is a circular dependency that becomes obvious at the worst possible moment.

Then use a password manager. This is the highest-leverage hour in the whole article, because credential reuse is the most common route in and a manager removes the entire category rather than mitigating it. Unique passwords everywhere, generated, never typed.

Dependencies, and the tree you did not plant

A JavaScript project has a supply chain whether you think about it or not. You install eleven things; you get four hundred. Most of them are maintained by people you have never heard of, some by nobody at all, and any one of them can ship a version that steals environment variables during postinstall.

You cannot audit four hundred packages. Nobody can. What you can do is reduce and observe.

Reduce means treating a new dependency as a decision rather than a convenience. A package that saves you thirty lines is usually not worth a permanent supply-chain relationship. On our tools site this is easier than it sounds, because a utility that runs entirely client-side and does one thing rarely needs much beyond the platform. Small scope pays a security dividend that nobody budgets for.

Observe means the automated basics: npm audit in CI so a known vulnerability fails the build, Dependabot or its equivalent raising the upgrade for you, and a committed lockfile so the thing you tested is the thing you ship. Pin versions. Do not let a build in six months quietly resolve a different tree than the build today.

One more habit, cheap and rarely done: before adding a package, look at when it was last published, how many maintainers it has, and whether it has install scripts. It will not catch a determined attack, but it catches the abandoned and the odd, which is most of what there is.

The data you never collected cannot leak

This is the control that outperforms every other control, and it is architectural rather than operational.

Our tools site does not send user input to a server. Not as a policy, as a design: the processing happens in the browser and there is no endpoint to send it to. The consequence is that there is no database of pasted content to breach, no backup of it to misplace, no retention period to define, no subject access request to service, and no breach notification to make about it. A tool that never receives your JSON cannot leak your JSON.

The same reasoning applies further down. No accounts means no password database, and no password database means no password database breach. Analytics that do not set identifying cookies produce a smaller obligation than analytics that do. Every field you decide not to collect is a row of future risk deleted in advance.

The instinct to collect "in case it is useful later" is the instinct that produces most of the data now sitting in breach dumps. Ask what you would actually do with a field. If the answer is vague, do not collect it.

Secrets, and where they must never be

There is one rule here and it admits no exceptions. Anything in client-side code is public. Not obscured, not compiled away, not safe because it is minified. Public.

That covers API keys in a bundled JavaScript file, tokens in a NEXT_PUBLIC_ variable, credentials in a config object, and anything committed to a repository even briefly. A secret pushed to git and removed in the next commit is still a leaked secret, because the history retains it and the history gets cloned. Rotate it rather than deleting it.

If a third-party service genuinely needs to be called from the browser, use a key scoped and domain-restricted for that purpose, and treat it as public because it is. If it cannot be scoped, it belongs behind a small server-side function and nowhere else.

Keeping the surface small

Every deployed thing is a thing that can be wrong. The admin panel you set up once, the staging site indexed by search engines, the subdomain pointing at a service you cancelled. Dangling DNS records pointing at deprovisioned services are one of the more common ways small sites get hijacked, and they exist purely through neglect.

So: delete what you do not use. Take down the staging environment when the feature ships. Remove the DNS record when you remove the service. Keep an actual list of what you run and what it points at, and read it once a quarter. It is a boring half hour and it is worth more than most tooling.

The standard hardening on what remains is cheap and should just be on. HTTPS everywhere with HSTS. A content security policy tight enough to matter. Subresource Integrity on any third-party script you cannot avoid, and avoid as many as you can. Automatic security updates where the platform offers them.

The day something goes wrong

You will not think clearly during an incident. That is the whole argument for writing things down beforehand.

What you need is not an incident response framework. It is a single page, stored somewhere that does not depend on your own infrastructure, containing: who to contact at the registrar, host, and email provider; how to revoke every deploy token and API key; where the backups are and when someone last tested restoring one; and what your contractual notification obligations are, in days, for each client who imposed one. That last item is the one people discover mid-incident, which is the worst time to read a contract.

Test the restore. An untested backup is a belief, not a backup.

What we cannot do, plainly

Being honest about this is more useful than pretending otherwise.

We do not run a security operations centre and nobody is watching logs at three in the morning. We do not commission penetration tests on a schedule. We cannot meaningfully defend against a targeted attacker with time and resources, and neither can any team our size. We do not read the source of every dependency we install, and any studio claiming otherwise is describing an aspiration.

We are not trying to be unbreakable. We are trying to not be the easy one, and to be recoverable when something does go wrong. Those are achievable. The first is not.

The checklist, in order

  1. Password manager, unique credentials everywhere. One afternoon. Removes the most common route in.
  2. Hardware-key or app-based MFA on registrar, DNS, email, hosting. Not SMS.
  3. Registrar lock on, and registrar contact email not hosted on the domain it protects.
  4. Audit who has access to what, and remove everyone who no longer needs it. Old collaborators, old tokens, old integrations.
  5. Lockfile committed, npm audit in CI, automated dependency updates enabled.
  6. Grep the repository and the client bundle for anything that looks like a key. Rotate what you find.
  7. Delete unused subdomains, staging environments and dangling DNS records.
  8. Reduce what you collect. Every field removed is risk removed permanently.
  9. HTTPS, HSTS, CSP, automatic updates.
  10. Write the one-page incident sheet, and restore a backup once to prove you can.

The first three cover most of the realistic risk. If you only ever do those, you are meaningfully ahead of where you were, and that is the honest measure of security work on a small team: not whether it is complete, but whether the likely things have been closed.

Tags: security, small teams

ESPYCRUX — ESPYCRUX is a small product studio based in India, building focused web applications and writing about the engineering behind them. Articles are written by whoever did the work, and published under the studio name. Reach the studio at admin@espycrux.com.