AI 与开发

Content Security Policy (CSP)

作者:Jake Luo · 发布于 2026年9月23日

A Content Security Policy (CSP) is a set of rules a website sends to the browser, usually as a Content-Security-Policy response header, that says which sources a page may load scripts, styles, images, fonts and frames from, and where it may send data. Anything the policy does not allow is refused by the browser before it runs. The page itself still loads, which is why a CSP mistake rarely looks like an error: it looks like a page that is quietly missing something.

What a policy actually controls

A policy is a list of directives, each governing one kind of request. It was designed as a second line of defence against cross-site scripting: if an attacker manages to inject markup into your page, the browser still refuses to run script from a source the policy never named. The directives you will meet first:

  • default-src — the fallback. Any fetch type without its own directive inherits this one, so default-src 'self' quietly covers fonts, media and more unless you say otherwise.
  • script-src, style-src, img-src, font-src — where each kind of subresource may come from. 'self' means the page's own origin; a hostname allows that host; 'unsafe-inline' allows code written directly in the page.
  • connect-src — where the page's own code may send requests with fetch, XHR or WebSockets. This is the directive that decides whether a script can phone home.
  • form-action — where a form may submit. frame-ancestors — who may embed the page in a frame, the modern replacement for X-Frame-Options.
  • Nonces and hashes — a way to allow one specific inline script without allowing all of them: the server marks the script it wrote, and anything injected later lacks the mark.

Two delivery details trip people up. A policy can also be set in a <meta http-equiv> tag, but that form does not support every feature, and a report-only policy cannot be delivered that way at all. And if a policy lists the same directive twice, the browser honours the first one and ignores the second, so an "added" rule can do nothing while looking correct in the source.

Why a blocked resource fails silently

When the browser refuses a request under a policy, it writes a line to the developer console and carries on rendering. No error page, no alert, nothing a visitor or an owner glancing at the site would notice. What the page looks like afterwards depends entirely on what was refused. A blocked analytics script leaves the page pixel-identical and your numbers at zero. A blocked web font falls back to a system typeface that most people never register. A blocked stylesheet or CSS framework turns a designed page into unstyled default HTML.

That spread is the practical problem: the same kind of violation ranges from invisible to page-destroying, and the policy text does not tell you which end you are on. Test the deployed URL, not a local copy — a file opened from your laptop is usually served with no policy at all, so it can load everything the live page will refuse. This is the most common reason a page built with AI looks right in preview and wrong once published.

What running one for other people's pages taught us

AgentCeres — the AI Growth Officer at agentceres.com — publishes the pages its agents build for customers, and every one of them is served under a strict policy: resources only from the page's own origin, images from the origin or embedded directly, forms that submit only back to the host, no embedding in other sites, and connect-src 'none', so code on the page can calculate, remember things locally and navigate, but cannot make a single network request. That last rule is why a secret placed in page code cannot even be used from the live page.

When we checked every hosted page on 2026-09-04, 5 of 52 carried at least one external resource that never loaded. The worst was a page whose entire layout came from a CSS framework loaded off a public CDN; the policy names no outside host, so the framework never arrived and the page went live as plain unstyled text. Others lost a radio stream or an embedded frame. We considered stripping blocked references at publish time and rejected it, because a stripped page still "publishes fine" and still looks wrong; refusing outright was no better, since the same rule would block a page over a font that merely falls back. So the publish step reports each blocked reference with the directive that will refuse it, and the fix happens before anyone sees the page. The verdict is computed from the same directive list the server sends, fallbacks included, so the warning cannot drift from the policy.

The lesson generalises past our setup: a policy you do not check against real pages is a policy that breaks pages without telling you. Before you enforce one, send it as Content-Security-Policy-Report-Only, which reports violations without blocking anything, and read what it would have broken.

What a policy cannot do

CSP governs what a page may load and where it may connect. It is easy to credit it with more than that.

  • It has no directive for cookies. Script the policy allows can still read and set cookies, apart from ones marked HttpOnly. Pages that share a parent domain can share cookies too, and closing that off takes a separate control, not a stricter policy.
  • It does not make inline code safe. Allowing 'unsafe-inline' permits whatever is written into the page, including anything an attacker managed to write there; that is what nonces and hashes exist to avoid.
  • It does not replace escaping and validation. It limits the damage of an injection; it does not prevent one. A page that trusts user input is still broken, just less exploitable.
  • It does not protect an AI agent from its inputs. A policy stops a browser loading a hostile script; it does nothing about hostile *text* an agent reads and acts on, which is the separate problem of prompt injection.

常见问题

Why do my images or fonts load locally but not on the live site?
Most often because the host serves a policy and your local copy has none. Open the live page, open the browser console, and look for lines mentioning Content Security Policy: each names the directive that refused the request. The fix is either to serve the file from your own site or to add the outside host to the directive that governs that file type.
Can I just add 'unsafe-inline' or a wildcard to make the errors go away?
You can, and it will work, and it will also remove most of what the policy was protecting. Name the specific hosts you actually use, and allow individual inline scripts with a nonce or hash rather than all of them. If you cannot tell which hosts you need, run the policy in report-only mode for a while and read the reports.
Does a Content Security Policy affect SEO?
Not directly; search engines do not rank on it. It can affect SEO indirectly if it blocks something the rendered page depends on, such as a script that inserts your content or structured data, because a crawler that renders the page in a browser generally meets the same refusal. Check the rendered page, not just the source.
相关术语
Prompt injectionVibe CodingDesign systemRate limit

一支替你把这件事做起来的 AI 增长团队

AgentCeres 是一支托管式 AI 营销团队,发布什么由你审批。14 天免费试用,每月低至 $39。

开始免费试用浏览术语表