My website won't load
When a site won't load, the cause is almost always one of a handful of things: the web server isn't running, the port is blocked by the firewall, DNS points somewhere else, the SSL certificate has expired, or there's a configuration error you can only see in the logs. This article walks through step-by-step diagnostics on your mHost VPS — from the process on the server out to the user's browser — to help you quickly find exactly what broke.
What you'll need
- SSH access as
rootor a user withsudo— most checks are run from the terminal. - The domain that's supposed to point at this VPS — needed for Steps 5 and 6 (DNS and SSL). If the site is only meant to be reachable by IP for now, you can skip those two steps.
- Knowledge of which web server is installed — Nginx, Apache, or Caddy. If you're not sure, see "Nginx: install and your first site", "Apache: install and virtual hosts", or "Caddy: automatic HTTPS" — or just try each service's command from Step 2 in turn.
Step 1. Identify the symptom: how exactly is the site failing
How the server responds (or fails to respond) immediately narrows down the list of suspects. Check from any computer with internet access — it doesn't have to be the server itself:
curl -I --max-time 10 https://example.comReplace example.com with your own domain. If the site doesn't have HTTPS yet, swap https:// for http://. What curl comes back with tells you which step to check first:
- Error `curl: (6)` — "Could not resolve host" — the domain doesn't resolve at all: a typo in the address, or DNS isn't set up yet — see Step 5.
- Error `curl: (7)` — "Connection refused" — packets reach the server, but the port is closed: the service isn't running, or it's listening on the wrong address or port — see Steps 2–3.
- Error `curl: (28)` — "Connection timed out" — the request just hangs and gets no response at all: most often the firewall is silently dropping packets — see Step 4. A wrong IP in the DNS record can also cause this.
- Error `curl: (60)` — "SSL certificate problem" (or a certificate warning in the browser) — a TLS certificate issue — see Step 6.
- The response comes back with status 500, 502, 503, or 504 — the web server is responding, but with an error on its own side or on the side of the application behind it — see Step 7 (logs).
- The response comes back, but it's not what you expected (a 404, someone else's site, an old version of the page) — the network and certificate are fine, the issue is in the site's own configuration — also see Step 7.
If you're not sure what's actually wrong, just work through Steps 2–7 in order.
Step 2. Check that the web server is running
The most common cause is that the web server's service has been stopped, or it crashed after a configuration error. The systemctl status command depends on which web server is installed:
- Nginx (all distros) —
systemctl status nginx - Apache on Ubuntu/Debian —
systemctl status apache2 - Apache on AlmaLinux/Rocky Linux —
systemctl status httpd - Caddy (all distros) —
systemctl status caddy
systemctl status nginxExample when everything is fine:
● nginx.service - A high performance web server and a reverse proxy server
Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; preset: enabled)
Active: active (running) since Thu 2026-07-09 10:12:03 UTC; 2h 14min ago
Docs: man:nginx(8)
Main PID: 1234 (nginx)Example when the service has crashed:
● nginx.service - A high performance web server and a reverse proxy server
Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; preset: enabled)
Active: failed (Result: exit-code) since Thu 2026-07-09 12:40:11 UTC; 3min agoThe Active line is what matters: active (running) — the service is up; inactive (dead) — stopped, but didn't crash; failed — crashed, usually from a configuration error or a port that's already taken.
If it's not active (running), start it (replace nginx with the right service name from the list above):
sudo systemctl start nginxIf the status goes straight back to failed, check the system journal for the reason:
sudo journalctl -xeu nginx --no-pager -n 50Step 3. Check it's listening on ports 80 and 443
A service can be running but still listening on the wrong port or the wrong network address — in which case nothing outside can reach it anyway. Check what's actually listening:
sudo ss -tlnp | grep -E ':80 |:443 'Example output when everything is fine:
LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=1234,fd=6))
LISTEN 0 511 0.0.0.0:443 0.0.0.0:* users:(("nginx",pid=1234,fd=7))State—LISTENmeans the port is open and waiting for connections.Local Address:Port— the address and port the process is bound to.0.0.0.0(or*) means all network interfaces, so the port is reachable from outside;127.0.0.1means only the server itself can reach it — not from outside, and not even via the server's own domain or IP.Process— which process actually holds the port (name and PID).
If there are no lines at all for port 80 or 443, nobody on the server is listening on them. Go back to Step 2: either the service isn't running, or its configuration points at a different listen (nginx) / Listen (Apache).
Step 4. Check the firewall
Even if the service is listening on the right port, the firewall on the VPS itself can still be blocking incoming connections from outside.
Ubuntu / Debian (ufw)
sudo ufw statusExample:
Status: active
To Action From
-- ------ ----
22/tcp ALLOW IN Anywhere
80/tcp ALLOW IN Anywhere
443/tcp ALLOW IN AnywhereIf there are no lines for 80/443, add them:
sudo ufw allow 80/tcp
sudo ufw allow 443/tcpAlmaLinux / Rocky Linux (firewalld)
sudo firewall-cmd --list-allLook for the services line in the output — it should include http and https (or the matching ports should be listed under ports):
services: ssh dhcpv6-client http httpsIf http/https aren't in the output, open them:
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reloadStep 5. Check DNS: does the domain point at this server
Service running, port open — and the site still won't load by domain name? Check whether the domain actually points at this VPS's IP.
dig +short example.comThe expected result is an IP address that matches your VPS's public IP (you can find it on the server's card in VMmanager 6, or right on the server itself with curl -4 ifconfig.me).
If dig comes back empty or with a different IP, keep in mind that changes take time to propagate (TTL), and check directly against a public DNS resolver to bypass any local cache:
dig +short example.com @1.1.1.1To confirm it really is a DNS problem and not the server itself, hit the server directly by IP with a Host header — as if the domain already resolved correctly:
curl -I -H "Host: example.com" http://203.0.113.10/Replace 203.0.113.10 with the server's actual IP. If the site loads that way (a 200 OK), but not by domain, the problem is 100% DNS, not the server.
Step 6. If the site uses HTTPS, check the certificate
Relevant if the browser shows a certificate warning, or the connection drops specifically on port 443 (not silently — that would be Step 3 or 4).
Check the certificate itself without opening a browser:
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -subject -datesExample output:
subject=CN=example.com
notBefore=Mar 11 08:00:00 2026 GMT
notAfter=Jun 9 08:00:00 2026 GMTnotAfter— the expiry date. If it's in the past, the certificate has expired — this is the most common cause of Chrome'sNET::ERR_CERT_DATE_INVALIDwarning.subject— the domain the certificate was issued for. If it doesn't match what's in the browser's address bar (say, the certificate only coverswww.example.com, but visitors go toexample.comwithoutwww), the browser will show a name-mismatch error even though the certificate itself is still valid.
If the certificate was issued through certbot (usually alongside Nginx or Apache), list the certificates and their status:
sudo certbot certificatesFound the following certs:
Certificate Name: example.com
Domains: example.com www.example.com
Expiry Date: 2026-06-09 08:00:00+00:00 (INVALID: EXPIRED)
Certificate Path: /etc/letsencrypt/live/example.com/fullchain.pem
Private Key Path: /etc/letsencrypt/live/example.com/privkey.pemIf the certificate has expired, check why auto-renewal didn't happen, and renew it manually:
systemctl list-timers | grep certbot
sudo journalctl -u snap.certbot.renew.service --no-pager -n 50
sudo certbot renewThe most common reasons auto-renewal fails:
- Port 80 was closed at renewal time — certbot verifies domain ownership over HTTP, and renewal fails without port 80 open — see Step 4.
- The domain stopped pointing at this server — the DNS record changed, or the site was moved — see Step 5.
- The certificate was issued for a different name than what's actually being opened in the browser — renewal itself isn't the issue here; the certificate needs to be reissued for the right domains.
Step 7. Read the web server logs
If the service is running, the ports are open, DNS and the certificate check out, and the site still isn't responding the way it should, the answer is almost always in the logs.
- Nginx —
/var/log/nginx/error.logand/var/log/nginx/access.log(all distros). - Apache on Ubuntu/Debian —
/var/log/apache2/error.logand/var/log/apache2/access.log, or separate per-site files if they're set in theVirtualHostviaErrorLog/CustomLog. - Apache on AlmaLinux/Rocky Linux —
/var/log/httpd/error_logand/var/log/httpd/access_log. - Caddy — by default logs to the system journal rather than a file:
journalctl -u caddy -n 50 --no-pager.
sudo tail -n 50 /var/log/nginx/error.logIf the service itself won't even start, so nothing ever reaches error.log, check the system journal instead:
sudo journalctl -xeu nginx --no-pager -n 50Typical log entries and what they mean:
- `bind() to 0.0.0.0:80 failed (98: Address already in use)` — the port is already taken by another process: for example, Apache and Nginx are both running at once, or an old process is stuck running. Find it:
sudo ss -tlnp | grep ':80 '. - `No such file or directory` for the site's root — a wrong
root(nginx) orDocumentRoot(Apache), or there's noindex.html/index.phpin the directory. - `(13: Permission denied)` reading the site's files, `403 Forbidden` in the browser — missing permissions on files/directories, or (on AlmaLinux/Rocky) SELinux is blocking access — there's a worked example of setting the context with
semanage/restoreconin "Nginx: install and your first site". - `connect() failed (111: Connection refused) while connecting to upstream` — this isn't an error from the web server itself, but from the backend application behind it: it isn't running, or it's listening on the wrong port. Check the application's process — see "Node.js apps with PM2 and Nginx", "Python and FastAPI: production deployment", "Deploying Django on a VPS", "Docker and Docker Compose".
- 502 or 504 in the browser and in access.log — the same thing: the backend is unreachable or responding too slowly.
- Logs aren't growing at all, or the last entry cuts off mid-line — the server has probably run out of disk space — see "Disk is full".
- The site loads, but very slowly or not every time — that's no longer "won't load" but high server load — see "High server load".
What's next
If you've been through every step and the site still won't load, or the cause wasn't among those listed above, the issue is most likely no longer in the VPS infrastructure but in the application's own code or configuration. Related topics: setting up HTTPS end to end — "Free SSL with Let's Encrypt (certbot)"; the firewall — "The ufw firewall: managing ports"; if the service itself won't start — "A service won't start"; a general baseline security checklist — "Basic VPS security checklist".