Skip to content

CSP configuration

ernolf edited this page Oct 11, 2026 · 4 revisions

🧾 CSP configuration in the config file (raw_csp)

Tip

TL;DR raw_csp in config/raw.config.php sets CSPs by token, path, extension or MIME type.

The rules apply to all shares. The order in which selectors and the per-share CSP are tried is described in Content Security Policy.

✍️ Policy formats

A policy may be written as

  • a string: a complete single-line header value, passed through after control characters and repeated whitespace are removed;
  • an indexed array: directive strings, joined with ; ;
  • an associative array (recommended): 'directive' => sources, where sources is a space-separated string or an array of strings. Sources are deduplicated, directives are put into a fixed order and joined into one header line.

📋 Allowed directives

In the associative format only these directive names are accepted; unknown ones are skipped. String and indexed policies are not filtered.

Group Directives
Fetch, fallbacks default-src, script-src, style-src, child-src
Fetch, common connect-src, font-src, frame-src, img-src, manifest-src, media-src, object-src, worker-src
Document base-uri, sandbox
Navigation form-action, frame-ancestors
Other upgrade-insecure-requests

🧩 Selectors

Selector Key Matches
token share token the token of a public share URL; never a private URL
path_prefix starts with /raw or /rss absolute prefix of the request path, longest match wins
path_prefix anything else prefix of the path after the token (public) or after /u/{user} (private), longest match wins
path_contains substring the full request path or the path after the token or user
extension html, json, ... file extension, lower case
mimetype text/html, ... MIME type, lower case

path_contains with or without slashes

  • A pattern starting with a slash, for example '/html/', matches only that exact sequence, so it targets a folder segment.
  • A pattern without a leading slash, for example 'html', is a plain substring and matches /some_html_text/, /some-html-data/, /htmlfile and /html/ alike.

Use '/folder/' to match a folder exactly, and a plain word only when the broad match is intended.

📚 Examples

Extension: permissive .html

<?php
$CONFIG = array (
  'raw_csp' =>
  array(
    'extension' =>
    array(
      'html' =>
      array(
        'default-src' => ["'self'"],
        'script-src'  => ["'self'", "'unsafe-inline'"],
        'img-src'     => ["'self'", "data:"],
        'media-src'   => ["data:"],
        'style-src'   => ["'self'", "'unsafe-inline'"],
        'font-src'    => ["data:"],
        'frame-src'   => ["'none'"],
      ),
    ),
  ),
);

Path prefix: absolute and relative

<?php
$CONFIG = array (
  'raw_csp' =>
  array(
    'path_prefix' =>
    array(
      // absolute: everything below the share with the token 'special-html'
      '/raw/special-html/' =>
      array(
        'default-src' => ["'self'"],
        'script-src'  => ["'self'"],
      ),
      // relative: matched against the path after the token or after /u/{user}
      'html/' =>
      array(
        'default-src' => ["'self'"],
        'script-src'  => ["'self'", "'unsafe-inline'"],
        'img-src'     => ["'self'", "data:"],
      ),
    ),
  ),
);

Path contains: public and private

<?php
$CONFIG = array (
  'raw_csp' =>
  array(
    'path_contains' =>
    array(
      '/html/' =>
      array(
        'default-src' => ["'self'"],
        'script-src'  => ["'self'"],
        'img-src'     => ["'self'", "data:"],
        'style-src'   => ["'self'", "'unsafe-inline'"],
      ),
    ),
  ),
);

Token: one share

<?php
$CONFIG = array (
  'raw_csp' =>
  array(
    'token' =>
    array(
      'abc123' =>
      array(
        'default-src' => ["'self'"],
        'img-src'     => ["'self'", "data:"],
      ),
    ),
  ),
);

Combined

<?php
$CONFIG = array (
  'raw_csp' =>
  array(
    'path_prefix' =>
    array(
      'html/' =>
      array(
        'default-src' => ["'self'"],
        'script-src'  => ["'self'", "'unsafe-inline'"],
      ),
    ),
    'path_contains' =>
    array(
      '/public/static/' =>
      array(
        'default-src' => ["'self'"],
        'img-src'     => ["'self'", "data:"],
      ),
    ),
    'extension' =>
    array(
      'json' =>
      array(
        'default-src' => ["'none'"],
        'img-src'     => ["data:"],
      ),
    ),
  ),
);

🧪 Testing

raw_csp is read on every request, so a change applies immediately. Check the header:

curl -sI 'https://cloud.example.com/raw/html/calc.html' | grep -i '^content-security-policy'
curl -sI 'https://cloud.example.com/raw/u/alice/Documents/html/calc.html' | grep -i '^content-security-policy'

If the policy is not the expected one:

  • check that the selector fits the URL form (token, path, extension);
  • check that no selector earlier in the matching order catches the request first;
  • remember that token never matches private URLs.

➡️ Next

Caching and HTTP behavior

Clone this wiki locally