Development Tips

Website Security Checklist for Small Business Sites (2026)

By Ken Key Jun 7, 2026 7 min read Updated Oct 16, 2026

Website Security Checklist for Small Business Sites (2026)
The short answer

A small business security checklist covers forced HTTPS, security headers, blocked directory listings, patched plugins and themes, offsite backups you actually test, least-privilege logins, and MFA on every admin account. Run it quarterly. Most breaches I see come from stale plugins and backups that quietly stopped running.

Part of the complete web development guide.

On this page

Most small business sites don’t get hacked by geniuses. They get hacked because a plugin was 14 months out of date, or a backup job silently stopped running in 2023, or the contact form was piping user input straight into a database query. A security audit is just a checklist run on a schedule. Here’s the one I actually use, and you can run most of it yourself in an afternoon.

Start with what you can see from the outside

Before you touch a line of code, look at the site the way an attacker does. No login required. These checks take about ten minutes and they catch the embarrassing stuff.

  • Is HTTPS forced? Visit http://yoursite.com. It should redirect to HTTPS, not just load over both. Mixed content warnings in the console mean some asset is still loading over plain HTTP.
  • What does the security headers report say? Run the domain through a header checker. You’re looking for Strict-Transport-Security, Content-Security-Policy, X-Content-Type-Options, and Referrer-Policy. Missing headers aren’t a breach by themselves, but they’re free wins.
  • Are directory listings off? Try hitting a folder like /uploads/ or /wp-content/uploads/. If you get a browsable file index, that’s a config problem, and it’s how people find your backup files.
  • Is anything sensitive publicly reachable? Try /.env, /.git/config, /backup.zip, /wp-config.php.bak. I find live .env files on small business sites more often than I’d like. If those return content instead of 403 or 404, stop and fix that today.

Security headers are the cheapest hardening you’ll ever do

Headers don’t replace good code, but they close off whole categories of attack (clickjacking, MIME sniffing, some XSS) for basically zero effort. On Apache, this goes in your vhost or .htaccess:

Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
Header always set X-Content-Type-Options "nosniff"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set Permissions-Policy "geolocation=(), microphone=(), camera=()"

On Nginx it’s the same idea in the server block:

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header X-Frame-Options "SAMEORIGIN" always;

Content-Security-Policy is the one that actually matters most and the one people skip because it’s annoying. Start in report-only mode so you don’t break your own site:

Content-Security-Policy-Report-Only: default-src 'self'; img-src 'self' data: https:; script-src 'self'; report-uri /csp-report

Watch the reports for a week, tighten the policy, then flip the header name to Content-Security-Policy and drop the -Report-Only. If you use Google Analytics or an embedded map, you’ll need to allow those origins explicitly. Don’t just slap unsafe-inline in there to make the warnings go away, that defeats the purpose.

Patch the boring stuff on a schedule

Nearly every compromised WordPress site I’ve looked at was running a vulnerable version of something. Not exotic zero-days. Known, patched, months-old vulnerabilities.

  • Core, themes, and plugins on the latest stable release. Automatic minor updates for core are fine. Major versions deserve a staging test first.
  • Delete plugins and themes you’re not using. Deactivated code is still code, and abandoned plugins are the number one source of issues.
  • Watch for abandoned plugins specifically. If a plugin hasn’t been updated in over a year and has any active install count, find a replacement now, not after it’s exploited.
  • Strong, unique passwords plus 2FA on every admin account. No shared logins. If three people use admin, you have no audit trail.

If you’re on a modern framework instead of a CMS, the same principle applies to your dependency tree. Run the audit, read the output, and act on the high and critical findings:

npm audit --production
composer audit
pip-audit

Check your own code for the classic mistakes

Frameworks protect you from a lot, but only if you use them correctly. These are the four things I still find in custom code.

SQL injection

Never build a query by string concatenation. Use parameterized statements, always.

// Wrong: user input straight into the query
$sql = "SELECT * FROM users WHERE email = '" . $_GET['email'] . "'";

// Right: prepared statement, input bound as a parameter
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = ?');
$stmt->execute([$_GET['email']]);
$user = $stmt->fetch();

Cross-site scripting

Escape on output, not on input. Store what the user typed, escape it when you render it. In a template, that usually means using the auto-escaping helper and not the “raw” one.

<!-- Escaped, safe -->
<p>Hello, {{ username }}</p>

<!-- Raw output: only for HTML you generated yourself -->
<div>{{ bio_html | safe }}</div>

If you ever let users submit HTML (a bio, a comment), run it through a real sanitizer library like DOMPurify on the server side. Never roll your own.

File uploads

  • Validate the file type by content, not just the extension. Check the MIME type server-side.
  • Store uploads outside the web root, or serve them through a script that checks permissions.
  • Never trust the original filename. Generate your own and strip anything executable.
  • Set a size limit. An unbounded upload is a denial-of-service waiting to happen.

Authentication and sessions

  • Hash passwords with bcrypt, scrypt, or argon2. Not MD5, not SHA-1, not SHA-256 by itself.
  • Regenerate the session ID on login to prevent session fixation.
  • Set cookies with Secure, HttpOnly, and SameSite=Lax (or Strict).
  • Rate-limit login attempts. A login form with no throttle is an invitation.

Backups and recovery: the part everyone skips

A backup you’ve never restored is a hope, not a backup. The three rules:

  • 3-2-1. Three copies, two different media, one off-site. A backup sitting on the same server as the site doesn’t count.
  • Test the restore. Once a quarter, actually pull a backup down and restore it somewhere. You’ll find the broken job now instead of during an incident.
  • Automate and alert. If the backup job fails silently, you don’t have backups. Set up an email or Slack alert on failure.

While you’re at it, check the retention window. If your backup only goes back seven days and a compromise sat undetected for three weeks, your “clean” backup is already infected. Thirty days minimum for most small sites.

Monitoring, so you find out before your customers do

You can’t fix what you don’t know about. A few cheap things that pay off:

  • File integrity monitoring on the web root. If index.php changes and you didn’t change it, you want to know within the hour.
  • Log review, or at least log shipping to somewhere an attacker can’t easily wipe. Check for repeated 404s on the paths from the first section. That’s someone scanning you.
  • Uptime and certificate expiry monitoring. An expired cert doesn’t just annoy visitors, it trains them to click through warnings.
  • A WAF in front of the site. Cloudflare’s free tier handles a surprising amount of garbage traffic. It’s not a substitute for patching, but it buys you time.

How often should you actually do this?

Here’s a realistic cadence for a small business site:

  • Weekly: apply updates, check the backup ran, glance at the error log.
  • Monthly: run the dependency audit, review admin accounts, confirm 2FA is still on for everyone.
  • Quarterly: test a restore, re-run the external checks, review the CSP report.
  • Yearly: a proper pass over custom code, or bring someone in to do it. If you’ve got a custom application rather than a CMS, this matters more, and it’s usually worth pairing with a review of the code itself.

I do this kind of work on sites I build here on Long Island, and honestly the audit is the easy part. The hard part is the calendar reminder. If you don’t have someone whose job it is to run the checklist, it doesn’t get run. That’s true whether you built the site with a CMS or with custom web development, and it’s the single biggest factor in whether a small site stays up or ends up serving spam.

FAQ

Do I really need a security audit if my site doesn’t take payments?

Yes. Attackers don’t only want credit cards. They want your server to send spam, host phishing pages, or mine crypto, and they’ll use your domain’s reputation to do it. A site with no checkout is still a target, often an easier one because nobody’s watching it.

Can I just install a security plugin and be done?

No. A plugin can help with firewalling, malware scanning, and login hardening, and those are worth having. But it won’t fix an SQL injection in your own code, won’t stop you from leaving a vulnerable dependency in place, and won’t restore a backup that never ran. It’s one layer, not the whole thing.

How much does it cost to get a small site audited?

For a typical brochure site, most of this is a few hours of work you or your developer can absorb into regular maintenance. Custom applications with logins, payments, or user data take longer because there’s actual code to review. The honest answer is that ongoing maintenance costs less than one cleanup after a compromise, every time.

Have a project like this?

I do custom web development for Long Island businesses. If you need this done right, let’s talk.