I Tried Deploying React + Laravel on One Server… and It Was Messier Than I Expected
So I thought this would be easy.
React frontend. Laravel backend. One EC2 server. One domain. Done.
That was the plan.
Reality was very different.
The plan (which looked simple on paper)
React → UI
Laravel → API
Nginx → handle routing
So basically:
/→ React/api→ Laravel
I’ve seen this setup before, so I thought I’d just follow the same idea and it’ll work.
Spoiler: it didn’t.
First confusion — Node setup
I installed Node using apt like this:
apt install nodejs npm
Seemed fine.
Then React started acting weird during build. Some packages didn’t behave properly.
Checked version — it was old.
At that point I realized: Default packages on Ubuntu are usually outdated.
Switched to NVM, installed latest Node — things immediately became stable.
That was my first “ok, this is not going to be straightforward” moment.
Then a random error: libatomic
Out of nowhere:
libatomic.so.1: cannot open shared object file
Looks scary. Sounds like something deep is broken.
Actual fix?
sudo apt install libatomic1
That’s it.
This taught me something important: Not every scary error is complicated. Sometimes it’s just one missing package.
Laravel “working”… but not really
I ran:
php artisan serve
Opened browser → API worked.
So I thought: “Backend is fine, move on.”
Wrong move.
Once I connected Nginx, Laravel stopped responding completely.
After wasting time, I realized:
👉 Nginx doesn’t use artisan serve 👉 It uses PHP-FPM
And I didn’t even have PHP-FPM installed.
Installed it:
php -v
sudo apt install php8.3-fpm
Only then Laravel actually became usable in real setup.
That was a big gap in understanding.
The most confusing bug — API returning React
This one took time.
I hit:
/api/posts
And instead of JSON, I got my React app.
At first I thought:
maybe route issue?
maybe Laravel problem?
No.
It was Nginx.
I had:
location /api {
Which I assumed was correct.
It wasn’t strict enough.
Changed it to:
location ^~ /api/ {
And suddenly everything started working.
That tiny change fixed the whole system.
Another mistake — using alias without understanding
I copied this from somewhere:
alias /var/www/backend/public;
It looked fine.
But it broke PHP execution in weird ways.
I didn’t fully understand how alias works with PHP.
So I removed it and used root instead.
Everything became predictable again.
Lesson: If you don’t understand something in Nginx, don’t blindly use it.
Domain issues (DuckDNS)
I used a free domain from DuckDNS.
Sometimes it worked, sometimes it didn’t.
Turned out the IP wasn’t updated properly.
So even though server was running fine, domain wasn’t pointing correctly.
Simple issue, but wasted time.
“Connection refused” — the classic trap
Site didn’t open at all.
No error page, just refused.
Problem?
Port 80 wasn’t open in AWS.
That’s it.
Opened ports → everything worked.
When things finally worked
After all this:
React loaded properly
Laravel API returned JSON
Routing worked (
/api)No more weird behavior
Then I added SSL using Let's Encrypt That part was actually smooth compared to everything else.
What I actually learned (not theory)
Most issues are not in React or Laravel → they are in server config
If something “should work” but doesn’t → assume config problem first
Don’t trust default installs → check versions
Nginx is powerful but unforgiving → small mistake = big problem
One thing I misunderstood earlier
I thought since both apps are on same server, API calls are internal.
Not true.
Browser still makes request over network → it counts as bandwidth.
Final thought
This setup works.
But it’s not “easy” unless you actually understand what’s happening.
If you just copy configs, you’ll get stuck.
If you debug step by step, it starts making sense.
That’s it.
No perfect guide. Just what actually happened.