MHOSTMHOST

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 root or a user with sudo — 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.
Tip: work through the steps in order — each one assumes the previous one already checked out. If you already know which direction to dig (say, the browser is pointing straight at a certificate problem), feel free to jump directly to that step.

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:

bash
curl -I --max-time 10 https://example.com

Replace 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:

Important: if you run curl on the server itself against 127.0.0.1 or localhost, the result only proves the service responds locally — it says nothing about whether the site is reachable from outside: the firewall and DNS don't affect loopback traffic at all. For an accurate picture, test against the domain or the public IP, ideally from a different computer.
  • 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
bash
systemctl status nginx

Example 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 ago

The 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):

bash
sudo systemctl start nginx

If the status goes straight back to failed, check the system journal for the reason:

bash
sudo journalctl -xeu nginx --no-pager -n 50
Tip: if the service refuses to start at all and journalctl shows a configuration error or a port already in use, that's a deeper topic covered in "A service won't start".

Step 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:

bash
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))
  • StateLISTEN means 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.1 means 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).

Tip: if the site runs behind a reverse proxy — for example, a Node.js app from "Node.js apps with PM2 and Nginx", or a container from "Docker and Docker Compose" — it's Nginx or Apache that should be listening on ports 80/443, while the app itself listens on its own internal port, usually on 127.0.0.1. If nothing is listening on 80/443 but the app's internal port is, the reverse proxy isn't configured or isn't running.
Done: if there are LISTEN lines for ports 80 and 443 bound to 0.0.0.0 (or *), the service is listening the way it should — the problem isn't here. Move on to Step 4.

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)

bash
sudo ufw status

Example:

Status: active

To                         Action      From
--                         ------      ----
22/tcp                     ALLOW IN    Anywhere
80/tcp                     ALLOW IN    Anywhere
443/tcp                    ALLOW IN    Anywhere

If there are no lines for 80/443, add them:

bash
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

AlmaLinux / Rocky Linux (firewalld)

bash
sudo firewall-cmd --list-all

Look 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 https

If http/https aren't in the output, open them:

bash
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
Tip: if a request from outside just hangs and times out (curl error 28 from Step 1) instead of getting an instant connection refused, that's a classic symptom of a closed firewall port — the packets are simply being dropped with no response.
Tip: a full walkthrough of installing and configuring ufw — ports, the default policy, application profiles — is in "The ufw firewall: managing ports".
Done: if 80 and 443 are allowed in the output above, the firewall isn't blocking the site — the problem isn't here. Move on to Step 5.

Step 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.

bash
dig +short example.com

The 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:

bash
dig +short example.com @1.1.1.1

To 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:

bash
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.

Tip: how DNS records work and how long updates take to propagate is covered in "DNS basics"; how to actually point a domain at an mHost VPS is covered in "Connecting a domain to your server".
Done: if dig +short example.com returns your server's IP, DNS is set up correctly — the problem isn't here. Move on to Step 6 (if the site uses HTTPS) or Step 7.

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:

bash
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -subject -dates

Example output:

subject=CN=example.com
notBefore=Mar 11 08:00:00 2026 GMT
notAfter=Jun  9 08:00:00 2026 GMT
  • notAfter — the expiry date. If it's in the past, the certificate has expired — this is the most common cause of Chrome's NET::ERR_CERT_DATE_INVALID warning.
  • 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 covers www.example.com, but visitors go to example.com without www), 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:

bash
sudo certbot certificates
Found 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.pem

If the certificate has expired, check why auto-renewal didn't happen, and renew it manually:

bash
systemctl list-timers | grep certbot
sudo journalctl -u snap.certbot.renew.service --no-pager -n 50
sudo certbot renew

The 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.
Tip: if the site runs on Caddy, it obtains and renews its own certificates without certbot, so certbot certificates won't apply. Check its own logs instead: journalctl -u caddy -n 50 --no-pager — any certificate-issuance errors will show up there.
Tip: the full walkthrough of setting up HTTPS with certbot, including auto-renewal, is in "Free SSL with Let's Encrypt (certbot)".

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.log and /var/log/nginx/access.log (all distros).
  • Apache on Ubuntu/Debian/var/log/apache2/error.log and /var/log/apache2/access.log, or separate per-site files if they're set in the VirtualHost via ErrorLog/CustomLog.
  • Apache on AlmaLinux/Rocky Linux/var/log/httpd/error_log and /var/log/httpd/access_log.
  • Caddy — by default logs to the system journal rather than a file: journalctl -u caddy -n 50 --no-pager.
bash
sudo tail -n 50 /var/log/nginx/error.log
Tip: if the site's configuration sets its own log paths, the actual paths for nginx can be found with sudo nginx -T | grep -E 'access_log|error_log' — it prints every access_log/error_log directive from the main config and all included files at once.

If the service itself won't even start, so nothing ever reaches error.log, check the system journal instead:

bash
sudo journalctl -xeu nginx --no-pager -n 50

Typical 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) or DocumentRoot (Apache), or there's no index.html/index.php in 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/restorecon in "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".