This note organizes security considerations for frontend implementation on websites. The scope and strength of what you apply depend on requirements and how the site is operated, so care is needed.
Checks
Confirm the following before implementation begins.
- Whether the site handles login, personal data, payments, or user input
- Whether a security standard or client requirements apply
- How far the target pages, environments, and domains extend
- Whether external scripts, fonts, video, maps, or APIs are used
- Who reviews CSP violation reports
- Who updates external services and policies after launch
- Whether target browsers and verification methods are decided
HTTP response headers are returned from the server or CDN. Clarify ownership across frontend, backend, infrastructure, and operations first. CSP in particular needs review whenever an external service is added. Even if you can set it during production, the policy and the implementation drift when no one is assigned to update it after launch.
Decide what CSP allows
Content-Security-Policy tells the browser which resources a page may load or execute. It limits unintended script execution and external connections. It also reduces the impact of issues such as XSS.
For example, the following policy limits basic resources to the same origin. It also blocks plugin content, base URL changes via the base element, and framing from other sites.
Content-Security-Policy:
default-src 'self';
script-src 'self';
style-src 'self';
img-src 'self' data:;
font-src 'self';
connect-src 'self';
object-src 'none';
base-uri 'none';
frame-ancestors 'none';
form-action 'self' This is an example for checking how the directives relate. Required endpoints differ by site. script-src 'self' alone does not reach the same strength as a Strict CSP that uses nonces or hashes. Do not ship this as-is to production. Build the policy around the actual resources and requirements.
The main directives to review are as follows.
| Directive | Primary target | What to check during production |
|---|---|---|
default-src | Resources without a more specific directive | Fallback for other directives |
script-src | JavaScript | Bundles, analytics, tag managers, inline scripts |
style-src | CSS | CSS files, inline styles, styles added by JavaScript |
img-src | Images | CDN, CMS, SVG, data: URLs, blob: URLs |
font-src | Web fonts | Font file origins |
connect-src | fetch(), XHR, WebSocket, and similar | APIs, analytics, error logging, streaming connections |
media-src | Audio and video | Video CDNs, audio files, blob: URLs |
worker-src | Workers | Web Workers, Service Workers, blob: URLs |
frame-src | Frames embedded in the page | Video, maps, external forms |
frame-ancestors | Parents that may embed this page | iframe display from external sites |
form-action | Form submission targets | Contact forms, search, external forms |
If you add every unknown endpoint that appears in violation logs, it becomes unclear why each permission exists. Inventory the resources the page uses first and record their purpose and owners.
| Type | Endpoint | Purpose | Required | If it fails |
|---|---|---|---|---|
| Script | analytics.example | Analytics | Optional | No measurement |
| Font | fonts.example | Web fonts | Optional | Falls back to system fonts |
| Connect | api.example | Content fetching | Required | Target content cannot be shown |
The domains above are fictional. In a real project, hostnames alone are not enough. Confirm who decides to use them and who can change them after launch.
Implementations that often conflict with CSP
Inline scripts
When script-src is strict, inline script elements and inline event handlers such as onclick do not run.
<button type="button" onclick="openMenu()">Menu</button> Register events from JavaScript. Keep executable code out of the HTML.
<button class="js-menu" type="button">Menu</button> const button = document.querySelector( '.js-menu' );
button.addEventListener( 'click', openMenu ); Adding 'unsafe-inline' when an existing implementation breaks may restore the display. It also broadly allows inline code, which conflicts with the goal of a fairly strict CSP. First check whether you can move the code to an external file or limit permission with a nonce or hash.
Evaluating strings as code
APIs such as eval() and new Function() that evaluate strings as JavaScript are blocked by a typical fairly strict CSP. Remove or replace that code before adding 'unsafe-eval' just to make it run.
Development builds sometimes rely on code generation, so verify against production files. Do not leave exceptions meant only for the development environment in the production policy. When using WebAssembly, include 'wasm-unsafe-eval' and check target browsers and requirements separately.
Inline styles
When style-src is strict, HTML style attributes, style elements in HTML, and styles added from JavaScript are affected. Libraries may also create style elements at runtime, so searching only the HTML you wrote is not enough.
Treat implementations that update an element’s style for animation separately from ones that insert new CSS rules from strings. The applicable CSP conditions differ, so check violations and rendering in the actual target browsers.
data: and blob:
Embedded images, video, Workers, and generated files may use data: URLs or blob: URLs. You can allow them on the specific directives that need them. Avoid adding them broadly to default-src only for convenience. That is easier to manage.
Sites that use WebGL add more load paths such as textures, video, Web Workers, and WebAssembly. Do not think in terms of “allowing Three.js.” Confirm which kinds of resources the browser ultimately fetches and from where.
External services
Tag managers, analytics, video, maps, chat, and similar tools may load additional scripts or frames from a single script. Adding only the first URL to script-src does not guarantee that the service works.
Do not allow every failing endpoint. Check whether the service is required, whether you can self-host it, and whether another approach meets the same goal. More allowed domains mean more things to maintain.
Nonces and hashes
Strict CSP does not rely only on a hostname allowlist. It uses nonces or hashes to specify which scripts may run.
When using a nonce, generate an unguessable value per HTTP response. Set the same value on both the CSP and the allowed script elements.
Content-Security-Policy: script-src 'nonce-{RANDOM}' 'strict-dynamic'; object-src 'none'; base-uri 'none' <script nonce="{RANDOM}" src="/assets/main.js"></script> {RANDOM} is not a fixed string. It is a value the server generates per response. Reusing the same nonce over time breaks the premise of the mechanism.
When you serve static HTML as-is, you can generate hashes from the script contents and allow those. You must update the hashes whenever the files change. Choose between nonces and hashes based on how HTML is generated and how deployment works.
With 'strict-dynamic', trust extends to scripts added by a script already trusted via nonce or hash. That can make external services easier to handle. It also means you need to review DOM operations inside trusted scripts.
Start with Report-Only
Applying CSP all at once to an existing site can block required resources and break pages. First ship a candidate policy with Content-Security-Policy-Report-Only.
Reporting-Endpoints: csp-endpoint="https://reports.example/csp"
Content-Security-Policy-Report-Only: default-src 'self'; object-src 'none'; base-uri 'none'; report-to csp-endpoint The report endpoint above is fictional. Report-Only sends violations but does not block resource loading or execution. Reporting API behavior can differ by browser support, so confirm that reports are received in the target browsers.
Proceed in this order.
- Inventory endpoints in the Network panel of developer tools
- Set the candidate policy in Report-Only
- Check major pages and interactions in the target browsers
- Sort violation causes into required traffic, code that needs fixing, and unnecessary traffic
- Fix the code or the policy
- Switch to an enforcing
Content-Security-Policy - Keep watching external-service changes and violations after launch
Violations from browser extensions and other sources unrelated to the site’s implementation can also appear. Do not add domains from the logs to the allowlist without checking them.
If you send violation reports externally, also review which URLs they contain and how they are operated. Do not trust received values. Decide what to store, access controls, rate limits, and retention. Design the security logs so they do not keep unnecessary information.
Inserting strings into the DOM
Even with CSP in place, user input and external data still need to be handled safely. For plain text, use textContent instead of building HTML strings.
const title = document.createElement( 'h2' );
title.textContent = data.title;
container.appendChild( title ); Places that can interpret strings as HTML or script, such as innerHTML, insertAdjacentHTML(), and document.write(), are called injection sinks. Do not pass external values through them as-is. First confirm whether HTML is actually required.
When you must accept HTML, decide which elements and attributes are allowed and sanitize. Under stricter requirements, you can enforce Trusted Types from CSP so ordinary strings cannot be passed to the target APIs.
Content-Security-Policy: require-trusted-types-for 'script'; trusted-types article-html Introducing Trusted Types also affects existing libraries and browser support. As with CSP, do not only add the setting. Inventory injection sinks and migrate first.
External files and SRI
When you load JavaScript or CSS from a CDN or another party, Subresource Integrity can verify the hash of the fetched file.
<script
src="https://cdn.example/library.js"
integrity="sha384-{HASH}"
crossorigin="anonymous"
></script> If the file contents change, the hash no longer matches. The browser refuses to load it. That helps against tampering. It can also fit poorly with services that auto-update external files. If you cannot fix the update method, consider self-hosting as well.
When verifying a cross-origin resource with SRI, the origin must also allow CORS. Do not stop after adding a hash. Check the actual response headers and load result.
Policies beyond CSP
Other HTTP response headers each have different roles. Do not add them only to increase the count. Check the features the site uses and the impact of each header.
Permissions Policy
Permissions-Policy controls access to browser features such as camera, microphone, and geolocation. An example that disables unused features is as follows.
Permissions-Policy: camera=(), microphone=(), geolocation=() When allowing features inside an external iframe, check both the response header and the iframe allow attribute. Feature coverage and browser support change over time, so do not keep using a fixed list without review.
Referrer Policy
Referrer-Policy controls how much referrer information is sent on navigations and resource fetches.
Referrer-Policy: strict-origin-when-cross-origin This setting sends only the origin, not the path, to external origins. You can be stricter with no-referrer. Analytics and external services may still need the referrer. Weigh the privacy benefit against operational impact.
COOP and COEP
Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy isolate the browsing context from cross-origin pages and resources. Setting both appropriately enables features that require cross-origin isolation.
They can also affect external images, video, iframes, auth popups, and similar cases. They are not something to enable on every ordinary site. Introduce them after checking features that need isolation, such as SharedArrayBuffer, and the external resources you load.
X-Content-Type-Options
X-Content-Type-Options: nosniff stops the browser from treating a response as a different format than its Content-Type.
X-Content-Type-Options: nosniff Setting the header alone is not enough. Serve JavaScript, CSS, fonts, and images with the correct Content-Type. If a file stops loading after you enable it, do not remove nosniff. Fix the MIME type on the delivery side.
HTTPS and HSTS
Unify traffic on HTTPS for more than HTML, including JavaScript, images, fonts, and APIs. Mixed Content, where an HTTPS page loads HTTP resources, may be blocked by the browser. Fix those endpoints before CSP becomes the concern.
Strict-Transport-Security tells a browser that has connected over HTTPS to keep using HTTPS afterward.
Strict-Transport-Security: max-age=31536000 A long max-age, includeSubDomains, and preload leave effects for a long time. Do not add them uniformly without checking certificate renewal, subdomains, and incident response. HSTS is not set from frontend JavaScript. Decide it together with delivery and operations.
CORS is not a way to widen access
When a resource cannot be fetched from an external API or CDN, people sometimes try to fix it by adding Access-Control-Allow-Origin: *. CORS is the server telling browsers which cross-origin pages may read the response.
Do not widen allowed origins only to clear an error. Confirm who should be allowed to read that resource. Also, allowing a connection in CSP connect-src does not let JavaScript use the response if the destination’s CORS settings do not allow it. They are separate controls.
Verify response headers
Most policies including CSP are not set from frontend JavaScript. They are returned as HTTP response headers from the server, CDN, hosting service, and similar layers.
You can specify part of CSP from an HTML meta element. Report-Only, frame-ancestors, and some other features are unavailable that way. When requirements are strict, use HTTP response headers as the baseline.
After configuration, you can verify the actual response from the command line as well as from the browser Network panel.
curl -I https://www.example.com/ Check more than the top page. Also check lower pages, error pages, localized pages, and other paths that return HTML. CDN caching and environment differences can return different headers on staging and production.
Production kickoff checklist
On projects with security requirements, include the following in the production process.
Requirements
- Confirm the standards that apply and the scope
- Decide ownership across client, development, infrastructure, and operations
- Decide target browsers and verification environments
- Decide who updates after launch and the communication path
Design and implementation
- List external domains and the purpose of each resource
- Check inline scripts and inline styles
- Check
eval(),new Function(), and injection sinks - Check use of
data:URLs,blob:URLs, Workers, and WebAssembly - Check traffic and
iframes added by external services - Decide how nonces or hashes are generated
Verification and launch
- Check major pages and interactions under Report-Only
- Classify violations into allow, fix in code, or remove unnecessary traffic
- Recheck with the production build and a production-like delivery environment
- After enforcement, check forms, external navigation, video, analytics, and similar flows
- Check response headers on lower pages and error pages
- Hand off post-launch violation review and policy updates to operations
References
- Content Security Policy Level 3 – W3C
- Content Security Policy – MDN
- CSP implementation – MDN
- Permissions Policy – W3C
- Referrer Policy – W3C
- Trusted Types – W3C
- Trusted Types API – MDN
- Subresource Integrity – MDN
- Cross-Origin-Opener-Policy – MDN
- Cross-Origin-Embedder-Policy – MDN
- Strict-Transport-Security – MDN
- Fetch Standard – WHATWG
- ※ Based on public specifications and official documentation as of July 22, 2026.
- ※ This article is an AI translation of the Japanese original.