Skip to content

Security Headers

Security headers instruct browsers to enable built-in protections against common attacks like clickjacking, XSS, MIME sniffing, and protocol downgrade. OLS supports setting headers natively or via .htaccess with the LiteHTTPD module.

HeaderPurpose
Strict-Transport-SecurityForce HTTPS connections (HSTS)
X-Frame-OptionsPrevent clickjacking
X-Content-Type-OptionsPrevent MIME type sniffing
Content-Security-PolicyControl resource loading
Referrer-PolicyControl referrer information
Permissions-PolicyRestrict browser features
X-XSS-ProtectionLegacy XSS filter (deprecated but still useful)

Apply headers to all virtual hosts in httpd_config.conf:

extraHeaders {
set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
set X-Frame-Options "SAMEORIGIN"
set X-Content-Type-Options "nosniff"
set Referrer-Policy "strict-origin-when-cross-origin"
set Permissions-Policy "camera=(), microphone=(), geolocation=()"
set X-XSS-Protection "1; mode=block"
}

In the virtual host configuration:

virtualhost example {
...
extraHeaders {
set Strict-Transport-Security "max-age=31536000; includeSubDomains"
set X-Frame-Options "DENY"
set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'"
}
}
context /api/ {
...
extraHeaders {
set Access-Control-Allow-Origin "https://example.com"
set Access-Control-Allow-Methods "GET, POST, OPTIONS"
set Access-Control-Allow-Headers "Content-Type, Authorization"
}
}

With the LiteHTTPD module, use standard Apache Header directives in .htaccess:

# Set headers
Header set X-Frame-Options "SAMEORIGIN"
Header set X-Content-Type-Options "nosniff"
Header set Strict-Transport-Security "max-age=31536000; includeSubDomains"
Header set Referrer-Policy "strict-origin-when-cross-origin"
Header set Permissions-Policy "camera=(), microphone=(), geolocation=()"
# Append to existing header
Header append X-Frame-Options "SAMEORIGIN"
# Remove a header
Header unset X-Powered-By
Header unset Server
# Conditional header
Header set X-Robots-Tag "noindex, nofollow" env=staging

Forces browsers to use HTTPS for all future requests to your domain:

Header set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
  • max-age=31536000 — remember for 1 year
  • includeSubDomains — apply to all subdomains
  • preload — opt in to browser preload lists (submit at hstspreload.org)

Controls which resources the browser is allowed to load:

# Basic restrictive policy
Header set Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'"
# WordPress-friendly policy
Header set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self'"

Start with Content-Security-Policy-Report-Only to test without blocking:

Header set Content-Security-Policy-Report-Only "default-src 'self'; report-uri /csp-report"

Prevents your site from being embedded in iframes (clickjacking protection):

# Block all framing
Header set X-Frame-Options "DENY"
# Allow same-origin framing only
Header set X-Frame-Options "SAMEORIGIN"

Prevents browsers from MIME-sniffing a response away from the declared content type:

Header set X-Content-Type-Options "nosniff"

Controls how much referrer information is sent with requests:

# Send origin only on cross-origin requests
Header set Referrer-Policy "strict-origin-when-cross-origin"
# Never send referrer
Header set Referrer-Policy "no-referrer"

Restricts browser features like camera, microphone, and geolocation:

Header set Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=()"
Header unset X-Powered-By
Header unset Server
extraHeaders {
set Strict-Transport-Security "max-age=31536000; includeSubDomains"
set X-Frame-Options "SAMEORIGIN"
set X-Content-Type-Options "nosniff"
set Referrer-Policy "strict-origin-when-cross-origin"
set Permissions-Policy "camera=(), microphone=(), geolocation=()"
unset X-Powered-By
}
Header set Strict-Transport-Security "max-age=31536000; includeSubDomains"
Header set X-Frame-Options "SAMEORIGIN"
Header set X-Content-Type-Options "nosniff"
Header set Referrer-Policy "strict-origin-when-cross-origin"
Header set Permissions-Policy "camera=(), microphone=(), geolocation=()"
Header set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:"
Header unset X-Powered-By
Terminal window
curl -I https://example.com

Or use online tools like securityheaders.com to scan your site and get a grade.

Headers not appearing in response:

  • For .htaccess: verify the LiteHTTPD module is loaded and autoLoadHtaccess is enabled
  • For OLS native: check extraHeaders is in the correct block (server, vhost, or context)
  • Restart OLS after configuration changes

CSP blocking legitimate resources:

  • Use Content-Security-Policy-Report-Only first
  • Check the browser console for CSP violation messages
  • Add the blocked domain to the appropriate directive