Skip to content

protect nginx temp directories from purge walks - #66

Open
hsntgm wants to merge 1 commit into
nginx-modules:masterfrom
psaux-it:protect-temp-dirs
Open

protect nginx temp directories from purge walks#66
hsntgm wants to merge 1 commit into
nginx-modules:masterfrom
psaux-it:protect-temp-dirs

Conversation

@hsntgm

@hsntgm hsntgm commented Jun 16, 2026

Copy link
Copy Markdown

This patch adds a safety mechanism to prevent purge walks (purge_all,
wildcard, and vary‑aware exact purges
) from deleting files inside
Nginx's own temporary directories.

The problem:

  • Nginx's documented best practice is to put *_temp_path and *_cache_path
    on the same filesystem for atomic rename().
  • This is frequently (mis)implemented as nesting *_temp_path under
    *_cache_path, e.g.:
    proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=z:10m;
    proxy_temp_path /var/cache/nginx/proxy_temp;
  • In that layout, a purge walk starting from /var/cache/nginx descends
    into proxy_temp and deletes in‑flight temporary files, corrupting
    responses and causing hard‑to‑debug errors under load.

The solution:

  • Hard‑code the default basenames of nginx's temp directories:
    client_temp, client_body_temp, fastcgi_temp, proxy_temp, scgi_temp,
    uwsgi_temp.
  • Add a pre_tree_handler that returns NGX_DECLINED for any directory
    whose path component exactly matches one of these names, causing
    ngx_walk_tree() to skip that entire subtree.
  • Add a per‑file check in every delete handler (purge_all, partial,
    exact) as a fallback, ensuring protection even on older nginx
    versions that don't support directory skipping.

The list is intentionally static and name‑based rather than read from
live *_temp_path directives, because the purge walk runs long after
configuration parsing, with no cheap way to retrieve every protocol's
temp_path at that time. These names are Nginx's compiled‑in defaults
and cover the overwhelming majority of real‑world installations.

This guard is a pure safety net: it only ever causes files to be
SKIPPED, never causes a file that would otherwise be left alone to be
deleted.

When a purge_all, wildcard, or vary‑aware exact purge performs a full directory walk of the cache root, it may descend into nginx's temporary directories (fastcgi_temp, proxy_temp, etc.) if they are nested under the cache path. This is a common misconfiguration, often copied from tutorials, where users set *temp_path to a subdirectory of *cache_path to ensure the final rename() is atomic and fast.

Deleting in‑flight temporary files corrupts responses and causes
sporadic, hard‑to‑debug errors under load, especially during high‑traffic periods.

Add a hard‑coded list of the default basenames of nginx's temp
directories (client_temp, client_body_temp, fastcgi_temp, proxy_temp, scgi_temp, uwsgi_temp). The purge walk now skips any directory whose path component exactly matches one of these names via the pre_tree_handler (returning NGX_DECLINED). Every file‑level delete handler also checks the path to avoid deletion if the directory skip fails.

The list is intentionally static and name‑based rather than read from the live *temp_path directives: the purge walk runs long after configuration parsing, inside a worker handling the purge request or servicing the background queue, with no cheap way to retrieve every protocol's temp_path. These names are nginx's compiled‑in defaults and cover the overwhelming majority of real‑world installations.

This guard is a pure safety net: it only causes files to be skipped,
never causes a file that would otherwise be left alone to be deleted.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant