- 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
Securing a Laravel App: A Practical 12-Point Checklist Before You Go Live
About Post
Laravel gives you a lot of security for free. CSRF tokens, escaped Blade output, hashed passwords, parameter binding in the query builder: all switched on before you write a line of code.
That's exactly why Laravel apps get breached in boring ways. The framework covers the defaults, and the holes come from the five-minute decisions we make around it: a debug flag left on, a $request->all() that saves too much, a controller that checks you're logged in but not whether the record is yours.
This is the checklist I run through before a Laravel 12 app goes live, and again whenever an app has been running long enough that nobody remembers how it was set up. It's grouped by where the problem lives.
Part 1: the environment
1. Debug mode is off in production
With APP_DEBUG=true, any exception renders a detailed error page: stack traces, file paths, queries and request details. That is a gift to anyone poking at your app. Production should have APP_ENV=production and APP_DEBUG=false, and it's worth a line in your deploy script that refuses to continue if it doesn't.
2. The APP_KEY is secret, unique and never committed
The app key encrypts your cookies and session data, signs URLs, and backs any encrypted casts on your models. If it leaks, someone can forge what your app trusts. Every environment gets its own key, it lives in the server's environment or secret store, and it never appears in Git.
If you ever need to rotate it, Laravel 11 and 12 support APP_PREVIOUS_KEYS, so existing encrypted values can still be decrypted while new ones use the new key. Users don't all get logged out at once.
3. The web server points at public/, and nothing else
The document root must be the public folder. If it's the project root, your .env, storage/logs and composer.json may be one URL away. It sounds obvious. It still happens on shared hosting and hastily configured servers.
Part 2: the code
4. Mass assignment is explicit
This line looks harmless:
$user->update($request->all());
Now imagine the model has $guarded = [] and a column called is_admin. Anyone can send is_admin=1 in the request body and promote themselves. The fix is two layers: list $fillable on the model, and only ever save validated data.
$user->update($request->validated());
A nice extra: call Model::preventSilentlyDiscardingAttributes(! app()->isProduction()) in a service provider. In development, Laravel will throw when you try to fill an attribute that isn't fillable, instead of silently ignoring it.
5. Every input is validated, including the ones you "control"
Hidden fields, select boxes and query strings are all user input. A form request or $request->validate() on every write endpoint, with rules for type, length and allowed values. Two cases deserve special attention:
- Sort and filter columns. Column names can't be bound as parameters, so
orderBy($request->input('sort'))deserves a whitelist (Rule::in(['created_at', 'amount'])). - Raw SQL.
whereRaw("name = '$name'")is SQL injection with extra steps. Use bindings:whereRaw('name = ?', [$name]).
6. Authorization checks ownership, not just login
This is the most common serious bug I see in code reviews, in any framework. The route has auth middleware, so it feels protected:
public function show(int $id)
{
return Contract::findOrFail($id); // any logged-in user, any contract
}
Change the ID in the URL and you're reading someone else's data. That's an IDOR (insecure direct object reference), and it's why policies exist. Write a ContractPolicy and check it:
public function show(Contract $contract)
{
Gate::authorize('view', $contract);
return $contract;
}
Small Laravel 11+ gotcha: the default base controller no longer includes the AuthorizesRequests trait, so $this->authorize() won't exist unless you add it back. Gate::authorize() works everywhere.
7. Blade output stays escaped
{{ $value }} escapes HTML. {!! $value !!} does not. Every {!! !!} in your views should have a reason you can say out loud, and user-provided content is never that reason.
8. Uploads are treated as hostile
Validate type and size ('file' => 'required|file|mimes:pdf,jpg,png|max:5120'), store sensitive files on a private disk rather than public, and let Laravel generate the file name. Serve private files through a controller that checks authorization, or a temporary signed URL.
Part 3: the edges of the app
9. CSRF protection has no lazy exceptions
Laravel validates CSRF tokens on web routes by default. The risk is the exception list: someone adds a wildcard to make a form "just work" and forgets about it. Webhooks are a legitimate exception, but they should be verified by signature instead.
10. Sensitive routes are rate limited
Login, password reset, OTP verification and anything that sends an SMS or email. Define a named limiter in AppServiceProvider::boot():
RateLimiter::for('login', function (Request $request) {
return Limit::perMinute(5)->by($request->input('email').'|'.$request->ip());
});
Then attach ->middleware('throttle:login') to the route. Keying on email plus IP slows down password guessing without letting one attacker lock out a real user from everywhere.
11. Security headers are set
A small middleware covers the basics:
public function handle(Request $request, Closure $next): Response
{
$response = $next($request);
$response->headers->set('X-Content-Type-Options', 'nosniff');
$response->headers->set('X-Frame-Options', 'SAMEORIGIN');
$response->headers->set('Referrer-Policy', 'strict-origin-when-cross-origin');
$response->headers->set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
return $response;
}
Register it in bootstrap/app.php with $middleware->append(SecurityHeaders::class). Only send HSTS once you're sure every subdomain is on HTTPS. A Content Security Policy is the next step up, but it needs testing against your real scripts, so roll it out in report-only mode first.
12. Dependencies are audited and updated
Your app is your code plus a few hundred packages. Run composer audit and npm audit in CI, keep Laravel on a supported version, and apply patch releases regularly. Small, frequent updates are far less scary than one giant upgrade after two years.
The rule behind all twelve: Laravel protects the defaults. Your job is to protect every place where you stepped away from them: a raw query, an unescaped echo, a CSRF exception, a $guarded = []. Search your codebase for those first.
The checklist, short version
APP_DEBUG=false, uniqueAPP_KEYper environment, document root onpublic/$fillableplus$request->validated(), never$request->all()- Validation on every write, whitelists for sort columns, bindings for raw SQL
- Policies that check ownership, not just authentication
- Escaped Blade, private storage for private uploads
- No lazy CSRF exceptions, throttled auth routes, security headers
composer auditin CI and regular updates
The authorization docs are worth a reread even if you've used policies for years. And I'm curious: which item on this list do you most often find missing when you open an inherited Laravel project?

Be first to comment it...