When you enter 93.184.216.34
directly into your browser instead of example.com
, the server doesn't know which website to serve. This happens because modern web servers use name-based virtual hosting, where multiple domains can share the same IP address.
Here's what happens in the HTTP protocol:
GET / HTTP/1.1
Host: example.com
Without the Host header (which browsers automatically include when you use a domain name), the server can't determine which virtual host configuration to use.
You can demonstrate this behavior using cURL:
# This works (includes Host header)
curl -v https://example.com
# This fails (no Host header)
curl -v http://93.184.216.34
Here's a typical Nginx virtual host configuration:
server {
listen 80;
server_name example.com;
location / {
root /var/www/example.com;
index index.html;
}
}
For development purposes, you can modify your /etc/hosts
file:
93.184.216.34 example.com
Or configure your web server to handle direct IP access:
server {
listen 80 default_server;
return 444; # Special Nginx code to close connection
}
Blocking direct IP access is actually good security practice:
- Prevents host header injection attacks
- Blocks malicious scanners looking for default server responses
- Improves HTTPS certificate validation
When you run host example.com
and get an IP like 93.184.216.34, but entering that IP directly in your browser fails, there are several technical reasons behind this behavior:
> curl -I http://93.184.216.34
HTTP/1.1 404 Not Found
Modern web hosting relies heavily on the HTTP Host
header. When you access a site via domain name:
GET / HTTP/1.1
Host: example.com
But with direct IP access, browsers typically send:
GET / HTTP/1.1
Host: 93.184.216.34
Most web servers host multiple sites on a single IP using virtual hosting. Here's a simplified Node.js example:
const http = require('http');
http.createServer((req, res) => {
if(req.headers.host === 'example.com') {
res.end('Welcome to example.com');
} else {
res.writeHead(404);
res.end('Host not found');
}
}).listen(80);
HTTPS adds another layer of complexity. Certificates are issued for domain names, not IPs:
> openssl s_client -connect 93.184.216.34:443
...
subject=CN = example.com
...
For testing purposes, you can override the Host header:
curl -H "Host: example.com" http://93.184.216.34
Or modify your local hosts file:
93.184.216.34 example.com
This behavior stems from HTTP/1.1 specifications (RFC 2616) that made the Host header mandatory. Prior to HTTP/1.1, IP-based access was more common.