astro-cloudflare-pages-headers

February 20, 2026 ยท View on GitHub

npm GitHub

GitHub Stars npm downloads

GitHub Release Date GitHub last commit

GitHub Actions Workflow Status GitHub Actions Workflow Status GitHub Actions Workflow Status GitHub Actions Workflow Status

A lightweight integration for Astro that automatically generates a Cloudflare Pages _headers file for deployments based on your server header configuration.

Features

  • Automatic _headers generation: Reads header settings from your astro.config.mjs and generates a _headers file during build.
  • Flexible configuration: Supports both flat and nested header formats.
  • Workers wildcard normalization: Optionally remaps a universal * route to /* in generated _headers.
  • Optional CSP auto-hashes: Can scan built HTML and append CSP hashes for inline styles, style attributes, and inline scripts.
  • Informative logging: Provides useful log messages during setup and build.

Installation

Install the integration via the astro add command:

astro add astro-cloudflare-pages-headers

Usage

Add the integration to your Astro configuration file (astro.config.mjs). The integration looks for header settings in the server.headers property:

Example with Flat Headers

astro.config.mjs:

import { defineConfig } from 'astro/config';
import astroCloudflarePagesHeaders from 'astro-cloudflare-pages-headers';

export default defineConfig({
  integrations: [
    astroCloudflarePagesHeaders(),
  ],
  server: {
    headers: {
      'X-Custom-Header': 'my-value',
      'X-Another-Header': 'another-value'
    },
  },
});

This configuration generates the following _headers file:

/*
  X-Custom-Header: my-value
  X-Another-Header: another-value

Optional CSP Auto-Hashing

If you use CSP integrations (for example astro-shield) and want inline hashes added automatically, enable the optional CSP pass:

import { defineConfig } from 'astro/config';
import astroCloudflarePagesHeaders from 'astro-cloudflare-pages-headers';

export default defineConfig({
  integrations: [
    astroCloudflarePagesHeaders({
      csp: {
        autoHashes: true,
        hashStyleElements: true,
        hashStyleAttributes: true,
        hashInlineScripts: true,
        stripUnsafeInline: true,
        maxHeaderLineLength: 2000,
        overflow: 'error',
      },
    }),
  ],
});

csp options:

  • autoHashes (default: false): Enables post-build CSP patching.
  • mode (default: route): global unions hashes from all HTML into each CSP route. route emits route-specific CSP hashes per built HTML route and removes wildcard CSP headers to avoid wildcard/exact-route CSP conflicts at runtime.
  • hashStyleElements (default: true): Adds hashes for inline <style> blocks.
  • hashStyleAttributes (default: true): Adds hashes for style="" attributes (via style-src-attr + 'unsafe-hashes').
  • hashInlineScripts (default: true): Adds hashes for inline <script> blocks.
  • stripUnsafeInline (default: true): Removes 'unsafe-inline' from patched directives when hashes are injected.
  • maxHeaderLineLength (default: 2000): Maximum allowed length for each emitted header line.
  • overflow (default: error): Overflow behavior when a header line exceeds maxHeaderLineLength. Use error to fail the build or warn to log and continue.

By default (autoHashes: false), behavior is unchanged from previous versions.

Workers Mode

If you want to keep server.headers using a universal * route (which is often convenient in dev), enable workers mode to normalize it to /* in generated _headers:

import { defineConfig } from 'astro/config';
import astroCloudflarePagesHeaders from 'astro-cloudflare-pages-headers';

export default defineConfig({
  integrations: [
    astroCloudflarePagesHeaders({
      workers: true,
    }),
  ],
  server: {
    headers: {
      '*': {
        'X-Frame-Options': 'DENY',
      },
    },
  },
});

With workers: true, the generated _headers route becomes /*.

Example with Nested Headers

astro.config.mjs:

import { defineConfig } from 'astro/config';
import astroCloudflarePagesHeaders from 'astro-cloudflare-pages-headers';

export default defineConfig({
  integrations: [
    astroCloudflarePagesHeaders(),
  ],
  server: {
    headers: {
      '/api': {
        'Cache-Control': 'max-age=3600',
      },
      '/static': {
        'X-Frame-Options': 'DENY',
      },
    },
  },
});

This configuration generates the following _headers file:

/api
  Cache-Control: max-age=3600

/static
  X-Frame-Options: DENY

How It Works

Setup

astro:config:setup

The integration reads your header configuation from config.server.headers and stores it internally.

Build

astro:build:done

  • If headers are configured, it converts them into the appropriate Cloudflare Pages format.
  • If workers: true is enabled, a universal * route is normalized to /* before writing _headers.
  • If csp.autoHashes is enabled, it scans generated HTML and patches Content-Security-Policy header directives with hash sources.
  • It writes the generated content to a _headers file in your build output directory.
  • Logs inform you if the file is successfully written or if any errors occur.
  • If no headers are configured, it logs a warning and skips file generation.

Development

Running Unit Tests

This project uses Vitest for testing. To run the tests:

Unit tests cover various scenarios including flat headers, nested headers, error handling, and logging verification.

npm test
pnpm test
yarn test

Testing Your Header Configuration

MDN Observatory is a great tool for testing your headers. You can use it to test your headers locally or after deploying to Cloudflare Pages.

Contributing

Contributions and improvements are welcome. Feel free to open issues or submit pull requests on the repository.

License

This project is licensed under the MIT License. See the LICENSE file for details.

Resources