How to handle content security policy (CSP) in a Vue SPA?
Avoid inline scripts and styles by moving all JavaScript to external files and using Vue’s event handlers instead of inline onclick attributes. 2. Use a strict CSP header without 'unsafe-inline' or 'unsafe-eval' by adopting Vue’s runtime-only build and Single File Components to eliminate the need for dynamic template compilation. 3. Handle dynamic content safely with v-html only after sanitizing user input using DOMPurify, ensuring CSP blocks script execution in injected HTML. 4. If inline scripts are necessary, use nonces or hashes instead of 'unsafe-inline', though this is best avoided in static SPAs unless using SSR. 5. Allow external resources like CDNs by explicitly specifying domains in CSP directives such as script-src, img-src, font-src, and connect-src, while avoiding 'unsafe-inline' for styles by using external or scoped CSS. 6. Test your CSP using browser dev tools, Google CSP Evaluator, and report-only mode to detect violations without enforcement, ensuring all policies work as intended before full deployment. By following these steps, a Vue SPA can operate under a strict Content Security Policy that effectively mitigates XSS and code injection risks without relying on unsafe directives, resulting in a secure and maintainable application.
Handling Content Security Policy (CSP) in a Vue Single Page Application (SPA) is essential for securing your app against XSS (cross-site scripting) and other code injection attacks. However, Vue SPAs — especially those using dynamic features like inline scripts, eval()
, or v-html
— can easily conflict with strict CSP rules. Here’s how to handle CSP properly in a Vue SPA.

✅ 1. Avoid inline scripts and styles
CSP blocks inline <script></script>
and <style></style>
tags by default. Vue itself doesn’t generate inline scripts when built properly, but common patterns can break CSP:
-
Avoid inline event handlers (
@click
,v-on
) in templates — these are generally fine because Vue compiles them into event listeners, not inlineonclick=""
. -
Don’t use inline
<script></script>
tags in yourindex.html
. Move all logic to external files.
❌ Bad (violates default CSP):

<!-- index.html --> <script> console.log('init'); </script>
✅ Good:
<!-- index.html --> <script src="js/app.js"></script>
Use Webpack/Vite to bundle your Vue app and externalize all JavaScript.

✅ 2. Use a strict CSP header (without 'unsafe-inline')
When deploying, set a strong CSP via HTTP headers (preferred) or <meta http-equiv="Content-Security-Policy">
.
Example CSP header for a Vue SPA (hosted on same origin):
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-eval' 'unsafe-inline'; ← Avoid this!
Wait — you see 'unsafe-eval'
and 'unsafe-inline'
? That’s common during development but insecure.
Fix 'unsafe-eval'
Vue’s runtime compiler (template compilation in browser) uses new Function()
, which triggers 'unsafe-eval'
. To remove this:
➡️ Use the runtime-only build of Vue
In your Vue project (Vue 3 with Vite or Webpack), ensure you're using the runtime-only version:
// vite.config.js or webpack config resolve: { alias: { 'vue': 'vue/dist/vue.runtime.esm-bundler.js' } }
And write your components using .vue
files with <template>
, not runtime templates like:
app.component('my-comp', { template: '<div>hi</div>' }) // needs compiler → needs unsafe-eval
✅ Instead, use SFCs (Single File Components):
<!-- MyComponent.vue --> <template> <div>Hi</div> </template> <script> export default {} </script>
Now you can remove 'unsafe-eval'
from script-src
.
✅ 3. Handle dynamic content safely
Vue’s v-html
directive can be dangerous and may require 'unsafe-inline'
if misused.
❌ Avoid:
<div v-html="userContent"></div>
This opens XSS risks and doesn’t help CSP — CSP can’t distinguish safe vs unsafe v-html
.
✅ Safer approach:
- Sanitize user content with libraries like DOMPurify:
import DOMPurify from 'dompurify'; const cleanHTML = DOMPurify.sanitize(userHTML);
<div v-html="cleanHTML"></div>
Still, v-html
doesn’t require relaxing CSP if you're not injecting inline scripts. CSP will block script execution in v-html
content — which is good.
So: v-html
is acceptable if you sanitize input and your CSP blocks script execution from DOM.
✅ 4. Use nonces or hashes for allowed scripts (if needed)
If you must include a safe inline script (e.g., early init code), use nonces or hashes instead of 'unsafe-inline'
.
Example with nonce:
Generate a unique nonce per request on the server:
Content-Security-Policy: script-src 'self' 'nonce-abc123'
Then:
<script nonce="abc123"> // small inline script </script>
⚠️ This requires server-side rendering (SSR) or dynamic HTML generation. In a static Vue SPA, this is hard to implement unless you use SSR (Nuxt, etc.).
Alternatively, avoid inline scripts entirely — better for CSP and maintainability.
✅ 5. External resources and CDNs
If your Vue app loads external assets (fonts, analytics, APIs), update CSP accordingly.
Example:
Content-Security-Policy: default-src 'self'; script-src 'self' https://www.m.sbmmt.com; img-src 'self' https://*.m.sbmmt.com; font-src 'self' https://fonts.gstatic.com; connect-src 'self' https://api.yourservice.com; style-src 'self' 'unsafe-inline'; ← minimize use
➡️ Avoid 'unsafe-inline'
for style-src
by:
- Moving all CSS to external files (Vue handles this via scoped styles or CSS extraction).
- Using
style-src 'self'
if all styles are bundled.
✅ 6. Test your CSP
Use browser dev tools (Security tab) to check CSP violations.
Also use:
- Google CSP Evaluator
- Report-only mode:
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report-endpoint
This logs violations without blocking, great for testing.
Summary: Best Practices
- ✅ Use runtime-only Vue build to avoid
'unsafe-eval'
- ✅ Avoid inline scripts/styles in
index.html
- ✅ Sanitize
v-html
content with DOMPurify - ✅ Use external bundles (Vite/Webpack) for all JS/CSS
- ✅ Set CSP via HTTP headers:
script-src 'self'
, no'unsafe-inline'
or'unsafe-eval'
- ✅ Allow only necessary external domains
- ✅ Test CSP with Report-Only and browser tools
Basically, with proper Vue setup and deployment hygiene, you can run a Vue SPA under a strict CSP — no unsafe flags needed. It just takes attention to how and where code is loaded.
The above is the detailed content of How to handle content security policy (CSP) in a Vue SPA?. For more information, please follow other related articles on the PHP Chinese website!
- ✅ Use runtime-only Vue build to avoid

Hot AI Tools

Undress AI Tool
Undress images for free

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

This article has selected a series of top-level finished product resource websites for Vue developers and learners. Through these platforms, you can browse, learn, and even reuse massive high-quality Vue complete projects online for free, thereby quickly improving your development skills and project practice capabilities.

The life cycle hook of the Vue component is used to execute code at a specific stage. 1.created: Called immediately after the component is created, suitable for initializing data; 2.mounted: Called after the component is mounted to the DOM, suitable for operating the DOM or loading external resources; 3.updated: Called when the data update causes the component to be re-rendered, suitable for responding to data changes; 4.beforeUnmount: Called before the component is uninstalled, suitable for cleaning event listening or timer to prevent memory leakage. These hooks help developers accurately control component behavior and optimize performance.

To implement reusable Vue paging components, the following key points need to be clarified: 1. Define props including the total number of lines, the number of lines per page and the current page number; 2. Calculate the total number of pages; 3. Dynamically generate the displayed page number array; 4. Process the page number click event and pass it to the parent component; 5. Add styles and interaction details. Receive data through props and set default values, use the computed attribute to calculate the total number of pages, use the method to generate the currently displayed page number array, render buttons in the template and bind click events to trigger the update:current-page event, listen to the event in the parent component to update the current page number, and finally highlight the current page number through CSS and control the button status to improve the user experience.

For Vue developers, a high-quality finished project or template is a powerful tool to quickly start new projects and learn best practices. This article has selected multiple top Vue free finished product resource portals and website navigation for you to help you find the front-end solutions you need efficiently, whether it is a back-end management system, UI component library, or templates for specific business scenarios, you can easily obtain them.

$ref is a keyword used to reference other parts of a JSON or YAML configuration file or external file structure, commonly found in JSONSchema and OpenAPI specifications. 1. The basic syntax of $ref is {"$ref":"path"}, which can point to the internal inside of the current document (such as #/definitions/User) or external files (such as user-schema.json#/definitions/User). 2. Usage scenarios include reusing schemas, splitting large files, and organizing code structures. 3. Note that the path must be correct, avoid circular references, ensure that external files are accessible, and avoid excessive nesting.

Using slots and named slots in Vue can improve component flexibility and reusability. 1. The slot allows the parent component to insert content into the child component through a tag, such as inserting paragraph text into the Card.vue component; 2. The named slot realizes control of the content insertion position through the name attribute, such as defining the header, body and footer areas respectively in the modal box component; 3. The default content can be set in the slot as an alternative when the parent component is not provided, such as the default close button; 4. The # symbol is the abbreviation syntax of v-slot:; 5. It is recommended to use slots reasonably to avoid excessive dependence, and some content can be considered to be implemented through props or scope components.

Install VueI18n: Vue3 uses npminstallvue-i18n@next, Vue2 uses npminstallvue-i18n; 2. Create language files such as en.json and es.json in the locales directory, supporting nested structures; 3. Create instances through createI18n in Vue3 and mount them in main.js, Vue2 uses Vue.use(VueI18n) and instantiate VueI18n; 4. Use {{$t('key')}} interpolation in templates, use useI18n's t function in Vue3Composition API, and Vue2Options API

Computed has a cache, and multiple accesses are not recalculated when the dependency remains unchanged, while methods are executed every time they are called; 2.computed is suitable for calculations based on responsive data. Methods are suitable for scenarios where parameters are required or frequent calls but the result does not depend on responsive data; 3.computed supports getters and setters, which can realize two-way synchronization of data, but methods are not supported; 4. Summary: Use computed first to improve performance, and use methods when passing parameters, performing operations or avoiding cache, following the principle of "if you can use computed, you don't use methods".
