turnley.dev

PHP and MySQL code for faucet operators

Tools & Calculators

SEC-008

Security headers, and which pages must be exempt

A tight Content-Security-Policy breaks ad embeds and pasted network code. Set it globally, exempt the pages that carry third-party markup.

Security headers cost nothing to add and stop whole classes of attack. Content-Security-Policy is the exception: it is the one that will break your site, and specifically it will break the part that earns money.

Ad networks inject scripts, iframes and images from domains you cannot enumerate. A policy strict enough to be worth having is strict enough to blank your ad slots, and the failure is silent unless someone opens the console.

So set the cheap headers everywhere and treat CSP as a per-area decision: strict on the admin panel where no third-party code belongs, absent or report-only on public pages that carry embeds.

PHP
function security_headers(bool $strictCsp = false): void
{
    // Free everywhere. No compatibility cost.
    header('X-Content-Type-Options: nosniff');
    header('Referrer-Policy: strict-origin-when-cross-origin');
    header('X-Frame-Options: SAMEORIGIN');
    header('Permissions-Policy: geolocation=(), microphone=(), camera=(), interest-cohort=()');

    if (($_SERVER['HTTPS'] ?? '') !== '' && $_SERVER['HTTPS'] !== 'off') {
        // Only once the whole site is HTTPS. This is hard to undo.
        header('Strict-Transport-Security: max-age=31536000; includeSubDomains');
    }

    if ($strictCsp) {
        // Admin only: nothing third-party is expected here.
        header(
            "Content-Security-Policy: default-src 'self'; "
          . "img-src 'self' data:; style-src 'self' 'unsafe-inline'; "
          . "script-src 'self'; frame-ancestors 'self'; base-uri 'self'; form-action 'self'"
        );
    }
    // Public pages carry pasted ad code. Run report-only first and read the
    // reports for a week before you consider enforcing anything.
}

Using it

On the admin panel, the strict policy also removes X-Frame-Options concerns via frame-ancestors, and costs nothing because you control every asset on the page.

HSTS is worth having on a .dev domain regardless, since the TLD is preloaded and plain HTTP was never going to work anyway.

What bites people

Do not enable HSTS until every subdomain is HTTPS. The max-age is honoured by the browser even after you remove the header.

X-Frame-Options SAMEORIGIN on a page that serves ad iframes is fine — it governs whether your page can be framed, not what your page may frame. Blocking your own embed is a different header.

Test with the console open. CSP failures do not appear on the page; the ad slot simply renders nothing.

Also in Auth and Security