EN DE

Content Security Policy in Apache (.htaccess)

A Content Security Policy locks down which resources the browser is allowed to load on your Apache-served website. This guide walks through the minimal, working deployment: enable mod_headers, drop the header into .htaccess, test in report-only mode, then switch to enforcing. Five minutes end to end once mod_headers is enabled.

Prerequisites: enable mod_headers

Apache's mod_headers module is what lets .htaccess rewrite response headers. On Debian/Ubuntu derivatives:

sudo a2enmod headers
sudo systemctl restart apache2

On RHEL/CentOS/Fedora, open /etc/httpd/conf/httpd.conf and make sure this line is present and uncommented:

LoadModule headers_module modules/mod_headers.so

Restart Apache afterwards. You can verify the module is loaded with apachectl -M | grep headers.

The minimal .htaccess snippet

Open the .htaccess file at your document root (create it if it doesn't exist) and add:

<IfModule mod_headers.c>
  Header always set Content-Security-Policy-Report-Only "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'"
</IfModule>

A few things to note:

Test it

Reload your site, open browser DevTools (F12) → Console. You'll see red warnings for every resource that would be blocked, like:

[Report Only] Refused to load the script 'https://cdn.example.com/analytics.js' because it violates the following Content Security Policy directive: "script-src 'self' 'unsafe-inline'".

Each of these is a real resource your site loads that isn't covered by the current policy. You have two options: add the domain to the corresponding directive, or remove the dependency. Keep iterating until the console is clean.

Faster approach: run our free scanner on your site. It crawls every page, finds all external resources, and generates the exact CSP you need — no trial and error. You can paste the result directly into .htaccess.

Switch to enforcing mode

Once the console is quiet, change the header name from Content-Security-Policy-Report-Only to Content-Security-Policy and reload:

<IfModule mod_headers.c>
  Header always set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'"
</IfModule>

That's it. Your site is now enforcing a CSP. Any violation from now on is actually blocked by the browser, not just reported.

Common pitfalls

"mod_headers is not loaded"

If you see this in the Apache error log, you skipped the prerequisite. Run sudo a2enmod headers && sudo systemctl restart apache2 (Debian/Ubuntu) or uncomment the LoadModule line in httpd.conf.

The header shows up but the policy is ignored

Check whether there's a second CSP header being set somewhere else (by PHP, a virtual host file, or another .htaccess). If multiple Content-Security-Policy headers are sent, browsers enforce the intersection — which is often more restrictive than intended. Use curl -I https://your-site.example to see all headers. If you find duplicates, keep only the one in .htaccess and delete the others.

WordPress/PHP apps require 'unsafe-inline'

Most CMS and PHP frameworks emit inline <script> and <style> blocks. Without 'unsafe-inline' in script-src and style-src, the site breaks. The stricter alternative is to use nonces or hashes, but that requires code changes — see the WordPress guide for details.

CORS and CSP are different things

A common confusion: if your site gets blocked loading resources from another domain, that might be CORS (Access-Control-Allow-Origin) rather than CSP. CSP tells the browser what your page is allowed to load; CORS tells the other server whether it's willing to be loaded by your page. Both can block resources but the fix is different.

Hot-reloading development servers

Webpack dev server, Vite, Next.js in dev mode — they all inject inline scripts for hot reloading. If your CSP blocks them, your dev environment breaks. Solution: deploy the strict CSP only in production. In Apache you can do this via SetEnvIf or simply by keeping .htaccess production-only.

Apache-specific edge cases

.htaccess performance

Every directive in .htaccess is re-evaluated on every request. For a single Header set the overhead is negligible, but if you want maximum performance move the directive to the virtual host block in /etc/apache2/sites-enabled/your-site.conf and disable .htaccess entirely with AllowOverride None. Same syntax, no per-request cost.

Using Header append vs. Header set

Always use set, never append. append would concatenate multiple CSP values, which browsers treat as the intersection — usually not what you want.

Reporting endpoints

Once you move to enforcing mode, consider adding a report-uri directive so you can see violations that happen in the wild (not just in your own testing):

Header always set Content-Security-Policy "default-src 'self'; ... ; report-uri https://your-reporting-endpoint.example/csp"

Services like report-uri.com offer free tiers; you can also self-host a collector.

Generate your exact CSP in 30 seconds → We crawl your site, find every resource, and produce a tight .htaccess block for your real stack — not a guess