You open your site or admin panel and get a wall of warnings: session_start(): open(/var/lib/php/session/sess_abc123, O_RDWR) failed: Permission denied. Or worse, users keep getting logged out, carts empty themselves, and login forms just reload. That's a PHP session problem, and it's usually fixable in ten minutes once you know where PHP is trying to write.
What the error looks like
You'll see one or more of these in the browser or in error_log:
Warning: session_start(): open(...sess_xxxx, O_RDWR) failed: Permission denied (13)Warning: session_start(): open(...) failed: No such file or directory (2)Warning: Unknown: Failed to write session data (files). Please verify that the current setting of session.save_path is correctWarning: session_start(): Cannot send session cookie - headers already sent(a related but different cause)
Without visible errors, the symptoms are subtler: login loops, "session expired" messages, shopping carts that reset, CSRF or nonce failures on forms.
Why it happens
PHP stores each visitor's session as a small file in a folder called the session save path. If PHP can't write there, sessions silently break. The usual causes:
| Cause | Typical sign |
|---|---|
| Save path folder doesn't exist | "No such file or directory" |
| Wrong owner or permissions on the folder | "Permission denied" |
| Disk or inode quota full | "Failed to write session data" |
Custom session.save_path in php.ini or .user.ini pointing somewhere stale | Started after a migration or PHP version change |
| open_basedir blocks the path | "open_basedir restriction in effect" |
Output sent before session_start() | "headers already sent" |
Step 1: Find out where PHP is saving sessions
Don't guess. Create a temporary file called sess-check.php in your site's web root:
<?php
echo 'save_path: ' . ini_get('session.save_path') . "\n";
echo 'handler: ' . ini_get('session.save_handler') . "\n";
echo 'user: ' . get_current_user() . "\n";
echo 'writable: ' . (is_writable(session_save_path() ?: sys_get_temp_dir()) ? 'yes' : 'NO') . "\n";
Load it in your browser (wrap the output in <pre> tags or view source). Delete the file as soon as you're done, since it reveals server paths.
If the path is empty, PHP uses the system default, usually /tmp or /var/lib/php/session.
Step 2: Check the folder on cPanel hosting
On shared or reseller cPanel accounts, you normally don't touch /var/lib/php/session. If the error mentions it, your account is likely using a PHP handler or a stale setting that doesn't match the server. The clean fix is to give your account its own session folder:
- In File Manager, go to your home directory (not
public_html) and create a folder namedphp_sessions. - Set its permissions to
0700. - Open MultiPHP INI Editor in cPanel, choose your domain, and set
session.save_pathto/home/youruser/php_sessions. - Save, then reload your check script.
Keeping the folder outside public_html matters. Session files shouldn't ever be downloadable from the web.
If you'd rather use a file, add this to .user.ini in your web root (PHP-FPM and FastCGI setups):
session.save_path = "/home/youruser/php_sessions"
Changes in .user.ini can take a few minutes to apply because PHP caches them (default user_ini.cache_ttl is 300 seconds).
Step 3: Fix it on a VPS
On your own VPS you control the system folder. Check it:
ls -ld /var/lib/php/session
ls -ld /var/lib/php/sessions # Debian/Ubuntu uses this one
php -i | grep session.save_path
The right ownership depends on who runs PHP. For PHP-FPM with Nginx on Ubuntu, that's usually www-data:
sudo mkdir -p /var/lib/php/sessions
sudo chown root:www-data /var/lib/php/sessions
sudo chmod 1733 /var/lib/php/sessions
sudo systemctl restart php8.3-fpm
On AlmaLinux or Rocky with Apache/PHP-FPM it's typically root:apache with mode 0770. Match whatever your pool config says in the user = line. If you run multiple FPM pools, give each pool its own php_value[session.save_path] so one site can't read another's sessions.
On SELinux systems, a correct chmod isn't enough if the context is wrong. Check with ls -Z and restore with sudo restorecon -Rv /var/lib/php/session.
Step 4: Rule out a full disk or inode quota
If permissions look fine but writes still fail, check space:
df -h
df -i
du -sh /home/youruser/php_sessions
Old session files pile up when garbage collection doesn't run. On Debian-based systems PHP's own cleanup is replaced by a cron job, so a broken cron means files never get deleted. You can purge stale sessions manually:
find /home/youruser/php_sessions -type f -name 'sess_*' -mmin +1440 -delete
That removes anything untouched for 24 hours. Add it to cron if you use a custom folder. Sessions in a custom path aren't cleaned by the system job.
Step 5: Handle open_basedir and "headers already sent"
If the error says open_basedir restriction in effect, your session path sits outside the allowed directories. Either move it inside your home directory (as above) or add it to the allowed list. See our guide on open_basedir errors if you're not sure how to edit it.
For "headers already sent", something printed output before session_start(). Common culprits are a stray space or blank line before <?php, a UTF-8 BOM at the top of a file, or a plugin echoing a notice. The error message names the file and line that started output, so open that file and clean it up. Make sure session_start() is called before any HTML.
WordPress-specific notes
WordPress core doesn't use PHP sessions, but plenty of plugins do: WooCommerce extensions, membership plugins, form builders, and some themes. If the Site Health screen shows "An active PHP session was detected", a plugin is calling session_start(), which can also break REST API and loopback requests because the session file lock holds up concurrent requests. Find the plugin by disabling them in batches, then ask the developer for a fix or replace it. Don't just ignore it, since those locks can make a busy site crawl.
Prevention
- Give each site its own session folder outside the web root, with
0700permissions. - After a migration or PHP version switch, re-check
session.save_path. Old values from a previous server often come along for the ride. - Keep an eye on disk and inode usage in cPanel's Disk Usage page.
- On busy apps, consider storing sessions in Redis or Memcached instead of files.
- Call
session_write_close()as soon as your code is done writing to the session, so parallel requests don't queue up.
Still stuck? Open a ticket with the exact warning text, your domain, and the output of the check script (with the path, not your passwords). We'll look at the account's PHP handler and permissions for you.
Frequently Asked Questions
Is it safe to delete everything in the session folder?
Yes, it's safe, but every logged-in visitor will be signed out and carts tied to sessions will reset. Deleting files older than a day is the gentler option.
Why do I get logged out randomly?
Usually the session folder is being cleaned too aggressively, the path isn't writable, or a load balancer sends you to a different server that doesn't share session files. Check session.gc_maxlifetime and the save path first.
Can I use /tmp for sessions?
You can, but it's shared and often cleaned by the OS on reboot or by tmp-cleaning timers. A dedicated folder is more reliable and more private.
Do I need to restart anything after changing session.save_path?
On a VPS, restart PHP-FPM (or Apache). On cPanel, MultiPHP INI Editor changes apply automatically, and .user.ini changes apply once the cache expires, usually within five minutes.
