October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsPC HealthRecommendedCrashes, freezes, slowdowns? Check your PC nowSpot repairable issues before they interrupt work.Check PCOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
VGSources
Blog

How to Let PHP Delete NGINX FastCGI Cache Files Safely

A safe PHP-to-NGINX cache invalidation guide: reproduce the exact FastCGI cache key, constrain the canonical path to a fixed root, check unlink() failures, and understand when an authenticated NGINX purge is preferable.
Length11 min Posted Quest giverVGSources Team
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

PHP can delete a known NGINX FastCGI cache file with unlink($path). The call succeeds only when the PHP-FPM worker can traverse every parent directory and remove that file under normal operating-system permissions. The path must also be the file generated by the active NGINX fastcgi_cache_path and fastcgi_cache_key configuration; guessing from a URL is unsafe and often wrong.

For one precisely identified entry, a fixed cache root, an exact key-to-filename calculation, canonical-path validation, and a checked unlink() call are the basic pattern. For production invalidation, an authenticated internal purge workflow is often easier to audit because it avoids exposing arbitrary filesystem deletion to application requests.

As an Amazon Associate I earn from qualifying purchases.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

What PHP is actually deleting

NGINX FastCGI caching stores response data as files beneath the directory declared by fastcgi_cache_path. PHP does not ask NGINX to remove an entry when it calls unlink(); it asks the operating system to remove a filesystem object. PHP documents unlink() as the file-deletion function. It returns true when the operation succeeds and false when it fails, and a failure raises an E_WARNING.

That distinction explains most permission errors. The effective identity is the PHP-FPM worker account, not the user who made the web request and not necessarily the account running NGINX. Directory traversal, file ownership, and mode or access-control rules are evaluated by the operating system. A script can therefore calculate the right filename and still be unable to remove it.

Deletion is also not a guarantee that a URL will remain uncached. A response already being read can continue to be served, and another request can create the cache entry again immediately after the file is removed. Treat unlink() as invalidation at a point in time, not as a lock on future requests.

Find the active cache layout before writing PHP

Read the configuration that is actually loaded by the running NGINX instance. Three directives determine whether a PHP calculation can address a file:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • fastcgi_cache_path supplies the cache root and the directory hierarchy.
  • The levels parameter determines how the hash is split into subdirectories.
  • fastcgi_cache_key determines the string that is hashed for the cache entry.

NGINX derives the cache key and filename with MD5. Its documentation illustrates a hierarchy configured with levels=1:2; a resulting file can look like /data/nginx/cache/c/29/b7f54b2df7773722d382f4809d65029c. The example is not a universal location or filename. Your deployment may use a different root, different levels, or a different key.

The key is more than the visible path in a browser. It can include the request scheme, host, query string, normalization rules, and custom variables selected in fastcgi_cache_key. If PHP hashes only a URL while NGINX hashes a scheme, host, normalized URI, and query string, the resulting digest points to another file. Deleting that file either does nothing useful or removes a different cache entry.

Requirements for a safe deletion feature

Set these conditions before allowing application code to invalidate a file:

  • A server-side root: keep the cache root in configuration or another trusted server-side setting. Do not accept it from a request parameter.
  • An exact key algorithm: reproduce every component and transformation used by the active fastcgi_cache_key. Do not infer the algorithm from a sample filename.
  • A known PHP-FPM identity: establish which account executes the worker and confirm that it can traverse the cache directories.
  • A bounded path: canonicalize the candidate path and verify that it remains below the intended cache root.
  • Checked outcomes: test the return value from unlink(), log diagnostic details on the server, and return a generic result to an untrusted caller.
  • Controlled access: expose invalidation only through an authenticated, authorized administrative path or an internal queue. Never make a raw delete operation available to an unauthenticated request.

Implementing direct deletion step by step

  1. Read the trusted cache root. Define a fixed value such as /var/cache/nginx/fastcgi in server configuration. It must not be assembled from request data.
  2. Compute the exact cache key. Use the same inputs, ordering, separators, normalization, and custom variables as NGINX. Hash and split the result according to the configured hierarchy.
  3. Build a candidate path below that root. Concatenate the trusted root and the calculated relative path. Do not concatenate a user-supplied filename or permit a caller to choose directory components.
  4. Canonicalize both paths. PHP’s realpath() resolves dot components and symbolic links. It returns false if a path cannot be resolved. Refuse the operation when either the root or candidate cannot be canonicalized.
  5. Enforce the root boundary. The canonical file must equal the canonical root only in the degenerate case of the root itself, or begin with the canonical root followed by a directory separator. A prefix test without the separator can mistakenly treat a directory such as /cache-old as being inside /cache.
  6. Delete and check. Call unlink($file) only after the boundary check. A false return is an operational failure, not a successful purge.
  7. Record diagnostics safely. Log the server-side path, PHP warning, and relevant request identifier where appropriate, but do not disclose filesystem paths or permission details to an untrusted caller.

This compact skeleton demonstrates the validation order. Replace the example root and relative path with values derived from your active configuration:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
<?php $cacheRoot = '/var/cache/nginx/fastcgi'; $cacheFile = $cacheRoot . '/c/29/b7f54b2df7773722d382f4809d65029c'; $root = realpath($cacheRoot); $file = realpath($cacheFile); if ($root === false || $file === false || ($file !== $root && strncmp($file, $root . DIRECTORY_SEPARATOR, strlen($root) + 1) !== 0)) { throw new RuntimeException('Refusing to delete a path outside the cache root'); } if (!unlink($file)) { throw new RuntimeException('Cache deletion failed'); }

The shown filename is only an illustration of the documented MD5 hierarchy. It may not exist on your host. The sample deliberately fails closed when the path cannot be resolved; decide separately whether your application should report an already-absent entry as an idempotent success or as a failed lookup.

Making the key calculation match NGINX

The most common logical error is not a filesystem permission at all: PHP targets a file that NGINX never created. Start with the literal fastcgi_cache_key expression in the active server configuration. List every variable and any normalization applied before hashing. Scheme differences, host names, query-string ordering, omitted defaults, and custom variables can all produce different keys for requests that appear equivalent to an application.

Next, reproduce NGINX’s digest and directory split exactly. The final hexadecimal digest is the filename, while the leading characters are arranged into the subdirectories specified by levels. With levels=1:2, the documentation’s example places one hexadecimal character in the first directory and two in the second. A deployment using another hierarchy will produce another relative path even when the digest is identical.

Do not use a URL-to-file shortcut unless you have verified it against the configuration and a real cache entry. A URL may omit information included in the key, and a request may be normalized before NGINX hashes it. The safe input to your deletion function is an application-level resource identifier that your server converts into the same canonical request representation NGINX uses, not an arbitrary path supplied by a client.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Diagnosing permission-denied and failed deletions

A failed unlink() can have several causes. Capture the server-side warning and inspect the deployment rather than returning the raw path or warning text to the caller.

Observed symptom Likely cause What to verify
unlink() returns false for a file that appears to exist The PHP-FPM account lacks permission to traverse a parent directory or remove the file Identify the worker account, then check ownership and access permissions on every parent directory and on the cache file
The calculated path cannot be resolved The key, cache root, hierarchy, or file name does not match the active NGINX configuration, or another process already removed the entry Re-read fastcgi_cache_path, levels, and fastcgi_cache_key; inspect whether the file still exists
The operation works for some entries but not others Different keys or locations are being generated, or permissions differ within the hierarchy Compare the complete canonical paths and the permissions on each distinct directory branch
The file disappears and then returns A concurrent or subsequent request regenerated the cache entry Consider whether the request arrived while the old response was still in use and whether the purge must be coordinated with traffic
A path that includes user input reaches the deletion code An arbitrary-file deletion or traversal vulnerability Remove raw path input, keep the root fixed, canonicalize the result, and enforce the root-boundary check

PHP’s filesystem-security guidance is relevant here: PHP remains subject to the operating system’s file and directory permissions. Granting a worker access to the cache therefore changes what that process can remove. Keep the allowed location as narrow as practical and make the invalidation operation an explicit, authenticated capability.

Direct unlink() versus an NGINX purge workflow

Direct deletion is attractive when one application event maps to one precisely known cache file. It has no separate HTTP request and can be implemented with the standard PHP filesystem function. Its cost is tight coupling: PHP must know NGINX’s on-disk naming scheme and must have filesystem rights in the cache tree.

NGINX documents a different model in which a special PURGE request or a custom purge header is handled by a protected location. The examples restrict access by client IP, so the purge interface is not an open public endpoint. The application asks NGINX to invalidate a key instead of manipulating the cache directory directly.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The FastCGI module also documents fastcgi_cache_purge. A matching entry is removed and successful operation is reported with HTTP 204 (No Content); wildcard keys can match multiple entries. The documentation identifies this directive as part of a commercial subscription, so its availability depends on the NGINX edition and deployment.

Concern PHP direct deletion Authenticated NGINX purge
What the caller identifies A specific on-disk file derived from the active key and hierarchy A cache key or purge request interpreted by NGINX
Coupling Coupled to filesystem layout, MD5 naming, and directory permissions Coupled to the configured purge location and request rules rather than PHP’s filesystem
Permission surface PHP-FPM must traverse directories and unlink files Access can be limited at the NGINX request layer, with examples restricting client IPs
Multiple-entry invalidation Requires PHP to identify and remove each matching file Wildcard matching is documented for fastcgi_cache_purge
Success response documented PHP returns a Boolean from unlink(); failures raise an E_WARNING fastcgi_cache_purge reports successful removal with HTTP 204
Availability Uses the PHP filesystem function fastcgi_cache_purge is documented as a commercial-subscription feature

For production systems, an authenticated internal purge endpoint or queue is usually easier to audit than allowing web-facing PHP code to delete arbitrary files. Direct deletion remains reasonable when the mapping is deterministic, the root boundary is enforced, and the PHP-FPM permissions are deliberately configured.

Concurrency, stale responses, and cache recreation

Removing a file affects the cache state at the instant the operating system completes the unlink. It does not recall a response that NGINX has already read or sent, and it does not prevent a later request from writing a new entry. A content update can therefore be followed by one request that still receives the old response and another that repopulates the cache.

Design invalidation around that behavior. If an update must be followed by a fresh representation, purge after the authoritative write and ensure the subsequent read uses the same key components as the deleted entry. When several related URLs change, an approach that can address a controlled group of keys may be more suitable than trying to discover individual files from PHP.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Deletion races are normal in a busy cache. Another worker can remove the file between the existence check and unlink(), or a request can recreate it after the check. Do not turn a preliminary metadata check into a guarantee. The decisive operation is the checked unlink() call, and its failure should be logged with enough context to distinguish a missing file from a permissions problem.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Why clearstatcache() is usually unnecessary

PHP documents that unlink() clears the relevant status cache automatically. Calling clearstatcache() immediately after a successful deletion is therefore generally unnecessary. Use it only when the same script performs additional metadata checks that require explicit cache clearing under your particular control flow. It does not repair an incorrect path or grant the PHP-FPM worker permission.

Security rules for an invalidation endpoint

  • Never accept a raw filesystem path. A caller who can submit ../ components or an arbitrary filename may be able to delete any file accessible to PHP.
  • Keep the root outside request data. Store it in server-side configuration and reject candidates that canonicalize outside it.
  • Do not trust a URL as a filename. Convert an authorized resource identifier into the exact configured cache key; do not let the caller supply the digest or directory split.
  • Authenticate and authorize the action. Invalidation should be an administrative capability, not a public content endpoint.
  • Return generic errors. Detailed paths, account names, and permission diagnostics belong in protected logs.
  • Review symlink behavior. Canonicalization resolves symbolic links before the boundary comparison, so a link that escapes the cache root is rejected.

These controls address two separate risks: deleting the wrong cache entry because the key calculation is inaccurate, and deleting a non-cache file because a path was allowed to escape the intended directory.

Deployment checklist

  1. Record the active fastcgi_cache_path, cache root, and levels setting.
  2. Record the complete fastcgi_cache_key expression, including scheme, host, query string, normalization, and custom variables.
  3. Confirm the PHP-FPM worker identity used by the application that will perform invalidation.
  4. Verify traversal permission on every parent directory and removal permission on representative cache files.
  5. Implement canonicalization and a separator-aware root-boundary test before calling unlink().
  6. Log the canonical target and failure reason only on the server; expose a generic response to callers.
  7. Test a known entry, a nonexistent entry, an outside-root path, and a permission-denied path in a non-production environment.
  8. Exercise concurrent traffic to observe whether an entry is served or recreated after deletion.
  9. Decide whether direct deletion or a protected NGINX purge workflow gives the better audit and permission boundary for the deployment.

Choosing the right approach

Use direct PHP deletion when the application can reproduce the exact NGINX key, the target is one known entry, and granting the PHP-FPM worker narrowly scoped access to the cache directory is acceptable. Keep the implementation server-side, canonicalize every path, and check the Boolean result.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Prefer a protected purge workflow when several cache entries may need invalidation, when you want NGINX to own cache semantics, or when filesystem access from PHP would complicate auditing. Restrict the purge request, authenticate the caller, and account for the documented availability of fastcgi_cache_purge in your NGINX edition.

In either design, correctness depends on the running configuration rather than a remembered default path or a filename copied from an example. The reliable sequence is: derive the configured key, locate the resulting file under the fixed root, prove the canonical path remains inside that root, perform the deletion or purge, and treat concurrent cache recreation as expected behavior.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More quests from Patch Notes

  1. How to Set Up a RedM (RDR2) Server in 2026: License Key, txAdmin and server.cfg, Step by StepBlog11min
  2. Best RedM (RDR2) Server Hosting in 2026: Comparing Four Hosts on Slots, Memory and PriceBlog10min
  3. How to Host a Mindustry Server in 2026: server-release.jar, Port 6567 and the Commands That MatterBlog7min
Recommended PC Tool
Recommended PC Tool
Outdated Drivers Are Slowing You DownFree scan - exact matches
PC Slower Than It Used to Be?Free scan - under a minute

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.