Skip to content

ernolf/files_sharing_raw

Repository files navigation

files_sharing_rawNextcloud raw file server



files_sharing_raw serves files as-is so you can link directly to the file itself (i.e. without any of Nextcloud's UI). This makes it easy to host static web pages, RSS feeds, images, or other assets and embed/link them elsewhere.

Design goals

  • Minimal: deliver bytes, not UI.
  • Fast: keep server work low (good for assets).
  • Quiet failures: plain 404 Not found (text/plain) for invalid/missing public shares (no Nextcloud HTML error pages), ideal for asset fetches.
  • Privacy-friendly: cookie-free responses (best effort).
  • Allowlist-gated: public raw access is opt-in — only explicitly enabled public share tokens are served.
  • Secure by default: strict CSP with optional per-scope overrides. *)
  • Streaming by default: for normal GET (200) responses, the body is streamed whenever possible instead of loading the entire file into memory.

*) For security and privacy, the content is served with a configurable Content-Security-Policy (CSP) header, allowing different policies per share token, path, file extension, or MIME type (with a safe hardcoded fallback).

Note

files_sharing_raw is the actively maintained successor to ernolf/raw, which stopped working with Nextcloud 32 due to breaking API changes (OCP\Share was removed). files_sharing_raw was rebuilt from the ground up to be compatible with Nextcloud 32 and later, while adding a proper database registry, a Files sidebar UI, per-share CSP overrides, webserver offload support, and more.
The longer app ID was chosen deliberately: from the outset, a pull request to Nextcloud core was planned to register files_sharing_raw in the rootUrlApps list — which is what enables the short, clean /raw/{token} URLs. That PR has been merged and the change ships with Nextcloud 32.0.7+ and 33.0.1+. On older patch releases a one-time manual patch is required (see Activating root alias URLs).


Table of contents


Quickstart

  1. Install/enable the app.

  2. Create a public share link for a file or folder in Nextcloud.

  3. In the share's Advanced settings panel (Files sidebar), enable the "Enable raw link" toggle.

  4. Access the raw URL:

    • https://my-nextcloud/raw/<token>

    and for folders:

    • https://my-nextcloud/raw/<token>/<path/to/file>
  5. (Optional) Alternatively or additionally, allowlist tokens in config/{raw.}config.php — useful for automation or custom link names.

  6. (Optional) Configure CSP policies via raw_csp.

Note

The short /raw/{token} URLs require the rootUrlApps entry described in Installation. Without it, the app automatically falls back to longer /apps/files_sharing_raw/{token} URLs.


URL forms

Public shares

If the share link is:

https://my-nextcloud/s/aBc123DeF456xyZ

then this app will serve the raw file at:

https://my-nextcloud/raw/aBc123DeF456xyZ

If the share is a folder, files within it are accessible as:

https://my-nextcloud/raw/aBc123DeF456xyZ/path/to/file

Private user files

A user can access their own private files (they must be logged in as that user). For example, a file named test.html in anansi's Documents folder would be available at:

https://my-nextcloud/raw/u/anansi/Documents/test.html

The /u/ prefix is required and cannot be omitted.

Note

Private files are served without any additional token allowlist check — the logged-in user's identity is the authorization gate.

Root aliases (/raw and /rss)

When the rootUrlApps entry is active (see Activating root alias URLs), the app uses short root alias URLs:

Purpose URL
Public share /raw/{token}
Public share + path /raw/{token}/{path}
Private file /raw/u/{userId}/{path}
RSS alias /rss or /rss/{path}

Note

/rss and /rss/{path} are convenience shortcuts that internally behave exactly like /raw/rss and /raw/rss/{path}. The underlying share token is rss — it must be enabled like any other token (UI toggle or config allowlist).

Fallback URLs (without rootUrlApps)

If the rootUrlApps entry is not yet active (see Installation, the app falls back to longer URLs:

Purpose URL
Public share /apps/files_sharing_raw/{token}
Public share + path /apps/files_sharing_raw/{token}/{path}
Private file /apps/files_sharing_raw/u/{userId}/{path}

The sidebar UI automatically shows the correct URL depending on whether root aliases are active. When root aliases are active, requests to fallback URLs are automatically 307-redirected to the canonical /raw/... form.


Enabling raw access

Public raw access is opt-in: a token must be explicitly allowed before the app will serve it. There are two ways to allow tokens — they can be combined freely, and the config allowlist always takes priority.

Via the Files sidebar

Open the share in the Files app (right sidebar → Advanced settings). Enable the "Enable raw link" toggle and click Update share. The share is immediately raw-accessible under /raw/{token}.

This toggle stores the enabled state per share in the database. The DB entry is automatically removed when the share is deleted.

Once the raw link is enabled, additional options become available via the three-dot menu (⋯) next to the raw link row:

Warning

Password-protected shares are served without a password check. Raw delivery is intentionally headless — there is no browser UI, no login form, and no place for a password prompt. As a result, /raw/{token} bypasses any password that is set on the Nextcloud share and delivers the content directly to anyone who has the raw URL.

Only enable raw access on shares whose content you are comfortable making publicly accessible. Do not enable raw access on password-protected shares unless you explicitly intend the content to be reachable without the password.

Via config: allowed_raw_tokens and wildcards

One or both of the following arrays in config/{raw.}config.php can be defined. Config always takes priority over the DB registry.

allowed_raw_tokens

An array of explicitly allowed tokens. These tokens must exactly match the share token used in raw links.

allowed_raw_token_wildcards

An array of wildcard patterns (*) matched against the share token. Wildcards are translated into regular expressions for dynamic validation.

Example configuration

<?php
$CONFIG = array (
// -
  'allowed_raw_tokens' =>
  array (
    0 => 'scripts',
    1 => 'aBc123DeF456xyZ',
    2 => 'includes',
    3 => 'html',
  ),
  'allowed_raw_token_wildcards' =>
  array (
    0 => '*suffix',
    1 => 'prefix*',
    2 => 'prefix*suffix',
    3 => '*infix*',
    4 => 'prefix*infix*',
  ),
// -
);

In this configuration:

  • Tokens such as scripts, aBc123DeF456xyZ, includes, and html are explicitly allowed.

  • Wildcards match the share token and can be used as:

    • suffix: *_jsondata_json
    • prefix: nc-*nc-assets
    • infix: *holiday_img*2026-02-10-holiday_img.jpg, 2026-02-12-holiday_img.png
    • combined: site-*_asset_*site-example.com_asset_script.js, site-other.example.com_asset_style.css

Usage with human-readable tokens

Generating human-readable tokens (instead of randomly generated ones) makes links more meaningful and easier to manage in both the UI toggle and the config allowlist.

For example: instead of a random token like aBc123DeF456xyZ, use a meaningful token such as html, javascript or data_json for shared directories, or apply prefixes/suffixes to enable wildcard matching.


Raw-only mode

When a share is flagged as raw-only, Nextcloud's standard share page (/s/<token>) returns 404 Not Found. The file (or folder) is then only accessible via the raw URL (/raw/<token>[/<path>]).

This is particularly useful for shared folders: without raw-only, anyone who has the share link can open the folder in Nextcloud's browser UI, navigate the directory tree, and discover all contained files. Enabling raw-only prevents any folder browsing entirely — only direct requests to known, explicit file paths via /raw/ will succeed.

Important

Raw-only does not grant /raw/ access on its own. The token must also be enabled for raw serving — either via the "Enable raw link" UI toggle or listed in allowed_raw_tokens / allowed_raw_token_wildcards. Raw-only only controls whether the standard /s/<token> share page is additionally blocked.

Via the Files sidebar

In the Files sidebar, once "Enable raw link" is active for a share, open the three-dot menu (⋯) in the raw link row and tick "Raw only". From that moment on, /s/<token> returns 404 for that share — the file or folder is only reachable via /raw/<token>.

raw_only_tokens

An array of tokens for which the standard Nextcloud share page (/s/<token>) is blocked. Accepts the same format as allowed_raw_tokens (exact token strings).

raw_only_token_wildcards

An array of wildcard patterns (*) matched against the share token. Accepts the same format and wildcard syntax as allowed_raw_token_wildcards.

Example configuration

<?php
$CONFIG = array (
// -
  // Grant /raw/ access to these tokens:
  'allowed_raw_tokens' =>
  array (
    0 => 'html',
    1 => 'platform',
  ),
  'allowed_raw_token_wildcards' =>
  array (
    0 => 'nc-*',
  ),

  // Additionally block /s/ for these tokens (raw-only):
  'raw_only_tokens' =>
  array (
    0 => 'html',
    1 => 'platform',
  ),
  'raw_only_token_wildcards' =>
  array (
    0 => 'nc-*',
  ),
// -
);

In this configuration html, platform, and any nc-* token are accessible via /raw/ but their Nextcloud share pages (/s/html, /s/platform, /s/nc-assets, …) all return 404.

Note

A token may appear in raw_only_tokens without being in allowed_raw_tokens. In that case, both /s/<token> and /raw/<token> return 404 — which is a valid but unusual setup (e.g. for tokens managed exclusively via the DB/UI toggle while still enforcing raw-only via config).


Content Security Policy

files_sharing_raw sends a Content-Security-Policy header with every raw response. Policies can be set per share via the Files sidebar or globally via the system config key raw_csp in config/{raw.}config.php. Both methods share a common evaluation order — the most specific matching rule wins.

Note

If no CSP matches a request (no per-share override, no matching config rule), the app falls back to this safe, very restrictive default:

"sandbox; default-src 'none'; style-src data: 'unsafe-inline'; img-src data:; media-src data:; font-src data:; frame-src data:"

This fallback is hardcoded inside the app (not in config.php).

Matching priority

When deciding which CSP to send, the app evaluates selectors in this order:

  • token (config) — exact match for a public share token in raw_csp['token'] (highest priority).
  • Per-share CSP — custom CSP stored via the UI or REST API (applies if the share is raw-enabled and a custom CSP is set; lower priority than config token, higher than path rules).
  • path_prefix — longest matching prefix. Supports absolute prefixes (starting with /apps/files_sharing_raw) and relative prefixes (matched against the path after the app prefix and token).
  • path_contains — substring match. Checked against both the full request path and the path after the app prefix, so public and private URLs are covered.
  • extension — file extension match (e.g. html, json).
  • mimetype — MIME type match (e.g. text/html, application/json).
  • hard-coded fallback (if nothing matches).

Note

token (config) is the share token that appears in public URLs. Private user paths (/raw/u/...) do not carry a share token — token and per-share CSP overrides cannot match on private URLs.

Per-share CSP (Files sidebar)

Note

Edit CSP is restricted to the admin group by default. To delegate this to a custom group, create the group, add the permitted users, then point the app to it:

occ group:add raw_csp_allowed
occ group:adduser raw_csp_allowed <uid>
occ config:app:set files_sharing_raw csp_editor_group --value="raw_csp_allowed"

Users outside the configured group (raw_csp_allowed is just an example name) see no Edit CSP entry in the menu and cannot change a share's CSP via the API.

In the three-dot menu (⋯) next to the raw link row, Edit CSP opens an inline panel for setting a per-share Content-Security-Policy override. The value is stored in the database and takes effect immediately for all subsequent raw requests to this share. Its priority in the matching chain is: below the config token rule, above all path/extension/mimetype rules.

The panel contains a preset dropdown and an editable text field:

Preset Stored CSP value Suited for
Server default (empty — falls back to server-wide rules) General use; no override
Sandbox (strict) sandbox; default-src 'none'; form-action 'none' Maximum isolation (no sub-resources at all)
Images only default-src 'none'; img-src 'self' data: blob:; form-action 'none' Image files
Documents (PDF / text) default-src 'none'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; font-src 'self' data:; form-action 'none' PDFs, text documents
Audio / Video default-src 'none'; media-src 'self' data: blob:; img-src 'self' data:; form-action 'none' Audio / video files
Custom (keeps current text unchanged) Any hand-crafted policy

Selecting a preset fills the text field with the corresponding CSP string. The presets are starting points, not fixed values — the text field is always freely editable. Refine or extend the preset value before hitting Save, and the modified string is what gets stored.

When the text field is edited manually and its content no longer matches any preset, the dropdown automatically switches to Custom — a visual indicator that the stored value is user-defined.

Note

Setting the per-share CSP to "Server default" (empty string) removes the override — the server-wide raw_csp rules apply as usual.

Config-based CSP (raw_csp)

The raw_csp system config key lets admins define CSP rules for different paths, file extensions, MIME types, or share tokens. These rules apply globally and are evaluated after any per-share CSP override (see matching priority above).

Policy formats accepted

A policy value for a selector may be one of:

  • String — a full, single-line CSP header value (passed through and sanitized).
  • Indexed array — list of directive strings; entries are joined with ;.
  • Associative array (recommended) — 'directive' => sources. sources may be a string (space separated) or an array of strings. The manager normalizes values, deduplicates and outputs a canonical single-line header.

Allowed directives

Allowed directive names are deliberately limited (to keep policies sane and safe):

Unknown/unsupported directives are ignored by the manager.

Config examples

  1. Extension rule — make all .html permissive
<?php
$CONFIG = array (
// -
  // Example: make all .html files more permissive
  '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'"],
      ),
    ),
  ),
// -
);
  1. Path prefix — relative and absolute
<?php
$CONFIG = array (
// -
  // Example: an absolute prefix and a relative prefix
  'raw_csp' =>
  array(
    'path_prefix' =>
    // absolute prefix: matches full URI starting at /apps/files_sharing_raw/...
    array(
      '/apps/files_sharing_raw/s/special-html/' =>
      array(
        'default-src' => ["'self'"],
        'script-src'  => ["'self'"],
      ),
      // relative prefix: matched against the path AFTER /apps/files_sharing_raw[/{token}] or /apps/files_sharing_raw/u/{user}/
      'html/' =>
      array(
        'default-src' => ["'self'"],
        'script-src'  => ["'self'", "'unsafe-inline'"],
        'img-src'     => ["'self'", "data:"],
      ),
    ),
  ),
// -
);
  1. Path contains — substring match (public + private)
<?php
$CONFIG = array (
// -
  // Example: apply when '/html/' appears anywhere in the path
  'raw_csp' =>
  array(
    'path_contains' =>
    array(
      '/html/' =>
      array(
        'default-src' => ["'self'"],
        'script-src'  => ["'self'"],
        'img-src'     => ["'self'", "data:"],
        'style-src'   => ["'self'", "'unsafe-inline'"],
      ),
    ),
  ),
// -
);
  1. Token — per share-token policy (optional)
<?php
$CONFIG = array (
// -
  // Example: apply a policy only for the public share token 'abc123'
  // This only applies when the public URL contains the token 'abc123'.
  'raw_csp' =>
  array(
    'token' =>
    array(
      'abc123' =>
      array(
        'default-src' => ["'self'"],
        'img-src'     => ["'self'", "data:"],
      ),
    ),
  ),
// -
);
  1. Combined example
<?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:"],
      ),
    ),
  ),
// -
);

Important note about path_contains matching:

If a pattern starts with a slash (for example '/html/'), the pattern is used verbatim as a substring search. '/html/' only matches when the exact sequence "/html/" appears in the request path (use this to target a folder segment precisely).

If a pattern does not start with a slash (for example 'html'), the pattern is used as a plain substring (no leading slash is added). 'html' therefore matches anywhere the characters html appear — e.g. /some_html_text/, /some-html-data/, /htmlfile and /html/.

Consequence: some-html-data will match the pattern 'html' but will not match '/html/'.

Recommendation: use '/folder/' when you need to match a folder segment exactly; use a plain token like 'foo' when you intentionally want a broad substring match.

The manager checks path_contains against both the full request path and the path portion after the app prefix, so public and private URLs are covered.

Testing

After you update config/{raw.}config.php (or deploy changes), test with curl:

  • Public share (root alias URL):

    curl -I 'https://your-instance.example/raw/html/calc.html'
  • Private user URL:

    curl -I 'https://your-instance.example/raw/u/alice/Documents/html/calc.html'

Inspect the Content-Security-Policy: response header. If you do not get the expected policy:

  • make sure the selector matches your URL form (token vs path vs extension),
  • check nextcloud.log for exceptions from CspManager or syntax errors in your config array,
  • remember that token only matches explicit share tokens (not private URLs).

Performance & caching

Cache-Control

Public responses use a configurable Cache-Control header:

  • raw_cache_public_max_age (int seconds, default: 300)
  • raw_cache_public_stale_while_revalidate (int seconds, default: 30; 0 disables)
  • raw_cache_public_stale_if_error (int seconds, default: 86400; 0 disables)

Private raw URLs (/raw/u/...) default to private, max-age=0.

Optionally enforce no-store for private URLs:

  • raw_cache_private_no_store (bool, default: false)

Note

304 Not Modified responses apply the same Cache-Control policy (public vs. private) as normal 200 responses, so caches behave consistently across conditional requests.

Webserver offload

For large files you can optionally let the webserver send the file body (PHP returns early):

  • raw_sendfile_backend (off|apache|nginx default: off)
  • raw_sendfile_allow_private (bool, default: false) *)
  • raw_sendfile_min_size_mb (int, default: 0) **)
  • raw_sendfile_nginx_prefix (string, default: /_raw_sendfile)

Warning

Webserver offload requires correct webserver configuration. The app enforces its own path restriction (files must be inside Nextcloud's datadirectory), but the webserver-side configuration is entirely the administrator's responsibility.

  • Nginx — internal; is mandatory. Without it, the /_raw_sendfile/ location is reachable directly from the internet, bypassing all PHP authorization checks. Any file inside the Nextcloud data directory would be accessible to anyone without authentication. Always verify that the location block carries the internal; directive before enabling offload.
  • Apache — XSendFilePath is recommended defense-in-depth. The app only ever sends paths within the datadirectory via X-Sendfile, so a missing XSendFilePath does not open an independent attack path. However, configuring it explicitly limits the blast radius should any unexpected behavior occur in the module itself.

Note

*) By default, offload is disabled for private raw URLs (/raw/u/...) to keep authenticated endpoints conservative by default. Enable raw_sendfile_allow_private to allow webserver offload for private raw responses too.

Note

**) If raw_sendfile_min_size_mb is set, offload is only attempted when the file size is known and meets the threshold. If the size cannot be determined (e.g. certain storage backends), offload is skipped.

Prerequisites (webserver configuration required):

  • Apache:
    • Requires mod_xsendfile *) (or an equivalent X-Sendfile implementation) to be installed and enabled.
    • Enable it and configure the allowed path(s) to include your Nextcloud datadirectory:
      XSendFile On
      # Use the *real* Nextcloud datadirectory from `config/config.php` -> 'datadirectory'
      XSendFilePath /path/to/nextcloud/data

Note

*) Module naming varies by distribution; the key requirement is that your Apache build supports X-Sendfile and that the module is enabled for the vhost serving Nextcloud.

  • Nginx:
    • Uses X-Accel-Redirect (built into nginx, no extra module needed).
    • Requires an internal location that maps the configured prefix (default /_raw_sendfile) to Nextcloud's data directory via alias.
    • Example (must match your raw_sendfile_nginx_prefix and Nextcloud datadirectory):
      location /_raw_sendfile/ {
          internal;
          alias /path/to/nextcloud/data/;
      }

Tip

The app builds the Nginx X-Accel-Redirect target by stripping the resolved (realpath) Nextcloud datadirectory prefix from the local file path. Ensure your Nginx alias uses the same resolved datadirectory path (and includes a trailing /). If datadirectory is a symlink but Nginx points to the symlink path (or vice versa), the mapping can mismatch and offload will be skipped.

Warning

Nginx offload bypasses PHP-set Content-Security-Policy headers. When X-Accel-Redirect is used, nginx serves the file body directly — it does not forward custom response headers set by PHP, including Content-Security-Policy. As a result, offloaded responses carry no CSP header at all.

Recommendation: set raw_sendfile_min_size_mb to a meaningful threshold (e.g. 10) so that small files — HTML pages, text files, RSS feeds, images — where a CSP is security-relevant are served by PHP (with full CSP enforcement), while only large binary files — videos, archives, large data blobs — where a CSP is not meaningful are offloaded to nginx.

If you require CSP on all responses regardless of file size, do not use raw_sendfile_backend = 'nginx'.

Example offload configuration in config/{raw.}config.php (for apache2):

<?php
$CONFIG = array (
// -
  // Private raw caching
  'raw_cache_private_no_store' => false, // true = Never save in browser

  // apache2
  'raw_sendfile_backend' => 'apache',
/*
  // nginx
  'raw_sendfile_backend' => 'nginx',
  'raw_sendfile_nginx_prefix' => '/_raw_sendfile',
*/

  // allow offload also for /raw/u/... (default false)
  'raw_sendfile_allow_private' => false,

  // only offload for files >= X MB (default 0 = no threshold)
  'raw_sendfile_min_size_mb' => 5,
// -
);

Offload debug header

To debug whether offload/streaming was used, send this request header:

  • X-Raw-Offload-Debug: 1

The response may include:

  • X-Raw-Offload: <status>; reason=<reason>

Note

When offload is active and actually used, the response may include an X-Raw-Offload header (e.g. apache-xsendfile / nginx-x-accel) even without debug enabled.
If you send X-Raw-Offload-Debug: 1, the app adds reason=... and can also emit a "not offloaded" reason, which is useful to validate your config and thresholds.

HTTP behavior

Cookie-free responses

files_sharing_raw intentionally aims to be cookie-free. It will best-effort prevent Set-Cookie from being emitted for raw responses (e.g. by closing any active session, disabling session cookies for the remainder of the request, and removing already queued Set-Cookie headers).

This keeps endpoints "naked" for asset serving and reduces overhead. (Best effort: a reverse proxy could still add cookies afterwards.)

ETags and Last-Modified

files_sharing_raw supports conditional requests (cache validation) using ETags together with the If-None-Match header and also supports Last-Modified / If-Modified-Since semantics.

Note

The app prefers "fast" validators (mtime + size) for ETag generation and only falls back to a content hash when needed.

  • ETag / If-None-Match: The server sends an ETag header identifying the current representation of the file. If the client sends If-None-Match: "<ETag>" and the value matches, the server responds with 304 Not Modified and no response body. The wildcard If-None-Match: * is also supported.
  • Last-Modified / If-Modified-Since: When the server can read file modification time (mtime) it sets a Last-Modified header. The server will honor If-Modified-Since when If-None-Match is not present. If the client date is equal to or newer than the file mtime, the server responds with 304 Not Modified.
  • Unix timestamp convenience: For convenience, If-Modified-Since accepts either an RFC-style HTTP-date (recommended) or a plain Unix timestamp (seconds). The server will trim optional quotes.

Examples:

  • Get file and see headers + body (returns ETag and Last-Modified):

    curl -i 'https://your.nextcloud/raw/.../file.ext'
  • Conditional GET using ETag (replace <ETag> with the ETag value returned by the server):

    curl -i -H 'If-None-Match: "<ETag>"' 'https://your.nextcloud/raw/.../file.ext'
  • Conditional GET using HTTP-date:

    curl -i -H 'If-Modified-Since: "Sun, 25 May 2025 21:40:02 GMT"' 'https://your.nextcloud/raw/.../file.ext'
  • Conditional GET using Unix timestamp (convenience):

    curl -i -H 'If-Modified-Since: "1748209203"' 'https://your.nextcloud/raw/.../file.ext'
  • The wildcard If-None-Match: * is also supported (returns 304 if the resource exists):

    curl -i -H 'If-None-Match: *' 'https://your.nextcloud/raw/.../file.ext'

Directory handling (index.html)

If the requested node is a directory, the app attempts to serve index.html from that directory.

HEAD requests

files_sharing_raw supports HEAD requests (headers only, no response body).

Plain 404 for invalid public shares

For public endpoints, the app returns a minimal text/plain 404 Not found response for disallowed/unknown tokens, missing shares, and missing paths. This avoids rendering large HTML error pages and keeps endpoints lightweight.


Notes & best practices

  • Do not enable raw access on password-protected shares unless you explicitly intend the content to be publicly reachable without a password. Raw delivery is headless by design — there is no password prompt, so the share password is bypassed entirely.
  • Review and update allowed_raw_tokens and allowed_raw_token_wildcards periodically to align with your security requirements. Alternatively, manage access via the Files sidebar UI per share.
  • Validate CSP rules and token configurations in a test environment before applying them in production.
  • Prefer extension or path-based matching for predictable results. path_contains with '/html/' is usually the safest way to target a folder named html.
  • Avoid script-src 'unsafe-inline' unless absolutely necessary. When you need inline scripts, prefer nonces or restrictive policies.
  • Keep the token selector (in raw_csp) only if you want per-share (per-token) policies from config. If you do not need that granularity, it is safe to remove token and rely on path/extension/mimetype rules. Per-share CSP can also be set via the UI (stored in DB).
  • The manager normalizes directives and removes duplicates; unknown directives are ignored (no crash but check logs).

Keep raw settings in a dedicated config file

  • Nextcloud can load settings from multiple files in config/. For files_sharing_raw, it's recommended to keep all raw-related directives like allowed_raw_tokens, allowed_raw_token_wildcards, raw_csp etc. in a dedicated config/raw.config.php (any *.config.php in config/ is loaded and merged alongside config.php).
  • This keeps raw-specific security settings isolated, avoids accidental clutter in config.php, and plays nicely with config management.
  • Gotcha: Nextcloud can consolidate config values into config/config.php. Don't rely on occ for raw settings if config/raw.config.php exists — raw.config.php has precedence and will override later.

Installation

From the Nextcloud App Store

The easiest way to install this app is via the Nextcloud App Store:

  1. Log into Nextcloud as admin.
  2. Go to Apps → search for Raw Fileserver → Install.

Manual installation (release tarball)

  1. Download the latest release tarball (files_sharing_raw.tar.gz) from the GitHub Releases page.
  2. Extract it into your Nextcloud /apps (or /custom_apps) folder:
    tar -xzf files_sharing_raw.tar.gz -C /path/to/nextcloud/apps/
  3. Enable the app:
    occ app:enable files_sharing_raw
    or log into Nextcloud as admin and enable it in the Apps list.

Developer setup (from source)

  1. Clone the repository into your Nextcloud /apps (or /custom_apps) folder:
    git clone https://github.com/ernolf/files_sharing_raw
    cd files_sharing_raw
  2. Install frontend dependencies and build the JS bundle:
    npm ci
    npm run build
  3. Enable the app:
    occ app:enable files_sharing_raw

Activating root alias URLs (/raw/)

To use the short /raw/{token} URLs instead of the longer /apps/files_sharing_raw/{token} fallback, files_sharing_raw must be registered in Nextcloud core's rootUrlApps list. The pull request for this has been merged and ships with Nextcloud 32.0.7+ and 33.0.1+. On those versions no manual action is needed.

If you are running an older patch release (below 32.0.7 or 33.0.1), the entry must be added once manually.

The change is a single line in lib/private/AppFramework/Routing/RouteParser.php:

private const rootUrlApps = [
    'cloud_federation_api',
    'core',
    'files_sharing_raw',   // ← add this line
    'files_sharing',
    // ...
];

A patch script is included in the app directory — just make it executable and run it:

chmod +x patch-route-parser.sh && ./patch-route-parser.sh

The script is idempotent — it finds RouteParser.php automatically and is safe to run multiple times.

Note

Nextcloud AIO / Docker users: see the AIO patch guide for step-by-step instructions on how to apply the patch inside the container.

Note

On Nextcloud 32.0.7+ and 33.0.1+ this manual step is not needed — the entry ships with the core update. On older patch releases, the patch is a one-time action and does not need to be repeated after subsequent Nextcloud updates (those updates will already carry the entry).

Without this entry the app still works — it simply uses the longer fallback URLs.

Migrating from the raw app

If you previously used the original raw app (which stopped working with Nextcloud 32):

  1. Disable raw: occ app:disable raw
  2. Install and enable files_sharing_raw (see above).

All raw_* config keys (allowed_raw_tokens, raw_csp, etc.) are reused automatically — no data migration is needed.

Updating

Via the Nextcloud App Store

Update directly from the Apps page in the Nextcloud admin UI — no manual steps needed.

Manual update

  1. Disable the app:

    occ app:disable files_sharing_raw
  2. Update the app files — either via

    or via

    in the app directory.

  3. Enable the app again:

    occ app:enable files_sharing_raw