When you deploy a virtual machine on Oracle Cloud, you expect the basics to "just work" — especially something as simple as serving a webpage over HTTP. But sometimes the universe decides you need a refresher in deep-network debugging.
This is the story of how a perfectly healthy Ubuntu VM, running Nginx, with a public IP and correct VCN routing, still refused to serve a single HTTP request. The culprit wasn't Oracle Cloud. It wasn't Nginx. It wasn't DNS.
It was the VM itself.
And the journey to uncover that truth is a great lesson in cloud networking diagnostics.
Everything looked fine on the surface:
0.0.0.0/0 → Internet GatewayYet every external request failed:
curl -I http://m3ktab.com
curl: (7) Failed to connect: No route to host
Ping worked. SSH worked. But HTTP was dead.
This is the kind of problem that tricks you into thinking Oracle Cloud is misconfigured. Spoiler: it wasn't.
Before blaming the network, always check the basics:
sudo ss -tlnp | grep ':80\|:443'
Nginx was listening on all interfaces. The server was healthy. Local curl confirmed it:
curl -I http://127.0.0.1
So the problem wasn't Nginx.
This is where the investigation gets interesting. We ran a tcpdump to watch for incoming SYN packets:
sudo tcpdump -n -i any 'tcp port 80 and tcp[tcpflags] & tcp-syn != 0' -vv
Then triggered an external request. The VM immediately lit up with SYN packets:
This was the breakthrough. External traffic was reaching the VM. So the network path from the internet → Oracle → VCN → subnet → VNIC was working perfectly.
But the VM wasn't replying. No SYN/ACK. No handshake. No HTTP.
This narrowed the problem down to one place: the VM's OS firewall was silently dropping packets.
We checked iptables:
sudo iptables -L INPUT -n -v
And there it was — the villain:
A catch-all REJECT rule sitting above the accept rule for port 80. This meant:
From the outside, this looks exactly like "No route to host," even though the route is fine.
We inserted accept rules above the reject rule:
sudo iptables -I INPUT 1 -p tcp --dport 80 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
sudo iptables -I INPUT 1 -p tcp --dport 443 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
Instantly, the VM began replying to SYN packets. And the external curl finally returned:
Victory.
To ensure the rules survive reboot:
sudo apt install -y iptables-persistent
sudo netfilter-persistent save
Optional cleanup:
sudo iptables -D INPUT <reject-rule-line-number>
sudo netfilter-persistent save
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d m3ktab.com -d www.m3ktab.com