Question
Laravel File Permissions with Apache: Secure Ownership and chmod
Question
I am running a Laravel 5 application on Apache, where the web-server account is _www:_www. Laravel requires its writable directories to be writable by the web server. I have often used chmod -R 777 to make the application work, but I understand that this is insecure.
The documentation states that directories within storage and vendor require write access by the web server. Does the web server need write access only to existing files inside those directories, or also to the directories themselves?
Changing the ownership of the entire Laravel project to _www:_www makes the site work, but then my normal user account cannot save files from my editor or Finder without entering an administrator password.
What is the correct and secure approach? Should I change permissions, change ownership, run Apache as my operating-system user, or use another method?
Short Answer
You will learn how Unix ownership, groups, and permission bits affect a Laravel application running under Apache. You will also learn why 777 and making the entire project web-server-owned are poor defaults, and how to grant write access only to Laravel directories that genuinely need it.
Concept
Unix-like systems decide whether a process can read, modify, or enter a path by checking:
- the file or directory owner
- the file or directory group
- permissions for the owner, group, and everyone else
- the account running the process, such as Apache's
_wwwaccount
A Laravel developer normally needs to edit source files as their own login user. Apache normally needs to read the application and write only to runtime-generated data, such as logs, sessions, cached files, and uploaded files.
Those are different responsibilities. The secure goal is therefore not to make every Laravel file writable by Apache. Instead:
- Keep application code owned by the deployment or developer user.
- Let Apache read the code it must execute or serve.
- Grant Apache group-based or ACL-based write access only to the runtime directories.
For modern Laravel releases, the usual runtime-writable locations are storage and bootstrap/cache. The vendor directory contains Composer-installed dependencies and should normally be readable by Apache but not writable at runtime. Composer may need to write vendor during installation or updates, but that operation should run as the deployment user, not as the web server.
A directory's permissions are especially important. To create, rename, or delete entries in a directory, a process needs write and execute permission on that directory. Write permission on an existing file alone is not enough to create a new log file or cache file.
Mental Model
Think of the project as an office building:
- Your developer account is the building manager who can change plans and furniture.
- Apache is a worker who needs to enter the building and use only a few designated work rooms.
storageandbootstrap/cacheare the work rooms where the worker creates notes, temporary files, and logs.- Source files,
.env, andvendorare the manager's records and should not be open for the worker to alter.
Using chmod -R 777 is like giving every person in the building a master key to every room. Changing the owner of every file to _www makes Apache the building manager, which prevents you from comfortably editing your own project. A shared group or ACL gives both the manager and worker the specific access they need.
Syntax and Examples
Permission bits are commonly shown in three groups:
owner group others
rwx rwx rwx
rmeans read.wmeans write.xmeans execute a file or traverse/search a directory.
For example:
chmod 775 storage
775 means:
- owner:
rwx - group:
rwx - others:
r-x
For a Laravel project, use a shared group rather than giving ownership of all files to Apache. Replace youruser with your login name and use the actual Apache group for your system. On the system in the question, that group may be _www.
# Keep the project owned by the developer/deployment account.
sudo chown -R youruser:_www /path/to/project
# Apache needs to read and traverse ordinary project directories.
find /path/to/project -type d -exec chmod 755 {} \;
find /path/to/project -type f -exec chmod 644 {} \;
# Allow the shared group to write only Laravel runtime directories.
chmod -R g+rwX /path/to/project/storage /path/to/project/bootstrap/cache
# Keep the shared group on newly created nested directories.
find /path/to/project/storage /path/to/project/bootstrap/cache -type d -exec chmod g+s {} \;
The uppercase X in g+rwX adds execute permission to directories and to files that are already executable. It does not make every ordinary file executable.
The exact ownership and mode policy depends on your deployment model. The important rule is scope: apply group-write permission to runtime directories, not recursively to all application code.
Step by Step Execution
Consider this layout:
project/
├── app/ drwxr-xr-x developer:_www
├── vendor/ drwxr-xr-x developer:_www
├── storage/ drwxrwsr-x developer:_www
│ └── logs/ drwxrwsr-x developer:_www
└── bootstrap/cache/ drwxrwsr-x developer:_www
Suppose Apache runs as user _www and belongs to group _www.
-
Apache receives a request and traverses
project/,app/, andvendor/.- The
xbit for others lets it traverse these directories. - The
rbit lets it read files that are world-readable, such as typical PHP source files.
- The
-
Laravel tries to append to
storage/logs/laravel.log._wwwmatches the directory's group_www.- Group
rwxpermits Apache to enterstorage/logsand create or modify its entries.
-
Laravel creates a new cache file in
bootstrap/cache.- Group write and execute permissions permit creating that entry.
-
A new directory created under
storageinherits group_wwwbecausestoragehas the set-group-ID bit (g+s).- Its displayed mode includes
s, such asdrwxrwsr-x. - This prevents newly created directories from unexpectedly becoming inaccessible to the other collaborator.
- Its displayed mode includes
-
Your editor runs as
youruser, the owner of the project.- It can save application code without
sudoor password prompts.
- It can save application code without
The process umask can still affect permissions of newly created files. If Apache creates files too restrictively, configure an appropriate process umask or use default ACLs for the writable directories.
Real World Use Cases
- Logging: Laravel writes application errors and diagnostic messages under
storage/logs. - Sessions and cache: File-based session and cache drivers create runtime files under
storage/framework. - Compiled views: Blade templates can be compiled into files in
storage/framework/views. - Framework cache: Laravel may write route, configuration, event, or package cache files in
bootstrap/cache. - File uploads: If uploads are stored locally, a Laravel disk may write under
storage/appor a configured writable location. - Deployments: A CI/CD account can install Composer dependencies and publish a release, while Apache only reads the release and writes runtime state.
Real Codebase Usage
In a production codebase, permissions are usually managed by deployment tooling rather than fixed manually after every change.
Common patterns include:
- Deploy as a dedicated user: A deploy account owns each release and runs
composer install. Apache has read access to the release. - Writable directories outside immutable releases: Applications often keep
storagedata in a shared location that is linked into each new release. This preserves logs and uploads while code releases remain read-only. - Shared Unix group: The deploy user and web-server user belong to a group that has write permission on selected runtime paths.
- Default ACLs: Where multiple users or processes must consistently write files, ACLs can define inherited permissions more precisely than basic mode bits.
- Least privilege:
.env, deployment keys, source code, and dependencies are not made Apache-writable merely to solve a cache or log problem. - Correct web root: Apache should normally use Laravel's
publicdirectory asDocumentRoot, not the project root. This is separate from permissions but prevents exposing.env,vendor, and application files over HTTP.
A deployment script may clear and rebuild caches while running as the deploy user:
php artisan optimize:clear
php artisan config:cache
php artisan route:cache
If these commands write bootstrap/cache, the deploy user also needs access there. A shared group lets both the deploy user and Apache use that directory when necessary.
Common Mistakes
Using chmod -R 777
chmod -R 777 /path/to/project
This allows every local account to write every project file. It also makes source code, dependencies, and configuration writable by the web-server process. If an application vulnerability is exploited, broad write access can make the impact worse.
Avoid it: grant group write access only to storage and bootstrap/cache.
Making the entire project owned by Apache
sudo chown -R _www:_www /path/to/project
This lets Apache write code and makes normal editing inconvenient because your login user is no longer the owner.
Avoid it: keep the developer or deploy user as owner; use Apache's group only where shared access is needed.
Giving file write permission but not directory write permission
chmod 664 storage/logs/laravel.log
chmod 755 storage/logs
Apache may be able to modify the existing log file if it has the correct group, but it cannot create a new log file, rotate one, or create another file in storage/logs because the directory lacks group write permission.
Avoid it: writable directories need group w and x, commonly 775 or 2775.
Making every file executable
chmod -R 775 /path/to/project
This sets execute permission on regular files, including PHP files, JSON files, and images. It is usually unnecessary.
Avoid it: use find to handle directories and files separately, or use g+rwX.
Assuming vendor must be web-server-writable
At runtime, Laravel normally reads code from vendor; it does not need Apache to modify it. Composer writes there during install/update operations.
Avoid it: run Composer as the deploy user and keep vendor read-only to Apache unless a specific, verified workflow requires otherwise.
Forgetting new-file permissions
A directory can initially be correct, but files created later may not be group-writable because of the creating process's umask.
Avoid it: use setgid directories and, when needed, default ACLs or an appropriate service umask.
Comparisons
| Approach | What it does | Suitable use | Main concern |
|---|---|---|---|
chmod 777 | Everyone can read, write, and traverse | Almost never; temporary local diagnosis only | Excessive access |
| Web server owns all files | Apache can modify all project files | Rarely appropriate | Developer cannot edit normally; code becomes writable |
| Developer owns project; Apache has read access | Apache reads application code | Good baseline | Does not alone allow logs/cache writes |
| Shared group on selected paths | Developer and Apache can write runtime directories | Common practical solution | Must manage inherited group and umask |
| POSIX ACLs on selected paths | Grants named users/groups precise access | Useful for complex multi-user setups | Less portable and more complex |
| Permission | On a regular file | On a directory |
|---|---|---|
r | Read file contents | List directory entries |
w | Modify file contents | Create, remove, or rename entries when combined with x |
x | Run as a program/script | Traverse or access entries inside |
Basic mode bits are simple and widely supported. ACLs are useful when one owner and one group are not enough, but they should still follow least privilege.
Cheat Sheet
# Inspect owner, group, and modes
ls -ld storage bootstrap/cache
ls -l storage/logs
# Replace these values for your environment
PROJECT=/path/to/project
OWNER=youruser
WEBGROUP=_www
# Developer/deployer owns the project; Apache is the shared group
sudo chown -R "$OWNER:$WEBGROUP" "$PROJECT"
# Typical read-only application code baseline
find "$PROJECT" -type d -exec chmod 755 {} \;
find "$PROJECT" -type f -exec chmod 644 {} \;
# Laravel runtime paths: shared-group write access
chmod -R g+rwX "$PROJECT/storage" "$PROJECT/bootstrap/cache"
# New nested directories inherit the group
find "$PROJECT/storage" "$PROJECT/bootstrap/cache" -type d -exec chmod g+s {} \;
- Prefer owner = developer/deploy user.
- Prefer group = Apache group only where collaboration is needed.
- Runtime directories generally need group
rwx:775or2775is common. - Normal code directories commonly use
755; normal files commonly use644. 2775means group-write plus the setgid inheritance bit on a directory.- Apache needs directory
xto traverse a path. - Apache needs directory
wplusxto create/delete/rename entries. - Do not make
.env, source code, orvendorbroadly writable. - Confirm the actual Apache user and groups with your operating-system and Apache configuration; names differ across platforms.
FAQ
Should Laravel's vendor directory be writable by Apache?
Usually no. Apache needs to read PHP dependencies in vendor at runtime. Composer needs write access when installing or updating dependencies, and Composer should normally run as the deployment user.
Which Laravel directories should be writable?
For current Laravel applications, storage and bootstrap/cache are the usual writable locations. Check your Laravel version and configured cache, session, log, and filesystem drivers because custom configuration can add another writable path.
Does Apache need write access to a directory or only its files?
It needs write and execute permission on the directory to create, delete, rename, or replace entries. It needs write permission on an existing file to modify that file's contents.
Why does my editor ask for a password after chown -R _www?
Your account no longer owns the files and may lack write permission. Keep your user or deploy account as owner instead of making Apache the owner of the entire project.
Is chmod 775 storage enough?
It can be enough if Apache belongs to the directory's group, the relevant nested directories also permit group writes, and newly created files do not lose required group access. Setgid directories or default ACLs help preserve access for new content.
Should I run Apache as my personal login user?
This is generally not a good production default. A compromised web application would gain the same access as your personal account. Use a dedicated low-privilege web-server account and grant narrowly scoped access.
Why should Apache's document root be public?
Laravel's public directory contains assets and the front controller intended for HTTP access. Pointing Apache at the project root can expose sensitive files such as .env or application source if configuration is wrong.
Can I use ACLs instead of changing groups?
Yes. ACLs are useful when several accounts need different access rules. They can also define default permissions for files created later, but the commands and availability vary by operating system.
Mini Project
Description
Configure a local Laravel project so that you can edit application code as your normal user while Apache can write logs and cache files. This demonstrates least-privilege permissions instead of using 777 or giving Apache ownership of the whole project.
Goal
Create a shared-group permission setup in which Apache can write Laravel runtime directories but cannot modify the entire application.
Requirements
Use your normal login account as the owner of the Laravel project.
Keep learning
Related questions
Are PDO Prepared Statements Enough to Prevent SQL Injection in PHP?
Learn how PDO prepared statements prevent SQL injection in PHP, what they protect, and the mistakes that still leave MySQL apps vulnerable.
Can You Bind an Array to an IN Clause in PHP PDO?
Learn how PDO handles placeholders in IN() clauses, why arrays cannot be bound directly, and the safe PHP pattern to build dynamic queries.
Choosing the Right MySQL Collation for PHP and UTF-8
Learn how MySQL character sets and collations work with PHP, and how to choose a practical UTF-8 setup for web applications.