Imagine that you have a house.
Every evening at 8 PM, you have to tidy your bedroom.
Pretty simple, right?
You are already inside the house. Your bedroom is upstairs.
So you walk upstairs, open the door, and tidy your room.
But what if, instead, you decided to do this:
« I am going to send myself a letter asking myself to tidy my bedroom. »
Sounds ridiculous?
That’s exactly what happens when a cron job uses wget to call an URL on the same server.
📮 The ridiculous letter
Let’s imagine your server is your house.
Your application is inside the house.
Your PHP code is inside the house.
Your database is inside the house.
Your cron job is also inside the house.
Everything you need is already there.
But instead of directly executing the code, the cron job does something like:
wget https://www.example.com/cron/my-task
In other words:
« Hey website, could you please call yourself and execute this task? »
Let’s see what happens.
❌ The wget approach
You need to tidy your bedroom at 8 PM.
Instead of going upstairs, you:
- 📝 Write a letter: « Dear me, please tidy my bedroom. »
- 📮 Put it in an envelope.
- 🚶 Leave your house.
- 🚶 Walk to the mailbox.
- 📬 Put the letter in the mailbox.
- 🚚 The postal service collects it.
- 🏢 The letter goes through the postal system.
- 🚚 The letter eventually comes back to your neighborhood.
- 📭 The letter is delivered to your own mailbox.
- 🚶 You go outside and get it.
- 📖 You open the letter.
- 🤔 You read: « Please tidy your bedroom. »
- 🏠 You finally go upstairs.
- 🧹 You tidy the room.
Congratulations.
You have successfully used the entire postal system to tell yourself something you already knew.
💻 What does this look like on a real server?
The same thing happens technically.
A cron job might contain:
*/5 * * * * wget -q -O /dev/null https://www.example.com/cron/process-orders
At first glance, this may look perfectly reasonable.
Every five minutes, the server calls an URL.
But look at what is actually happening.
The request has to go through the web stack:
CRON
│
▼
wget
│
▼
DNS / hostname resolution
│
▼
Network
│
▼
Firewall / load balancer / CDN / proxy
│
▼
Web server
│
▼
PHP / PHP-FPM
│
▼
Magento / application
│
▼
Task
And all of this happens to execute code that is already on the same machine.
It is the technical equivalent of walking around the block to enter your own front door.
🏠 The better solution: use the stairs!
If the code is already on the server, the simplest solution is usually to execute it directly.
Instead of:
wget https://www.example.com/cron/process-orders
you could have a CLI command:
php bin/my-command
And your cron becomes:
*/5 * * * * php /path/to/application/bin/my-command
Now the journey looks more like this:
CRON
│
▼
PHP CLI
│
▼
Application
│
▼
Task
That’s it.
No letter.
No mailbox.
No postal service.
No trip around the city.
Just:
🏠 « I’m already home. I’ll just walk upstairs. »
🚀 1. It’s faster
An HTTP request introduces additional work.
Even on the same server, you may have:
- DNS resolution
- TCP connections
- TLS negotiation if using HTTPS
- HTTP processing
- web server processing
- PHP-FPM
- HTTP headers
- authentication
- routing
- application bootstrap
- network timeouts
A CLI command can avoid most of this.
This becomes particularly important when the cron task runs frequently or performs heavy processing.
A task running every minute doesn’t need to spend part of every minute pretending to be an external visitor.
🧠 2. The application knows it’s a scheduled task
A CLI command can be designed specifically for background processing.
For example:
php bin/magento my:process-orders
The application knows:
« This isn’t a customer visiting a webpage. This is a scheduled background operation. »
That’s a much cleaner separation.
An HTTP controller, on the other hand, is designed to handle an HTTP request.
Using a web URL as a replacement for a CLI command mixes two different responsibilities:
HTTP
→ customer/browser/API requests
CLI
→ server-side commands/jobs/maintenance
🔐 3. It reduces your attack surface
This is perhaps the most important point.
Suppose you create:
https://www.example.com/cron/process-orders
You now have an HTTP endpoint that triggers an administrative operation.
Even if you try to hide it, the URL exists.
Someone can potentially discover it.
Someone can call it.
Someone can call it repeatedly.
Someone can try to manipulate parameters.
Someone can generate thousands of requests.
And suddenly your « private cron URL » isn’t really private anymore.
« But we can add a secret token! »
Yes.
For example:
https://www.example.com/cron/process-orders?token=ABC123
That’s better than nothing.
But now you’re building an authentication mechanism for an HTTP endpoint…
…because you wanted to execute a local task.
The CLI version can instead rely on the server’s existing permissions and execution environment.
php bin/my-command
No public URL required.
No HTTP endpoint required.
No secret token in a URL.
🐌 4. HTTP introduces failure points you don’t need
Remember our postal service?
The letter can get lost.
The same principle applies to HTTP.
Your request can fail because of:
- DNS problems
- connection failures
- firewall rules
- reverse proxy problems
- CDN configuration
- SSL certificate issues
- HTTP timeouts
- web server limits
- PHP-FPM limits
- rate limiting
- WAF rules
- authentication
- maintenance mode
- incorrect routing
- a web server restart
And the application task itself may be perfectly healthy.
Imagine discovering this at 8:05 AM:
« The nightly order processing didn’t run. »
Why?
Because the web server had a temporary problem.
That’s frustrating when the job could simply have been executed locally.
⏱️ 5. Timeouts become especially annoying
HTTP requests are designed around the idea that a client sends a request and eventually receives a response.
But background jobs can take a long time.
For example:
Process 10 products → 2 seconds
Process 1,000 products → 3 minutes
Process 100,000 products → 45 minutes
If you trigger the job through HTTP, you now have to think about:
HTTP timeout
Nginx timeout
PHP timeout
PHP-FPM timeout
proxy timeout
CDN timeout
The task might still be running…
…while the HTTP client has already given up.
That’s another unnecessary complication.
A CLI process is much more natural for long-running jobs.
📊 6. Monitoring is easier
A proper CLI command can return an exit code.
For example:
php bin/my-command
can return:
0 → success
1 → error
Cron and monitoring systems understand this very well.
You can also log meaningful information:
Starting order processing...
1,245 orders found
1,245 orders processed
17 errors
Process completed
With an HTTP request, you often end up with something like:
wget ...
and then you have to inspect HTTP status codes, output, logs, response bodies, etc.
Again: more machinery for the same job.
🧱 7. It bypasses the normal web traffic
This is particularly important for an e-commerce website.
Your website is designed to serve customers.
Customers generate HTTP traffic:
Customer
↓
Website
↓
Magento
Your background jobs are different:
Cron
↓
CLI
↓
Magento
You don’t necessarily want an internal background task to compete with customer traffic through the same HTTP infrastructure.
Otherwise, your server can end up doing this:
Customers ┐
├──→ Web Server → PHP-FPM → Magento
Cron jobs ┘
When the cron jobs could instead be handled separately:
Customers ──→ Web Server ──→ PHP-FPM
Cron ──────→ CLI ──────────→ Application
This doesn’t magically make the job cheaper—it still consumes CPU, memory, database connections, etc.—but it removes unnecessary web-layer overhead and makes the architecture clearer.
⚠️ But is wget always wrong?
No.
This is important.
Using wget to call an HTTP endpoint is not inherently bad.
HTTP is exactly what you should use when the task is genuinely an HTTP operation.
For example:
wget https://api.example.com/import
might be perfectly legitimate if:
- the API belongs to another server
- the service is external
- the endpoint is intentionally public/private API infrastructure
- the application is deliberately designed around HTTP
- the request must go through an HTTP gateway
The problem is specifically this pattern:
A cron job on your server calling an HTTP URL on the same application, just to execute local code.
That’s where the architecture starts looking like the person mailing a letter to themselves.
🧑💻 Magento makes this particularly obvious
In Magento, you already have a CLI framework.
For example:
bin/magento
Magento commands are designed for exactly this kind of operation.
Instead of creating:
https://www.example.com/custom/cron/run
you can create something like:
bin/magento vendor:module:process
And your cron can execute:
*/5 * * * * php /path/to/magento/bin/magento vendor:module:process
This gives you a clear distinction:
Web Controller
↓
HTTP request
↓
Customer / external system
CLI Command
↓
Scheduled job
↓
Cron / administrator / deployment
Much easier to understand.
Much easier to secure.
Much easier to monitor.
🏁 The simple rule
Here’s the rule I would use when reviewing an existing application:
🌍 Calling another server?
Use HTTP.
Server A ──HTTP──→ Server B
That’s normal.
🏠 Calling your own application from the same server?
Prefer CLI.
Cron ──→ CLI ──→ Application
Rather than:
Cron ──→ wget ──HTTP──→ Your own website
📨 The final version of our story
So, if someone asks:
« Why is using
wgetin cron to call our own website a bad practice? »
You can simply tell them:
Imagine you’re inside your house and you need to tell yourself to clean your bedroom.
You could walk upstairs and clean it.
Or you could write yourself a letter, put it in the mailbox, wait for the postal service to deliver it, collect the letter, read it, and then finally go upstairs.
Both methods work.
But one of them is obviously ridiculous.
That’s what calling your own website with
wgetfrom cron can be.If the code is already on the server, execute it directly with CLI whenever possible.
Don’t leave the house just to send yourself a letter. 🏠📮
