Add HSTS and security headers
Templ issues and renews your SSL certificate and redirects HTTP to HTTPS automatically, but it does not add HSTS or other security response headers for you. You add those at the application level with a plugin or a bit of PHP, or ask us to add them at server level if you need them on static files too. This is what security scanners are flagging when they report a missing Strict-Transport-Security header.
What is HSTS
HSTS (HTTP Strict Transport Security) is a response header that tells browsers to always load your site over HTTPS, even if someone types http:// or follows an old http:// link.
Once a browser has seen the header, it refuses to connect over plain HTTP for the duration you set, which protects against downgrade and cookie-hijacking attacks.
Because Templ already redirects HTTP to HTTPS and serves a valid certificate, adding HSTS is a hardening step on top of a working setup, not a fix for a broken one.
Option 1: Use a plugin
The no-code path is a plugin that manages response headers.
The free Redirection plugin can add headers under Redirection → Site → HTTP Headers → Add Header: add a Strict-Transport-Security header with a value like max-age=63072000; includeSubDomains.
Most security plugins (for example Wordfence or All In One Security) can also add HSTS and related headers from their settings.
Option 2: Add the headers with PHP
If a plugin can't do exactly what you need, add the headers with a bit of PHP. There are two common ways to run it, in order of least to most technical:
- Code Snippets plugin - install it, go to Snippets → Add New, paste the code below, set it to Run everywhere, and activate. No files to touch.
- Your theme's
functions.php- paste the code below at the end of the file. Use a child theme so a theme update doesn't wipe it out.
function templ_security_headers() {
$siteurl = get_option('siteurl');
if (strpos($siteurl, 'templweb.com') !== false || strpos($siteurl, 'templtrial.com') !== false) {
return;
}
header('Strict-Transport-Security: max-age=63072000; includeSubDomains');
}
add_action('send_headers', 'templ_security_headers');
The guard skips the header on temporary templweb.com and templtrial.com domains, so HSTS only applies once you're on your real domain.
Leaving it out could pin HSTS to a throwaway hostname you no longer control.
includeSubDomains forces HTTPS on every subdomain too - remove it if any subdomain (like a mail or legacy host) is not served over HTTPS.
Start with a short max-age such as 300 to confirm everything still loads, then raise it to 63072000 (two years) once you're confident.Optional additional headers
You can add more headers inside the same function. These are safe defaults that don't depend on your site's specific setup:
header('X-Content-Type-Options: nosniff');
header('X-Frame-Options: SAMEORIGIN');
header('Referrer-Policy: strict-origin-when-cross-origin');
header('Permissions-Policy: geolocation=(), camera=(), microphone=(), payment=()');
- X-Content-Type-Options: nosniff stops browsers from guessing a file's type and running it as something it isn't.
- X-Frame-Options: SAMEORIGIN blocks other sites from embedding your pages in a frame (clickjacking protection).
- Referrer-Policy controls how much of the referring URL is shared when visitors click away.
- Permissions-Policy turns off browser features your site doesn't use, like geolocation, camera, microphone, and payment. Only disable what you're sure you don't need - a booking or checkout plugin may rely on one of them.
Skip Expect-CT and X-XSS-Protection - both are deprecated and ignored by modern browsers.
A Content-Security-Policy (CSP) is powerful but site-specific: a blanket policy usually breaks scripts, styles, or embeds, so add one only after testing it against your own theme and plugins.
Preloading HSTS
You'll see preload in many HSTS examples.
It submits your domain to a browser-baked list that forces HTTPS before the first visit ever happens.
preload and submit to hstspreload.org once every subdomain is permanently HTTPS.
Removal from the preload list takes months to reach browsers, so a mistake here can make subdomains unreachable for a long time.Option 3: Ask us to add them at server level
Options 1 and 2 only cover responses that WordPress generates - HTML pages, including Templ Cache hits. They do not apply to static files like CSS, JS, and images, which nginx serves directly without running PHP. If a security scanner or a compliance requirement expects the headers on every response, we can add them to your site's nginx configuration instead.
Contact support with the headers and values you want, and we'll add them for you.
We recommend the application-level options where they're enough, because you can change or roll back a header yourself without opening a ticket.
Server-level headers also apply to your templweb.com and templtrial.com domains, so a long HSTS max-age will pin those hostnames too - start at max-age=300.
X-Frame-Options and Permissions-Policy in particular can break embeds, page builders, and third-party integrations that expect to frame your pages or use a browser feature you turned off.Verify the header
Confirm the header is live with curl from your terminal:
curl -sI https://yourdomain.com | grep -i strict-transport-security
You should see your Strict-Transport-Security line in the output.
Headers sent from PHP are stored with the cached page, so they're served on Templ Cache hits too - a cached response (X-Cache-Status: HIT) carries the same security headers as an uncached one.
They are not applied to static files such as CSS, JS, and images, which nginx serves without running PHP - see Option 3 if you need those covered.
See How caching works for more on cache status.