6.2 KiB
HTMLPurifier Cache Directory Fix
Problem
When deploying to a server, you may encounter this error:
Directory /var/www/timebank_cc_dev/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Serializer not writable.
This occurs because HTMLPurifier tries to write cache files to its vendor directory, which:
- Should NOT be writable for security reasons
- May not have correct permissions on production servers
- Can be overwritten during
composer install
Solution
We've reconfigured HTMLPurifier to use Laravel's cache directory instead.
Changes Made
-
Updated
app/Helpers/StringHelper.php(Lines 55-63)- HTMLPurifier now uses
storage/framework/cache/htmlpurifier - Auto-creates directory if it doesn't exist
- Works across all environments without manual configuration
- HTMLPurifier now uses
-
Created
deployment-htmlpurifier-fix.sh- Standalone script to set up HTMLPurifier cache directory
- Auto-detects web server user or accepts it as argument
- Gracefully handles permission errors
- Always exits successfully (doesn't break deployment)
-
Integrated into
deploy.sh(Lines 306-316)- Automatically runs during deployment
- Uses same web user/group as rest of application
- Runs for both local and server environments
How It Works
Automatic (via deploy.sh)
When you run ./deploy.sh, it will automatically:
- Create
storage/framework/cache/htmlpurifierdirectory - Set ownership to web server user (www-data, apache, nginx, etc.)
- Set permissions to 755
- Verify the directory is writable
Manual (if needed)
If you need to run the setup separately:
# Let script auto-detect web server user
./deployment-htmlpurifier-fix.sh
# Or specify web server user
./deployment-htmlpurifier-fix.sh www-data
# Or specify both user and group
./deployment-htmlpurifier-fix.sh www-data www-data
Code-Level (automatic)
The StringHelper::sanitizeHtml() method automatically creates the cache directory if it doesn't exist (lines 58-61):
$cacheDir = storage_path('framework/cache/htmlpurifier');
if (!is_dir($cacheDir)) {
mkdir($cacheDir, 0755, true);
}
Deployment Instructions
First Time Setup (New Servers)
Just run your normal deployment:
./deploy.sh
The HTMLPurifier cache directory will be set up automatically.
Existing Deployments (Already Running)
If you're updating an existing server that already has the old code deployed:
-
Pull and deploy the latest code:
./deploy.sh -
The script will automatically:
- Update code to use new cache location
- Create and configure the cache directory
- Set correct permissions
-
Verify (optional):
ls -la storage/framework/cache/htmlpurifierYou should see the directory with correct ownership and permissions.
If You Still Get Errors
If you encounter permission errors after deployment:
-
Manually run the fix script:
cd /var/www/timebank_cc_dev ./deployment-htmlpurifier-fix.sh www-data www-data -
Check storage directory permissions:
sudo chown -R www-data:www-data storage/ sudo chmod -R 775 storage/ -
Check SELinux (if applicable):
sudo chcon -R -t httpd_sys_rw_content_t storage/ -
Clear all caches:
php artisan cache:clear php artisan view:clear php artisan config:clear
Benefits
- Security: Vendor directory remains read-only
- Reliability: Cache survives
composer installupdates - Laravel Standard: Uses Laravel's cache directory structure
- Cross-Environment: Works on local, dev, and production without changes
- Auto-Recovery: Code creates directory if missing
- Deployment Safe: Script always continues even if errors occur
Technical Details
Cache Location
- Old (problematic):
vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Serializer - New (fixed):
storage/framework/cache/htmlpurifier
Permissions
- Directory: 0755 (owner: rwx, group: r-x, others: r-x)
- Owner: Web server user (www-data, apache, nginx)
- Group: Web server group (same as owner)
Files Created
When HTMLPurifier runs, it creates definition cache files:
HTML/4.01_XHTML1.0_Transitional.serHTML/4.01_XHTML1.0_Transitional-Attr.AllowedFrameTargets.ser- And others based on configuration
These are automatically managed by HTMLPurifier and Laravel's cache clearing commands.
Troubleshooting
Error: "Directory not writable"
Cause: Web server user can't write to cache directory
Fix:
sudo chown -R www-data:www-data storage/framework/cache/htmlpurifier
sudo chmod -R 755 storage/framework/cache/htmlpurifier
Error: "Failed to create directory"
Cause: Parent directory (storage/framework/cache/) doesn't have write permissions
Fix:
sudo chown -R www-data:www-data storage/framework/cache/
sudo chmod -R 775 storage/framework/cache/
Error: SELinux blocking writes
Cause: SELinux security context preventing writes
Fix:
sudo chcon -R -t httpd_sys_rw_content_t storage/framework/cache/
Testing
After deployment, verify HTMLPurifier works:
-
View a post with HTML content:
- Visit any page that displays post content
- No errors should appear
-
Check cache directory:
ls -la storage/framework/cache/htmlpurifier/You should see definition cache files created by HTMLPurifier
-
Run tests:
php artisan test --filter PostContentXssProtectionTestAll 16 tests should pass
Related Files
app/Helpers/StringHelper.php- Sanitization method with cache configurationdeployment-htmlpurifier-fix.sh- Standalone setup scriptdeploy.sh- Main deployment script (includes HTMLPurifier setup)SECURITY_AUDIT_XSS.md- Complete XSS vulnerability audit report
Support
If you encounter issues not covered here:
- Check Laravel logs:
storage/logs/laravel.log - Check web server error logs:
/var/log/apache2/error.logor/var/log/nginx/error.log - Verify web server user:
ps aux | grep -E 'apache|nginx|httpd' | grep -v grep - Check storage permissions:
ls -la storage/