Skip to content
HomeHome
DE
WhatsAppMailPhone
← All articles
Astro 7.1: setting up a Content Security Policy without hand-maintaining the header
Coding

Astro 7.1: setting up a Content Security Policy without hand-maintaining the header

Photo: fantasyflip / Unsplash

Astro 7.1 shipped on 16 July 2026 - a minor release with no breaking changes. The practical win: the built-in Content Security Policy is now more granular. How to set it up step by step, plus the smaller changes to pagination, content collections and the dev server.

Eric MengeAuthorEric MengeOwner & web developer at EMIT Solution
Published
Reading timeca. 8 min

In short

  • Astro 7.1 shipped on 16 July 2026 as a minor release with no breaking changes - installed with one command, no code rework. Unlike the 7.0 jump, which was a major with real migration work.
  • The centrepiece is the built-in Content Security Policy. Astro generates the policy from the actual build - it knows every script and every style fragment it ships and writes the matching hashes itself. Exactly the manual work you otherwise redo with every new script.
  • 7.1 adds four more granular directives (script-src-elem, script-src-attr, style-src-elem, style-src-attr) and lets you open single cases deliberately - for example allowing inline styles without loosening the rules for external CSS files.
  • Smaller additions: paginate() gains a format function for the URL shape, content collections save memory with deferRender, and the dev server runs multiple instances in parallel with --ignore-lock.

Astro’s 7.0 release was a major, with everything a major brings: a new Rust compiler, a different bundler, a handful of breaking changes to check first. How uneventful that turned out in practice I wrote up when upgrading this site to Astro 7. 7.1 is the exact opposite: a minor release you install and then forget - or better, install and then use to set up one thing that used to be more of a chore than it needed to be.

Astro 7.1 shipped on 16 July 2026. It brings no breaking changes, but a noticeably more usable Content Security Policy, a small yet sensible pagination option, and two moves that ease memory pressure on large content collections. In order - and with the focus on the part that pays off immediately.

What 7.1 contains

The scope is modest, which is the point of a minor. The items that actually change something in practice:

  • Content Security Policy, more controllable: four new directives that let you allow scripts and styles more precisely.
  • Pagination with its own URL shape: paginate() gains a format function that can rewrite every generated URL.
  • Content collections, leaner: deferRender puts off rendering until access, and an experimental option splits large data stores into chunks.
  • Dev server in parallel: an --ignore-lock flag lets several instances of the same project run side by side.

Source code with markup on a screen Photo: florianolv / Unsplash

You install it like any Astro update, via npx @astrojs/upgrade or directly with astro@latest. A minor with no breaking changes means the build that ran before keeps running. It only gets interesting once you touch the new capabilities.

Content Security Policy: the header you otherwise hand-maintain

A Content Security Policy is one of the most effective and least loved security measures on the web. The idea is simple: the browser gets a list of which sources it may load and run scripts, styles, images and the rest from. Anything not on the list is blocked. If foreign code slips into the page through a gap, the browser simply doesn’t run it - the policy doesn’t cover it. Against cross-site scripting there is barely anything better.

It’s unloved because the upkeep was tedious. Inline scripts and inline styles need either a blanket 'unsafe-inline' - which gives up a good part of the protection again - or a hash for each individual fragment. And that hash changes the moment a single character of the code changes. Maintain the policy by hand as a header or meta tag, add an analytics snippet, forget to update the hash, and half the page stops loading. Exactly that friction is why many projects end up back on 'unsafe-inline' or drop CSP altogether.

A padlock on a laptop keyboard Photo: flyd2069 / Unsplash

Astro’s built-in CSP flips it around: you don’t maintain the policy, Astro derives it from the actual build. At build time the framework knows every script and every style fragment it ships and writes the policy to match - including the hashes for inline code you would otherwise track by hand. In its basic form, a single switch in astro.config is enough:

// astro.config.mjs
import { defineConfig } from 'astro/config';

export default defineConfig({
  security: {
    csp: true,
  },
});

From then on, every page ships a Content Security Policy that matches the code it serves. Add a script later, and the matching hash becomes part of the policy on the next build automatically. The “forgot the hash” failure mode disappears, because nobody enters a hash by hand anymore.

What 7.1 makes finer about the policy

The switch alone covers the standard case. In real projects, though, you want a single exception more often than expected - without opening up the whole category - and that is exactly where 7.1 comes in. New are four more granular directives: script-src-elem, script-src-attr, style-src-elem and style-src-attr. They are specialised versions of script-src and style-src, and they separate what used to sit in one bucket: -elem governs the actual <script> and <style> elements, -attr the inline attributes such as a style="..." set directly on an element.

The practical benefit: you can allow one messy but unavoidable case deliberately without loosening the rest. A typical example is inline style attributes set by a third-party component. Instead of allowing inline styles wholesale, you open only the attribute variant:

// astro.config.mjs
export default defineConfig({
  security: {
    csp: {
      styleDirective: [
        { resource: "'unsafe-inline'", kind: "attribute" },
      ],
    },
  },
});

This accepts inline styles as “unsafe” without giving up safety for external CSS files - those keep running under the strict rule. The full list of options is in the Astro configuration docs; the pattern stays the same either way: start narrow and open only the concrete case you actually need.

This site runs on Astro itself, and a CSP header is one of those things you used to set up cleanly exactly once and then watch nervously with every change. Having the framework derive the policy from the build, so you only name the real exceptions, is the difference between “planned, when there’s time” and “done”.

Pagination: URLs that map to real files

Astro’s paginate() turns a long list into the individual page routes - page 1, 2, 3 and so on. In 7.1 the function takes an additional format parameter that lets you rewrite every generated URL:

export const getStaticPaths = ({ paginate }) => {
  return paginate(posts, {
    pageSize: 10,
    format: (url) => `${url}.html`,
  });
};

Sounds like a detail, but it solves a real problem in static builds: some hosts expect a URL to point to an actual file on disk. A “clean” URL without an extension then doesn’t match the file structure, and you work around it with server rules. The format function takes each generated URL and returns the one you actually need - here one with a .html ending that matches the file name.

Content collections: less memory on large sets

Anyone with many Markdown files in a content collection knows the effect: the sync step pulls everything into memory, and with several hundred entries that gets noticeable. 7.1 offers two approaches.

The first is the deferRender option on the glob loader. It defers rendering an entry to the moment it is actually accessed, instead of rendering everything up front:

// src/content.config.ts
const docs = defineCollection({
  loader: glob({
    pattern: '**/*.md',
    base: 'src/content/docs',
    deferRender: true,
  }),
});

The second is experimental and aimed at very large data stores: with collectionStorage: 'chunked', Astro splits the data into several files once a chunk reaches 10 MB. That gets around some platforms’ size limits for individual files.

export default defineConfig({
  experimental: {
    collectionStorage: 'chunked',
  },
});

Rows of server fans in a data centre Photo: winstonchen / Unsplash

For a typical company blog with a few dozen articles, neither is necessary. But once a documentation site or a catalogue with thousands of entries is in play, the difference in memory use is the point at which a CI build either finishes or aborts.

Dev server and logger

Two details for daily work. Astro 7 introduced a lock that stops you from accidentally starting the same dev server twice. Sometimes, though, that is precisely what you want - two instances of the same project side by side. For that there is now astro dev --ignore-lock. The catch: instances started this way don’t show up in astro dev stop, astro dev status and astro dev logs, because those commands don’t track them.

On top of that, logging can be replaced through a custom entry point. If you want to route Astro’s output into your own format or system, you point the config at your own logger file:

export default defineConfig({
  logger: {
    entrypoint: new URL('./src/custom-logger.js', import.meta.url),
  },
});

A new AstroRuntimeLogger type provides the matching typing when you pass the logger into your own utility files.

Conclusion

Astro 7.1 isn’t a release you pay for with a weekend of migration - it’s one you pick up along the way. No breaking change, one command, and afterwards a Content Security Policy that follows from the build instead of being maintained in a text file. If you skipped CSP so far because of the effort, this version takes away your best excuse.

The through-line is the same as with the recent Astro releases, and one of the reasons I build company websites on Astro: the framework takes the thankless work off your hands without asking you to rebuild what already works.

If you run an Astro site and want a clean Content Security Policy header without wrestling with hashes: I’m Eric Menge of EMIT Solution, reachable at info@emit-solution.com and via emit-solution.com. I set up security headers like this on Astro projects and am happy to look at where the concrete exceptions in your setup are.

FAQ

Do I have to change any code for Astro 7.1?+

No. 7.1 is a minor release with no breaking changes. An npx @astrojs/upgrade or astro@latest installs it, and the build that ran before runs after. The new capabilities - the finer CSP control above all - are options you switch on when you need them, not requirements.

What is a Content Security Policy and why should I care?+

A CSP header tells the browser which sources it may load and execute scripts, styles and other resources from. It is the most effective brake on cross-site scripting: even if foreign code makes it into the page, the browser won't run it unless the policy covers it. The price used to be the manual upkeep of that policy - and that is exactly what Astro takes off your plate.

What exactly is new about CSP in 7.1?+

The Content Security Policy already existed in Astro. 7.1 adds four more granular directives - script-src-elem, script-src-attr, style-src-elem and style-src-attr - as specialised versions of script-src and style-src. They let you open individual cases deliberately, such as allowing inline style attributes without loosening the rules for external CSS files along with them.

Is it worth moving from 7.0 to 7.1 right away?+

Yes, because it costs nothing: no breaking change, one command. Whether you use the new CSP directives, the pagination option or deferRender straight away depends on your project - but there is no reason to stay on 7.0.

Want to know more?

In a free intro call we discuss how you can use these topics for your company. Not a sales pitch, but an honest assessment.

Book a free intro call