- Knowledge
- technology
- OOP
- Tips
- Programming
- Tips
- Tutorial
- SEO
- Ranking
- Knowledge
- Special Day
- Seo
- Bug
- Data science
- Seo
- artificial intelligence
- Machine Learning
- Robotics
- happyNewYear2021
- newYearEve
- 2021
- Automation
- Smart Home
- Career
- Best Practices
- Git
- Logging
- Web Fundamentals
- DNS
- HTTPS
- Performance
- AI Tools
- ChatGPT
- Claude
- Gemini
- Laravel
- Eloquent
- MySQL
- HTTPS
- TLS
- Web Security
- Certificates
- Developer Life
- Debugging
- Docker
- DevOps
- Transactions
- Queues
- LLMs
- AI
- AI Coding
- Developer Tools
- React Native
- Expo
- Kate PMS
- Mobile Apps
- Laravel
- Authentication
- Sanctum
- Cookies
- API Design
- Payments
- Idempotency
- DeepSeek
- Open Source AI
- LLMs
- AI News
- Git
- Version Control
- AI Coding
- Prompting
- PHP
- Checklist
- MCP
- AI Agents
- OpenAI
- Architecture
- Microservices
- Modular Monolith
- Estimation
- Developer Life
- Project Planning
- Humour
- OAuth
- OpenID Connect
- Authentication
- Embeddings
- Vector Search
- RAG
- pgvector
- OpenAI
- GPT-4.1
- Codex CLI
- Events
- Testing
- Clean Code
- Maintainability
- Code Review
- Webhooks
- API
- Security
- Claude Code
- Workflow
- AI
- LLM
- Prompt Injection
- Mobile
- React
- Networking
- TCP
- UDP
- HTTP/3
- CLAUDE.md
- AWS
- Cloud Security
- Backups
- PHPUnit
- Software Engineering
- Leadership
- Communication
- RAG
- Embeddings
- AI Engineering
- IT Infrastructure
- Networking
- Access Control
- CI/CD
- GitHub Actions
- Gemini CLI
- Claude Code
- JavaScript
- Async/Await
- Node.js
- Promises
- Security
- Cryptography
- Passwords
- MySQL
- Database
- Vibe Coding
- Software Quality
- DNS
- Code Reading
- Onboarding
- Productivity
- Background Jobs
- Developer Humour
- Estimates
- Dev Life
- JWT
- o3-mini
- DeepSeek R1
- Rate Limiting
- Kate PMS
- E-Signing
- Audit Trail
- REST
- GraphQL
- API Design
- Laravel 12
- Upgrade Guide
- Open Source
- Self-Hosting
- Task Scheduling
- Cron
- Secrets
- CORS
- PHP
- PHP-FPM
- OPcache
- GitHub Copilot
- Software Architecture
- Engineering
- TypeScript
- JavaScript
- Type Safety
- AI Security
- React Native
- Product Design
- AI Agents
- Kiro
- Queues
- Redis
- RabbitMQ
- AWS SQS
- Nginx
- Apache
- GPT-5
- gpt-oss
- Clean Code
- Architecture
- Naming
- Documentation
- Career
- ADR
- Teamwork
- Supply Chain
- Kate HRM
- HR Software
- Permissions
- System Design
- Pagination
- SSH
- Linux
- Big O
- Databases
- Laravel Boost
- MCP
- Developer Skills
- Validation
- Databases
- Indexes
- Code Quality
- Deployment
- Developer Humour
- Feature Flags
- Code Review
- Pull Requests
- Docker
- Cursor
- Authorization
- RBAC
- Gemini
- Long Context
- PHP 8.4
- Caching
- Dependency Injection
- Web Performance
- Browser
- CSS
- Database
- Migrations
- ChatGPT
- AI for Developers
- Monitoring
- On-Call
- REST
- Backend
- SQL
- NoSQL
- Database Design
- Coding Agents
- Claude 4
- API Resources
- REST API
- Load Balancing
- Scaling
- AWS
- AI Tools
- Claude
- Sora 2
- CTE
- 2FA
- TOTP
- Programming Languages
- Prompts
- Developer Workflow
- API Gateway
- APIs
- Passport
- API Auth
- Learning
- Burnout
- Developer Growth
- Web Development
- SEO
- Kate Mall
- ChatGPT Atlas
- Agent Skills
- Middleware
- Laravel 12
- Collections
- Context Window
- Monitoring
- Commit Messages
- Self Review
- Growth
- Regex
- Programming Basics
- Text Processing
- Database Design
- Normalization
- Linux
- Server Security
- Linux Foundation
- Open Standards
- Legacy Code
- Documentation
- AI Workflow
- File Uploads
- Test Data
- Hashing
- Performance
- Caching
- Enums
- Scope Creep
- Estimation
- Codex
- Gemini CLI
- Timezones
- Carbon
- Bugs
- PHP 8.5
- Gemini 3
- GPT-5.1
- Data Integrity
- Event Loop
- Async
- Opus 4.5
- AI Models
- React
- Forms
- Frontend
- Backups
- AI Images
- DALL-E
- Midjourney
- Race Conditions
- Concurrency
- Legacy Code
- Refactoring
- Senior Engineer
- Scope
- LLM
- CDN
- Web
- Sub-Agents
- Soft Deletes
- Audit Log
- Concurrency
- AI Learning
- NestJS
- AI Evals
- Policies
- SPF DKIM DMARC
- Unicode
- UTF-8
- Knowledge Graph
- Value Objects
- Technical Debt
- Feature Flags
- Laravel Pennant
- Deployment
- Copilot
- Composer
- Dependencies
- Artisan
- Automation
- AWS S3
- Object Storage
- Cloud
- Small Language Models
- Ollama
- Production
- Sessions
- HTTP
- Mentoring
- SQL
- Virtual Machines
- Web Development
- HTTP/2
- QUIC
- Web Performance
- AI Integration
- LLM API
- SOLID
- OOP
- Hosting
- Serverless
- Merge Conflicts
- Temperature
- AI Development
- Reverse Proxy
- Nginx
- Infrastructure
- Verification
- Passkeys
- WebAuthn
- Teams
- Communication
- Stakeholders
- Monorepo
- CI/CD
- Versioning
- JSON Schema
- Livewire
- Inertia
- Meetings
- Distributed Systems
- Privacy
- Full-Stack
- T-Shaped Skills
- Money
- Notifications
- Web Security
- HTTP Headers
- CSP
- Function Calling
- Load Testing
- k6
- Data Extraction
- Debugging
- WebSockets
- SSE
- Real-Time
- Laravel Reverb
- Infrastructure as Code
- Terraform
- Side Projects
- Laravel Pint
- OpenAPI
- Swagger
- UX
- Multimodal
- Jest
- Pair Programming
- APIs
- Rate Limiting
- Resilience
- Dev Humour
- Design Tokens
- JWT
- API Keys
- Sessions
- PHPStan
- Rector
- Incidents
- Reporting
- Dashboards
- Zero Trust
- IAM
- Search
- Laravel Scout
- Junior Developers
- Mentoring
- Images
- WebP
- AVIF
- Bug Reports
- Let's Encrypt
- Design Docs
- Software Design
- Observers
- Replication
- Accountability
- Data Structures
- Reliability
- LLM Memory
- Error Handling
- Payments
- Payment Gateway
- Webhooks
- PCI DSS
- Observability
- OpenTelemetry
- Personal Brand
- Writing
- Conventions
- Dates
- Scheduling
- Disaster Recovery
- Compression
- Brotli
- Deadlines
- Developer Habits
- State Machines
- Tech Roles
- UUID
- ULID
- Horizon
- Planning
- Engineering Culture
- Ownership
- Soft Skills
- Socialite
- Cost Control
- Collations
- Unicode
- Octane
- PostgreSQL
Linux Permissions for Web Developers: Why chmod 777 Is Never the Fix
About Post
It's late, the deploy just finished, and the site shows a blank page. The log says Permission denied. Someone on a forum, years ago, with great confidence, says: chmod -R 777. You run it. The site works.
And you've just made every file of your application writable by every user and every process on that server.
Linux permissions look cryptic, but the whole system fits in your head in about ten minutes. Once it does, you'll fix "permission denied" properly, and 777 will make you wince.
Reading ls -l
Everything starts with this line:
$ ls -l storage/logs/laravel.log
-rw-rw-r-- 1 www-data www-data 48211 Nov 26 09:14 laravel.log
Ignore the first character for now (- means a file, d a directory). The next nine are three groups of three:
| Chars | Who | Here | Meaning |
|---|---|---|---|
rw- | Owner (www-data) | read, write | The owning user can read and change it |
rw- | Group (www-data) | read, write | Members of the group can too |
r-- | Others | read | Everyone else can only read it |
When a process touches a file, Linux checks in order: are you the owner? Use the owner bits. Otherwise, are you in the group? Use the group bits. Otherwise, use "others". Only one set applies.
The numbers are just a shorthand
Each permission has a value: read is 4, write is 2, execute is 1. Add them up per group:
7= rwx (4+2+1)6= rw- (4+2)5= r-x (4+1)4= r-- (read only)
So 644 is "owner can write, everyone can read", the normal mode for code files. 755 is the same for directories, plus the x they need. And 777 is "everyone can do everything".
Directories play by slightly different rules
This is the part that confuses people. On a directory:
- r lets you list the names inside it.
- x lets you enter it and reach the files inside. Without it, even a readable file inside is unreachable.
- w lets you create, rename and delete files inside it.
That last one surprises people: whether you can delete a file depends on the directory's permissions, not the file's. And a directory needs x for anything inside it to be usable, which is why directories are 755 when files are 644.
Who is "you", for a web app?
When someone visits your Laravel site, your PHP code doesn't run as you. It runs as the user PHP-FPM (or Apache's PHP module) runs as: commonly www-data on Debian and Ubuntu, apache or nginx on Red Hat-style systems and Amazon Linux. That's the user that needs access.
Laravel's rule is simple: the web server needs to read your code and write only to storage/ and bootstrap/cache/. Nothing else.
Why 777 is wrong
777 doesn't fix the problem. It removes the question. Every user and every process on the server can now modify your files: other sites on the same box, a compromised service, a cron job running as someone else.
The scariest version is a writable code directory combined with any upload or file-write bug. If an attacker can get a .php file into a place the web server executes, they're running code on your server. Good permissions are one of the layers that make that much harder.
And practically, 777 hides the real cause. The problem was usually ownership: the wrong user owns the files. Fix that, and the permissions can stay tight.
A sane setup for a Laravel app
One common approach: a deploy user owns the code, and the web server's group can read it and write only where it must. Adjust the user, group and path for your server:
# deploy user owns the code; web server group can read it
sudo chown -R deploy:www-data /var/www/app
sudo find /var/www/app -type d -exec chmod 755 {} \;
sudo find /var/www/app -type f -exec chmod 644 {} \;
# Laravel writes only here
cd /var/www/app
sudo chmod -R ug+rwX storage bootstrap/cache
sudo find storage bootstrap/cache -type d -exec chmod g+s {} \;
# secrets: owner read/write, group read, nobody else
sudo chmod 640 .env
Two details doing quiet work here. The capital X in ug+rwX adds execute only to directories (and files that were already executable), so you don't make every log file executable. And g+s (setgid) on a directory makes new files created inside it inherit the directory's group, so the group stays www-data no matter who creates them.
The rule: when you see "permission denied", ask which user is trying to do what to which file. Then fix ownership or the one directory involved. Never widen permissions on everything to make an error go away.
The classic Laravel trap: the log file owned by root
Everything works, until one day the site crashes because it can't write to storage/logs/laravel.log. The cause: someone ran php artisan as root (or a cron job did), Laravel created today's log file, and now it's owned by root. The web server can't append to it.
The fix is to run Artisan commands, queue workers and the scheduler as the same user as the web server:
sudo -u www-data php artisan migrate --force
sudo crontab -u www-data -e # add the scheduler line here
And configure Supervisor (or systemd) to run queue workers with user=www-data. One user for everything that runs your app means one set of permissions that works.
A "permission denied" checklist
- Which user is the process running as? (
ps aux | grep php) - Who owns the file, and what are its permissions? (
ls -l) - Can that user reach it? Every parent directory needs
x. (namei -l /full/path/to/fileshows the whole chain.) - Is it a directory problem? Creating or deleting needs
won the directory. - Still stuck on a Red Hat-style system? Check SELinux, which can deny access even when the permissions look right.
What's the strangest "permission denied" you've had to chase? Mine usually end with a file someone created as root at 2 AM.

Be first to comment it...