skip to content

PHP: When is /tmp not /tmp?

 Tweet Share0 Tweets

Until a few years ago you could use PHP, or any other application, to write a file to /tmp and see it appear immediately in the server /tmp directory. This is no longer the case.

So what's happening?

On our servers, running Linux, Apache and PHP, there are at least three factors at work:

  1. systemd: PrivateTmp;
  2. PAM: libpam-tmpdir;
  3. PHP: sys_get_temp_dir();


In the systemd service configuration files for Apache2 and some other programs you will see PrivateTmp=yes or PrivateTmp=true as the default setting.

This means that the affected programs will not have access to the system /tmp directory. Instead any read/write requests for /tmp silently redirect to a location assigned by systemd. Something like:


So if you have a PHP script run by Apache, any files written to /tmp will only be accessible by Apache and not from the command-line or another application as there is no safe and reliable way (that we know of) to determine the actual file location.


Similarly from the command-line the libpam-tmpdir package creates separate /tmp directories for different users based on their User ID:


Only it's not as strict as the PrivateTmp as you can still write to /tmp if you want as shown below.

What's actually happening is that the $TMP and $TMPDIR constants are updated at login for each user.

PHP sys_get_temp_dir

When writing a file to /tmp using PHP there are (at least) two different ways to specify the the path as shown in this simple script:

<?PHP $file = new \SplFileObject("/tmp/tmp-test.txt", "a+"); $file->fwrite("written to /tmp/ by " . posix_getpwuid(posix_geteuid())['name'] . "\n"); $file = NULL; $file = new \SplFileObject(sys_get_temp_dir() . "/tmp-test.txt", "a+"); $file->fwrite("written to sys_get_temp_dir() by " . posix_getpwuid(posix_geteuid())['name'] . "\n"); $file = NULL; ?>

In the first case we simply use the literal /tmp while in the second case we rely on the sys_get_temp_dir function, which sometimes returns a different value.

Test Results

Using the above script, called from Apache both using a web browser and using cURL from the command-line (which is essentially the same thing) the file is consistently written to:

/tmp/systemd-private-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-apache2.service-XXXXXX/tmp/tmp-test.txt written to /tmp/ by www-data written to sys_get_temp_dir() by www-data written to /tmp/ by www-data written to sys_get_temp_dir() by www-data

Restarting the Apache process will likely change this to a new directory, but until then we can use /tmp to share files between scripts as long as they're run from Apache.

When running PHP from the command-line as different users:

root:~# php /path/to/script.php root:~# su - www-data -s /bin/sh -c "php /path/to/script.php" chirp:~$ php /path/to/script.php

If we specify /tmp then the file appears where expected:

/tmp/tmp-test.txt written to /tmp/ by root written to /tmp/ by www-data written to /tmp/ by chirp

But using sys_get_temp_dir() results in separate files based on who is running the script:

/tmp/user/0/tmp-test.txt written to sys_get_temp_dir() by root /tmp/user/1001/tmp-test.txt written to sys_get_temp_dir() by chirp /tmp/user/XX/tmp-test.txt written to sys_get_temp_dir() by www-data


If you want to share PHP-generated files between Apache and command-line scripts then you will need to create your own PHP-writable directory for the scripts to use, and implement your own garbage collection and security.

And sharing between different command-line (CLI) users is only possible if you specify /tmp rather than relying on sys_get_temp_dir().

But if each user only needs access to their own files then sys_get_temp_dir() is the way to go.

Note that we haven't changed the default sys_temp_dir setting in php.ini which could further complicate things.


Send a message to The Art of Web:

used only for us to reply, and to display your gravatar.

<- copy the digits from the image into this box

press <Esc> or click outside this box to close

Post your comment or question