Security settings for a small-business website, by platform

Last updated

The short answer

Have your server send a small set of security headers with each page, stop it announcing which software versions it runs, and mark login cookies as secure. Four of the headers are safe to add straight away. Two need care: HSTS goes in stages, and a Content-Security-Policy starts in a report-only mode so it can't break the site. On a hosted builder most of this is set for you and can't be changed.

What security headers are

Each time a browser loads a page, the server sends a few lines of instructions with it, called headers, that visitors never see. Security headers tell the browser to refuse things an attacker might try: loading your page inside another site's frame, guessing that an uploaded image is a script, or falling back to an insecure connection. OWASP's headers cheat sheet describes each one.

Why it matters to a small business

Automated tools look for known weaknesses on any site they can reach, large or small, so being a small business doesn't keep a site out of their way. Headers don't replace keeping your software up to date, but they limit the damage when something goes wrong, such as a plugin with a flaw or a third-party script that is tampered with. A site that announces an old software version tells those tools where to start.

How the free scan decides

The scan reads the headers your homepage sends. The lines it uses:

The settings, and the safe order

Start by keeping a copy of whichever settings file you edit. Then:

  1. The four simple headers. These rarely affect how a site works:
    • X-Content-Type-Options: nosniff stops browsers guessing a file's type.
    • Referrer-Policy: strict-origin-when-cross-origin stops full page addresses being passed to other sites.
    • X-Frame-Options: SAMEORIGIN stops other sites showing your pages in a frame. If one of your own other sites frames this one, it stops working there.
    • Permissions-Policy: camera=(), microphone=(), geolocation=() switches off features your site doesn't use. If your pages ask for the visitor's location, such as a store finder, use geolocation=(self).
  2. HSTS, in stages. It tells browsers to use https only. OWASP explains it. Start with max-age=300; once the site has worked over https with that, raise it to max-age=604800 (a week), then max-age=31536000; includeSubDomains. Check each subdomain works over https before the last stage, and leave out preload unless you mean it, because it is hard to undo.
  3. Content-Security-Policy, report-only first. It lists where scripts, styles and images may load from, which stops injected scripts running. Send it as Content-Security-Policy-Report-Only first: that blocks nothing and only reports. Open your pages with the browser's developer tools and read the Console; add each host it reports to the policy. Only when the reports stop, rename the header to Content-Security-Policy. An enforced policy that misses your tag manager stops it. web.dev's strict CSP guide covers the stronger nonce-based form.
  4. Hide version numbers and keep the software current. Remove headers such as X-Powered-By and X-AspNet-Version, and keep your platform, theme and plugins up to date. Hiding the number doesn't fix an old version; updating does.
  5. Secure cookies. Login and session cookies should carry Secure (sent only over https), HttpOnly (not readable by scripts) and SameSite, as OWASP's session guidance recommends.

Where to set them, by platform

Windows hosting (IIS and ASP.NET)

These go in web.config, the file in your site's top folder. Keep a copy first: one malformed line takes the site offline. <system.webServer> and <system.web> both sit directly inside <configuration>. If your file already has any of these elements, merge the lines into the one that is there: an element that appears twice stops the site. Each <remove> before an <add> clears a value the server may already set.

<system.webServer>
  <httpProtocol>
    <customHeaders>
      <remove name="X-Powered-By" />
      <remove name="X-Content-Type-Options" />
      <add name="X-Content-Type-Options" value="nosniff" />
      <remove name="Referrer-Policy" />
      <add name="Referrer-Policy" value="strict-origin-when-cross-origin" />
      <remove name="X-Frame-Options" />
      <add name="X-Frame-Options" value="SAMEORIGIN" />
      <remove name="Permissions-Policy" />
      <add name="Permissions-Policy" value="camera=(), microphone=(), geolocation=()" />
      <remove name="Strict-Transport-Security" />
      <add name="Strict-Transport-Security" value="max-age=300" />
    </customHeaders>
  </httpProtocol>
  <security>
    <requestFiltering removeServerHeader="true" />
  </security>
</system.webServer>
<system.web>
  <httpRuntime enableVersionHeader="false" />
  <httpCookies requireSSL="true" httpOnlyCookies="true" sameSite="Lax" />
</system.web>

Save, then open your homepage in a private window; if it shows an error, put your copy back. removeServerHeader needs IIS 10 on Windows Server version 1709 or later, and the sameSite setting needs .NET Framework 4.7.2 or later; leave out whichever your host doesn't have. requireSSL marks every cookie https-only, so send http visitors to https first: on a page still opened over plain http, sign-in and baskets stop working. If your <httpRuntime> already has a targetFramework, add the attribute to it. On ASP.NET MVC, the X-AspNetMvc-Version header is switched off in code, with MvcHandler.DisableMvcResponseHeader = true; in Application_Start. Microsoft's custom headers page has the details.

Apache, and WordPress on Apache

These go in .htaccess in your site's top folder. On WordPress, paste them above the # BEGIN WordPress line, because WordPress rewrites everything between its own markers. If the site shows a server error (500) afterwards, remove the lines and send them to your host.

<IfModule mod_headers.c>
  Header unset X-Powered-By
  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 "camera=(), microphone=(), geolocation=()"
  Header always set Strict-Transport-Security "max-age=300"
</IfModule>

nginx

Add each header with add_header Name "value" always; inside your site's server block, and server_tokens off; to hide the version. A location block with add_header lines of its own drops these, so repeat them there. Run nginx -t before reloading. If your host manages nginx, send them the lines.

Cloudflare, Netlify and Vercel

On Cloudflare, add the headers with a response header transform rule; it works whatever server is behind it. On Netlify they go in the _headers file, and on Vercel in vercel.json.

Shopify, Wix and Squarespace

The builder runs the server and sets its own headers; on most plans you can't add or change them. When the scan recognises a hosted builder, the finding says so, and there is nothing for you to fix there. What stays yours: strong passwords and two-step sign-in for each admin account, and removing apps and staff accounts you no longer use.

How to check yours

Run the free scan: the Security section lists each header it found and its value, and when it recognises your server it gives the lines to paste for that server. The sample report shows the layout. The one-address guide covers the redirect to https, which HSTS depends on.

Checklist

What the free scan checks for this

These are the FocalScore checks that cover this guide's subject. Each one in your report says what it found, shows the evidence and gives the fix.

Sources

Last checked . Documentation, settings and rules change; if one of these pages now says something different, it is right and this guide is out of date. Tell us at info@focalscore.com.

  1. OWASP Cheat Sheet: HTTP security response headers
  2. OWASP Cheat Sheet: HTTP Strict Transport Security
  3. web.dev: A strict Content Security Policy
  4. OWASP Cheat Sheet: Session management
  5. Microsoft Learn: Custom headers in IIS
  6. Cloudflare: Response header transform rules