Website down? A step-by-step checklist to find the cause

By the PULSX team. Updated . 16 minute read.

TL;DR

Work from the outside in. First confirm the site is down for everyone, then check DNS, the TLS certificate, the network path and the HTTP response. Only then log in to the server and look at the process, disk, memory, recent deploys, the database and outside services.

Key takeaways

  • Confirm the outage from a second network before you change anything.
  • Check in order: DNS, TLS, network, HTTP, server, recent changes, database, third parties, CDN or host.
  • The symptom points at the layer: NXDOMAIN is DNS, "connection refused" is a stopped service, 502 is a dead application, 504 is a slow one.
  • A full disk and the out-of-memory killer are two common causes of a site that "just stopped". Run df -h and dmesg early.
  • If a deploy went out just before the outage, roll it back first and debug afterwards.
  • Write down times and commands as you go. You will need them for the review.

Is the site down for everyone, or only for you?

Check from a second network before you touch the server. A "the site is down" report is often one person's connection, DNS cache or VPN, and nothing on the server will fix that.

Do these three things. They take about a minute.

  1. Test from outside. Put the address into the website down checker. It requests the page from a server, not from your machine.
  2. Test from your phone on mobile data. Turn wifi off first, so the phone uses a different network and a different DNS resolver.
  3. Test from a terminal on another machine, if you have one:
curl -sS -o /dev/null -m 15 -w "%{http_code} in %{time_total}s\n" https://example.com/

If the site loads from outside and fails only for you, the fault is local. Check these in turn: your VPN, a browser extension, an entry in your hosts file, an office firewall, and a stale DNS cache. To clear the cache:

# macOS
sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder

# Windows
ipconfig /flushdns

If it fails from everywhere, it is a real outage. Write down the time now, in UTC, and what you saw: a timeout, an error code, a certificate warning. That one line starts your incident log, and the symptom decides which step below matters most.

Is DNS still pointing at your server?

Ask DNS for the address of your domain and compare it with the address your server really has. If DNS returns nothing or the wrong address, no visitor can reach you, however healthy the server is.

$ dig +short example.com A
203.0.113.10

One address that matches your server means DNS is fine. Move on. If the output is empty, ask for the status:

$ dig example.com A | grep status
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 41234
StatusWhat it meansLikely cause
NOERROR, with an answerThe record exists.DNS is working. Check that the address is the right one.
NOERROR, no answerThe name exists but has no record of this type.The A record was deleted or renamed.
NXDOMAINThe name does not exist at all.An expired domain, a deleted zone, or a typo in the name.
SERVFAILThe resolver could not get an answer.Broken or unreachable nameservers, or a DNSSEC fault.

Has the domain expired?

$ whois example.com | grep -i -E "expir|status"
Registry Expiry Date: 2026-10-10T04:00:00Z
Domain Status: clientHold

An expiry date in the past explains everything. A status of clientHold or serverHold means the domain has been taken out of DNS, often because it expired or was suspended. Renew it or resolve the hold at your registrar. The site returns as resolvers pick the domain up again.

Are the nameservers right?

$ dig +short example.com NS
ns1.example-dns.com.
ns2.example-dns.com.

These must be the nameservers of the company where you edit your records. If they name an old provider, your records are being served from a place you no longer control. Fix the nameservers at the registrar.

After any DNS fix, allow up to the record's TTL for the change to reach every resolver.

No terminal to hand? The DNS lookup tool shows the same records in a browser.

Is the TLS certificate valid?

Read the certificate's dates. An expired certificate does not stop the server, but every browser blocks the page with a full-screen warning, so for visitors the site is down.

$ echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
    | openssl x509 -noout -dates -subject
notBefore=Jul 12 08:01:22 2026 GMT
notAfter=Oct 10 08:01:21 2026 GMT
subject=CN = example.com

Compare notAfter with today's date. In this output the certificate ran out on 10 October. curl gives the same verdict in one line:

$ curl -I https://example.com
curl: (60) SSL certificate problem: certificate has expired

If the dates are fine, three other certificate faults produce a warning:

  • Wrong name. The certificate covers example.com but not www.example.com, or the other way round.
  • Missing intermediate certificate. The site works in some browsers and fails in curl and on some phones. The server must send the full chain.
  • Wrong certificate served. A new virtual host or a load balancer change now hands out a default certificate.

An expired certificate usually means an automatic renewal failed quietly weeks ago. If you use certbot, these two commands show the state and test the renewal:

sudo certbot certificates
sudo certbot renew --dry-run

For a quick outside view, the SSL checker shows the expiry date and the names on the certificate.

Can traffic reach the server at all?

Test the TCP port your site listens on. That one test tells you more than ping or traceroute, because it uses the same kind of connection a browser does.

$ nc -vz -w 5 example.com 443
Connection to example.com port 443 [tcp/https] succeeded!

The exact wording differs between systems. The three results mean:

  • Succeeded: the network path works and something is listening. The fault is higher up, in TLS or HTTP.
  • Connection refused: the machine answered, and nothing is listening on that port. The web server has stopped.
  • Timed out: no answer came back. A firewall is dropping the packets, the machine is off, or DNS points at the wrong address.

What ping can and cannot tell you

ping -c 4 example.com

A reply tells you the address is reachable, how long the round trip takes and whether packets are being lost. It does not tell you the web server is running. A machine can answer ping for hours with nginx stopped.

No reply does not prove the server is down either. Many hosts and firewalls block ping on purpose. And if your site sits behind a CDN, you are pinging the CDN's edge, not your server.

What traceroute can and cannot tell you

traceroute example.com     # tracert on Windows

traceroute lists the routers between you and the server. It is useful when it stops at the same hop every time and the port test above also fails. That pattern suggests a routing or provider fault you cannot fix yourself.

Do not read too much into the rest. A row of * * * usually means a router that does not answer these probes, and traffic passes through it normally. Slow replies at a middle hop that do not carry through to the last hop are the router answering probes at low priority, not real delay.

What does the server say over HTTP?

Request the page with curl -v and read the status code. By this step DNS, TLS and the network are cleared, so the code tells you which part of your stack is failing.

$ curl -v -o /dev/null https://example.com/
* Connected to example.com (203.0.113.10) port 443
...
< HTTP/1.1 502 Bad Gateway
< Server: nginx

Lines starting with * are curl's notes on the connection. Lines starting with < are what the server sent. Find the first < HTTP line.

  • 500: the application crashed on this request. Read the application error log.
  • 502: the proxy is up but the application behind it is not answering. Check the application process.
  • 503: the server is refusing work: overload, a rate limit, or maintenance mode left on.
  • 504: the application is too slow. Look at the database and at calls to outside services.
  • 403 or 404 on the home page: a deploy, a routing rule or a file permission is wrong.
  • 200: the server thinks it is fine. Look at the body. It may be an error page, a blank page or a parked-domain page.

The guide to HTTP status codes goes through each code and who sends it.

If the site is slow and not failing

Ask curl where the time goes:

$ curl -o /dev/null -s -w "dns=%{time_namelookup} connect=%{time_connect} tls=%{time_appconnect} first_byte=%{time_starttransfer} total=%{time_total}\n" https://example.com/
dns=0.012031 connect=0.045210 tls=0.110874 first_byte=8.731455 total=8.733902

Each number is seconds since the start. Here DNS, the connection and TLS finished in 0.11 seconds. The server then took 8.6 seconds to send the first byte. The network is fine and the application is slow.

If you use a CDN, test the origin directly

curl -sv -o /dev/null --resolve example.com:443:203.0.113.10 https://example.com/

--resolve makes curl connect to the address you give, here your origin server, and skip DNS. If this works and the public URL does not, the fault sits between the CDN and your origin, or inside the CDN.

Is the server itself healthy?

Log in and check five things in this order: the process, the listening ports, the disk, the memory and the load. If you cannot log in over SSH either, the whole machine is in trouble. Open your host's control panel and look at the machine's console and status there.

1. Is the process running?

systemctl status nginx
systemctl status myapp
journalctl -u myapp -n 50 --no-pager

Look for active (running). A state of failed or a restart counter that keeps climbing means the service is crashing. The last 50 log lines usually say why.

2. Is it listening on the right port?

sudo ss -ltnp

You should see a line for ports 80 and 443, and one for your application's port. If the application port is missing, the proxy has nothing to talk to. That is your 502.

3. Is the disk full?

$ df -h /
Filesystem      Size  Used Avail Use% Mounted on
/dev/vda1        50G   50G     0 100% /

A full disk breaks things in odd ways: the database refuses writes, sessions cannot be saved, the application fails to start. Run df -i as well. A disk can run out of inodes (file slots) while still showing free space.

4. Did the server run out of memory?

$ free -m
$ sudo dmesg -T | grep -i -E "out of memory|killed process"
[Mon Oct 12 09:12:44 2026] Out of memory: Killed process 2143 (node) total-vm:1893456kB, anon-rss:1612340kB

When Linux runs out of memory, the kernel's OOM killer stops a process to save the machine. It often picks your application or your database, because they use the most. A line like the one above, timed just before the outage, is your cause.

5. Is the machine overloaded?

$ uptime
 09:20:11 up 41 days,  3:02,  1 user,  load average: 9.80, 8.95, 6.10
$ nproc
2

The three load numbers cover the last 1, 5 and 15 minutes. Compare them with the CPU count from nproc. A load of 9.8 on 2 CPUs means work is queueing. Run top to see which process is responsible.

Worked example: a 502 caused by a full disk

At 09:14 UTC the site starts returning 502. DNS, TLS and the port test pass. On the server:

$ systemctl status myapp
   Active: failed (Result: exit-code) since Mon 2026-10-12 09:13:58 UTC
$ journalctl -u myapp -n 1 --no-pager
Oct 12 09:13:58 web1 myapp[2143]: Error: ENOSPC: no space left on device, write
$ df -h /
/dev/vda1        50G   50G     0 100% /
$ sudo du -xh /var/log --max-depth=1 | sort -h | tail -n 3
118M    /var/log/journal
41G     /var/log/myapp
41G     /var/log

The application's own logs filled the disk: 41 GB of them. Free some space, then start the service:

sudo truncate -s 0 /var/log/myapp/app.log
sudo systemctl restart myapp
curl -I http://127.0.0.1:3000/

Use truncate, not rm, on a log that a running process holds open. A deleted file keeps its space until that process closes it. The lasting fix is log rotation, which belongs in the review, not in the middle of the outage.

What changed just before it broke?

Ask what changed in the hour before the outage, and undo it if you can. A change is the first suspect, because a system that ran fine yesterday rarely breaks on its own.

Changes to look for, roughly from most to least obvious:

  • A code deploy.
  • A web server or proxy config edit.
  • A changed or missing environment variable or secret.
  • A database migration.
  • A DNS or firewall change.
  • An automatic operating system or package update.
  • A scheduled job that started at that time.
  • Something that expired: an API key, a certificate, a trial, or the card on your hosting account.
# What was deployed, and when?
git log -5 --date=iso --pretty="%h %ad %s"

# Is the nginx config valid?
sudo nginx -t

# Debian and Ubuntu: recent package changes
grep -E " upgrade | install " /var/log/dpkg.log | tail

Roll back first, debug later. If a deploy went out ten minutes before the errors began, put the previous version back. You can work out why it failed once visitors can use the site again.

The one exception is a database migration. If the new version changed the schema, the old code may not work against it. Check before you roll back.

Is the database up and accepting connections?

Test the database separately when static files load but dynamic pages fail, or when the application log is full of connection errors. The web server can be perfectly healthy while every page that needs data returns 500 or 504.

$ pg_isready -h 127.0.0.1 -p 5432
127.0.0.1:5432 - accepting connections

$ mysqladmin ping
mysqld is alive

If the database does not answer, check its process, its disk and the OOM killer exactly as you did for the application. A database server with a full disk stops accepting writes.

If it answers but the application still fails, look for these two problems.

Out of connections

The application log shows FATAL: sorry, too many clients already on PostgreSQL or Too many connections on MySQL. The database has a fixed limit, and something has used it all up: a traffic spike, a connection leak, or too many application workers.

Restarting the application releases its connections and buys you time. The real fix is a connection pool sized below the database limit.

A stuck or slow query

One long query can hold a lock that every other request waits on. On PostgreSQL, list what is running:

SELECT pid, state, now() - query_start AS running_for, left(query, 80) AS query
FROM pg_stat_activity
WHERE state <> 'idle'
ORDER BY running_for DESC
LIMIT 10;

A query that has run for many minutes on a busy table is the likely blocker. Stop it with SELECT pg_terminate_backend(<pid>);. On MySQL, SHOW FULL PROCESSLIST; gives the same view.

Is the fault with a third party, your CDN or your host?

If your own server, application and database all look healthy, look outwards. Your site depends on services you do not run, and one of them may be the part that is down.

Third-party services

List what your pages call during a request: a payment provider, a login provider, an email API, a search service, object storage. If one of them hangs and your code has no timeout, every request waits with it. Your users see a 504 that started somewhere else.

  • Search your application log for errors that name an outside hostname.
  • Check the provider's own status page.
  • Look at the live status of well-known services to see whether a large provider is having trouble.

If a dependency is down, switch off the feature that needs it if you can. A checkout that says "card payments are paused" is better than a site that hangs on every page.

Your CDN

Run the --resolve test from the HTTP step. If the origin answers correctly and the public URL fails, the problem is at the CDN: an outage on their side, a firewall rule that blocks real visitors, or an origin setting that no longer matches your server.

Your host

Signs of a host-level fault: SSH times out, several unrelated servers fail together, or the control panel shows the machine as stopped. Check three things:

  • The host's status page, for an incident in your region.
  • Your account, for a suspension or an unpaid invoice.
  • The machine's state in the control panel. A stopped or rebooting machine may only need starting.

If the host is having an outage, you cannot fix it. Your job changes from repair to communication, which is the step after the table below.

Which symptom points to which cause?

Use this table when you already know the symptom and want the shortest route to the cause. Run the command in the last column first.

SymptomMost likely causeRun this first
Timeout (nothing comes back)A firewall dropping packets, the machine off or frozen, or DNS pointing at a wrong address.nc -vz -w 5 example.com 443
Connection refusedThe web server process has stopped, so nothing listens on the port.sudo ss -ltnp then systemctl status nginx
502 Bad GatewayThe proxy runs but the application behind it has crashed or stopped.systemctl status myapp then journalctl -u myapp -n 50
503 Service UnavailableOverload, a rate limit, maintenance mode left on, or no healthy server behind a load balancer.uptime then tail -n 50 /var/log/nginx/error.log
504 Gateway TimeoutThe application is too slow: a stuck database query or a hanging outside call.curl -o /dev/null -s -w "%{time_total}\n" http://127.0.0.1:3000/
Certificate warningAn expired certificate, a name that is not on it, or a missing intermediate.echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -dates
NXDOMAINAn expired domain, a deleted DNS zone or wrong nameservers.whois example.com | grep -i -E "expir|status"
Slow, but loadingHigh load, low memory, a slow query or a slow third party.curl -o /dev/null -s -w "%{time_starttransfer}\n" https://example.com/ then uptime

The table gives the most likely cause, not the only one. If the first command comes back clean, return to the ordered steps above and carry on from where the symptom put you.

What should you tell users while you work?

Tell them early, tell them what is affected, and tell them when you will say more. A short honest note in the first few minutes does more good than a full explanation an hour later.

Post the first update as soon as you have confirmed the outage is real. You do not need the cause yet. A first message needs four things:

  • What is affected, in the user's words: "The website and the mobile app are not loading."
  • Since when, with a time zone: "Since about 09:14 UTC."
  • What you are doing: "We are investigating."
  • When the next update comes: "Next update by 09:45 UTC."

Then keep the promise. Post at the time you said, even when the only news is "still working on it". Silence reads as "nobody is looking".

Three things to leave out while the outage is live:

  • Guesses at the cause. If you say "database problem" and it turns out to be DNS, you have to correct yourself in public.
  • Fix times you cannot stand behind. Say when the next update is, not when the fix is.
  • Blame. Naming your host or a colleague helps nobody who is waiting.

Post where users will look: a status page, your support inbox auto-reply, and a social account if you have one. Keep the status page on different infrastructure from the site. A status page on the same server goes down with it.

When service is back, say so plainly, with the duration: "Resolved at 09:52 UTC. The site was unavailable for 38 minutes. We will publish a review this week."

What should you do once the site is back?

Do two things while the details are fresh: write down what happened, and make sure you hear about the next outage before your users do.

Write down what happened

Do this the same day. Terminal history scrolls away, logs rotate and memory fades within days. Collect:

  • A timeline in UTC: when it started, when you found out and how, each thing you checked, each change you made, when it recovered.
  • The evidence: the commands you ran and their output, the log lines that showed the cause, any graphs.
  • The cause, as far as you know it, and what triggered it.
  • The fix, and the things you tried that did not help.
  • The impact: who was affected and for how long.
  • Two gaps: the time from the start to you knowing, and the time from knowing to the fix.

The guide to incident postmortems shows how to turn these notes into a short review with actions, without turning it into a hunt for someone to blame.

Hear about it first next time

If a customer told you about this outage, the gap between "it broke" and "you knew" was too long. Close it with checks that run from outside your own server:

  • An HTTP check on your main URL, every minute, so a 5xx or a timeout reaches you in minutes.
  • A check on a health endpoint that fails when the database does.
  • Certificate expiry tracking, so the expiry date is never a surprise.
  • A DNS check on your main record, so a deleted or changed record is caught.
  • A heartbeat check for each scheduled job, such as backups and log rotation, so a job that stops running does not go unseen.
  • Alerts sent to a place you look at: a phone push or a team chat channel, not only an inbox.

Also turn on auto-renew for your domain and put the renewal date in your calendar.

PULSX runs HTTP(S), DNS, TCP port, ping and heartbeat checks as often as every 60 seconds, tracks certificate expiry on every HTTPS monitor, and alerts by email, Slack, Discord, webhook or Android push. It is free for 20 monitors and you can sign up without a card.

Common questions

How do I know if a website is down for everyone or just me?+
Test it from a second network. Use an online down checker that requests the page from a server, then load the site on your phone with wifi turned off so it uses mobile data. If it loads from outside and fails only on your computer, the cause is local: a VPN, a browser extension, a hosts file entry, an office firewall or a stale DNS cache.
What should I check first when my website is down?+
Check in order from the outside in. First confirm it is down from another network. Then check that DNS returns the right address, that the TLS certificate has not expired, that the port accepts connections, and what HTTP status code comes back. Only after that log in to the server to look at the process, disk space, memory and recent deploys.
What is the difference between a timeout and "connection refused"?+
A refused connection means the machine answered and said nothing is listening on that port, so the web server process has most likely stopped. A timeout means no answer came back at all. That points to a firewall dropping packets, a machine that is off or frozen, or DNS sending you to the wrong address.
Why does my site show 502 Bad Gateway?+
A 502 means the proxy in front of your site, such as nginx or a CDN, is running but could not get a valid answer from the application behind it. The usual cause is that the application has crashed or stopped. Check the application service status and its last log lines, then check disk space and the out-of-memory killer.
Can a full disk take a website down?+
Yes. When the disk is full the database cannot write, the application cannot save sessions or logs, and services fail to start. Run df -h to see space used and df -i to see inodes. The most common culprits are log files with no rotation, old backups and unused container images.
Does ping tell me whether a website is up?+
No. A ping reply only shows that the address is reachable on the network. The web server can be stopped while the machine still answers ping. Many hosts also block ping, so no reply does not prove the site is down. Testing the TCP port with nc or requesting the page with curl is a better check.
What does NXDOMAIN mean?+
NXDOMAIN is a DNS answer that means the domain name does not exist. For a site that worked yesterday, the usual causes are an expired domain, a deleted DNS zone or record, or nameservers changed to a provider that does not hold your records. Check the expiry date with whois and the nameservers with dig.
What should I tell customers during an outage?+
Post a short update as soon as you have confirmed the problem. Say what is affected, since when, that you are investigating, and when the next update will come. Do not guess at the cause or promise a fix time. Update at the time you promised, and post a clear resolved message with the duration when service is back.

More guides

Free DNS, SSL and cron tools

Know the minute your site goes down.

PULSX checks your site, API, cron jobs and SSL certificate every 60 seconds. Free for 20 monitors, with no card.