Skip to content
Products
AI Website Builder New VPS Hosting Cloud Servers Web Hosting Dedicated Servers Domains
Company
About Documentation Support Center Contact Get Started Call +91 75795 45488
Login
Hosting Panel — cPanel & Billing Console Panel — VPS Management
ALL SYSTEMS OPERATIONAL
Hosting

CloudLinux Symlink Protection: Fix 403/500 in cPanel

Getwebup 5 min read

You create a symlink — maybe to share an uploads folder between two sites, or to point a cache directory somewhere else — and the page behind it starts throwing a 403 Forbidden or a bare 500 Internal Server Error. Nothing else on the account changed. What you've run into is CloudLinux's Symlink Race Condition Protection, a kernel-level security feature that most cPanel users never hear about until it breaks something.

Symptom: What You're Actually Seeing

  • The URL behind a symlinked file or folder returns 403 or 500, but the same content opened through its real, non-symlinked path works fine.
  • A PHP include() or require() that points through a symlink fails with failed to open stream: Permission denied, even though ls -l shows normal-looking permissions.
  • The Apache error log has a line like Symbolic link not allowed or link target not accessible right around the timestamp of the failed request.
  • It usually starts right after you create a new symlink, restore an account from backup, or migrate a site between servers.

On CloudLinux-based cPanel servers — which is most shared, reseller, and a fair number of managed VPS setups — the kernel itself enforces symlink ownership checks, independent of whatever Options line you have in .htaccess. This exists to stop a known class of attack: on a shared server, if account A could create a symlink pointing into account B's files and trick a process into following it, A could read or overwrite B's data. CloudLinux closes that gap by requiring, for every symlink Apache or PHP follows:

  1. The symlink and its target must be owned by the exact same user (same UID).
  2. None of the directories in the path to the target can be group- or world-writable, unless the sticky bit is set on them.
  3. The target file or directory itself must not be world-writable.

Fail any one of those and the kernel refuses to resolve the link for the web server or PHP process — even though root, logged in directly, can follow it without any trouble at all.

What you seeUsual cause
403 Forbidden on the symlinked URLSymlink target is owned by a different cPanel user than the symlink itself
500 Internal Server ErrorTarget lives inside a directory that's group- or world-writable (often left at 777 from an old fix)
open_basedir restriction in effect in PHP logsSymlink ownership passed, but the resolved real path falls outside PHP's open_basedir allow-list

The Fix

Check the Apache error log for the account (WHM > Server Configuration > Terminal, or via SSH):

tail -f /usr/local/apache/logs/error_log | grep -i symb

A hit on "Symbolic link not allowed" confirms it — you're not chasing a permissions typo or a caching issue.

2. Check whether ownership actually matches

stat -c '%U %n' /home/user1/public_html/shared
stat -c '%U %n' /home/user2/public_html/uploads

If the two usernames differ, that's the whole problem.

chown -h user1:user1 /home/user1/public_html/shared
find /home/user1/public_html/uploads -type d -exec chmod 755 {} \;
find /home/user1/public_html/uploads -type f -exec chmod 644 {} \;

The -h flag on chown matters — without it you'll change the ownership of whatever the link points to instead of the link itself.

If the target genuinely belongs to a different cPanel user, CloudLinux will not let this through, by design, and root loosening it defeats the entire point of the feature on a shared server. Instead:

  • Sync the files with a scheduled rsync or cron job instead of linking to them live.
  • Put both sites under the same cPanel account as addon domains, so they share one owner and one home directory.
  • Serve the shared assets from a subdomain under the account that already owns them, and reference it by URL rather than by filesystem path.

5. Rule out open_basedir separately

Even after ownership checks pass, PHP can still block the resolved path. In MultiPHP INI Editor, add the real target directory (not just the symlink path) to open_basedir for that domain, then retest.

6. Retest immediately

curl -I https://example.com/shared/file.jpg

Don't rely on a browser reload — a cached 403 can look identical to a fresh one. curl -I gives you the real status without any caching in the way.

If You Have Root or WHM Access

CloudLinux does let root loosen symlink owner-match checks server-wide or per LVE, but treat that as a last resort reserved for genuinely single-tenant boxes — a VPS running one cPanel account, where there's no second user to protect against. On any shared or reseller server, leave it on. It costs you nothing when everything is one owner, and it's the exact control that stops one hacked account from reaching into another.

Prevention

  • Run chown -h on a symlink the moment you create it — don't leave it to inherit whatever the parent directory had.
  • Never symlink across two different cPanel accounts; sync or restructure instead.
  • Keep directories at 755 and files at 644. Resist the urge to chmod -R 777 a folder to "just make it work" — that's usually what breaks symlink protection a week later.
  • After any account restore, migration, or WHM transfer, re-check ownership on every symlink in the account — pkgacct/restorepkg can shift UIDs even when paths stay identical.
  • Document any symlinks you rely on. They're invisible in a normal file listing at a glance and easy for the next person (or future you) to break without realizing it.

Frequently asked questions

Why did this symlink work yesterday and break today?

Nothing about the link itself needs to change for this to happen. A backup restore, a WHM account transfer, or a CloudLinux update can shift ownership or tighten enforcement without touching the symlink's target path at all. Re-check ownership with `stat` right after any migration or restore.

Can I just ask my host to disable Symlink Protection?

On shared or reseller hosting, no reputable host will do this account-wide since it reopens a known cross-account attack path. It's only realistic to disable on a single-tenant VPS where there's no second account to protect against.

Does it matter whether I created the symlink via SSH or cPanel File Manager?

No. The check happens at the kernel level for any process reading through the link, so Apache, PHP-FPM, and an SSH shell are all subject to the same ownership rules regardless of how the link was created.

I fixed the ownership but PHP still throws a permission error — why?

That's usually open_basedir, a separate PHP-level restriction. Once the OS-level symlink check passes, PHP still checks the resolved real path against its own allow-list. Add the target directory to open_basedir in MultiPHP INI Editor.

I'm on a Getwebup VPS with root access — should I turn this off?

If it's a single cPanel account on that VPS, the same-owner rule is close to moot since everything already shares one user. It still costs nothing to leave it enabled, so there's rarely a reason to switch it off unless you fully understand what you're trading away.

#cpanel #cloudlinux #symlink-protection #403-error #500-error #file-permissions

Keep reading

Chat with Support