When a website goes down, it is tempting to SSH into the server and start restarting things. That can occasionally fix the immediate problem, but it can also destroy useful evidence and leave you with no idea what actually failed.
A better approach is to work through the request in order. Check whether the domain resolves, whether the server can be reached, whether the web server responds, whether PHP or another application runtime is healthy, and whether the application can reach its database and other dependencies.
This guide provides a practical troubleshooting process you can work through when a website is unavailable, returning errors or taking too long to respond.
Before changing anything, load the site and note exactly what happens.
There is an important difference between:
403 Forbidden404 Not Found500 Internal Server Error502 Bad Gateway504 Gateway TimeoutEach points towards a different part of the stack.
From your own computer, use curl to see the HTTP response:
curl -I https://example.com
A successful response might look like:
HTTP/2 200
content-type: text/html; charset=UTF-8
curl -I requests the response headers without downloading the normal response body, which makes it useful for quickly checking a URL.
To print only the HTTP status code:
curl -sS -o /dev/null -w "%{http_code}\n" https://example.com
For more information about the connection itself:
curl -v https://example.com
Verbose output can show DNS resolution, the IP address being contacted, TLS negotiation and the HTTP response.
A useful first-pass guide is:
| Symptom | Start by checking |
|---|---|
| Domain cannot be found | DNS |
| Connection timed out | Network, firewall, server availability |
| Connection refused | Web server, listening ports, firewall |
| SSL warning | Certificate and DNS |
403 |
Web server configuration and permissions |
404 |
Document root, server block or application routes |
500 |
Application and PHP logs |
502 |
PHP-FPM or another upstream service |
504 |
Slow or unavailable upstream service |
| Very slow response | Server resources, database, application |
This narrows the investigation immediately.
If the domain itself cannot be resolved, there is little point investigating PHP or MySQL yet.
Check the domain:
dig +short example.com
You might see:
203.0.113.10
Check IPv6 as well:
dig +short AAAA example.com
Compare the result with the server's actual public IP address.
A common problem is an old DNS record pointing to a previous server after a migration.
You should also check the www hostname separately if the website uses it:
dig +short www.example.com
Do not assume that because example.com is correct, www.example.com is also correct.
A particularly confusing issue occurs when the A record is correct but the domain also has an incorrect AAAA record.
Some visitors may connect over IPv4 and see the website normally while others attempt IPv6 and fail.
If you are not actually serving the website over IPv6, remove an obsolete AAAA record rather than leaving it pointing elsewhere.
If records were changed recently, different resolvers may still have the previous value cached.
You can query specific DNS resolvers:
dig @1.1.1.1 example.com
dig @8.8.8.8 example.com
If DNS points to the correct server, move on.
An IP address resolving correctly does not prove that the server is accessible.
Try connecting to HTTPS:
curl -v https://example.com
If you receive something similar to:
Connection timed out
the request may not be reaching the web server at all.
Possible causes include:
If you can SSH into the server, check whether anything is listening on ports 80 and 443:
sudo ss -lntp | grep -E ':(80|443)\b'
You would normally expect to see Nginx, Apache or another web server listening.
For example:
LISTEN 0 511 0.0.0.0:80
LISTEN 0 511 0.0.0.0:443
If nothing is listening, investigate the web server next.
If the service is listening locally but external connections still time out, investigate firewall and network rules.
For Nginx:
sudo systemctl status nginx
You want to see:
Active: active (running)
If Nginx has stopped, do not immediately restart it. Look at why it stopped.
Check recent service logs:
sudo journalctl -u nginx -n 100 --no-pager
Then test the configuration:
sudo nginx -t
A valid configuration should produce something similar to:
syntax is ok
test is successful
If the configuration test fails, fix the reported problem before restarting or reloading Nginx.
For Apache on Ubuntu or Debian:
sudo systemctl status apache2
sudo apachectl configtest
On many RHEL-family systems:
sudo systemctl status httpd
sudo apachectl configtest
Check recent events:
sudo journalctl -u nginx --since "1 hour ago"
If the outage started shortly after someone changed a site configuration, SSL setting or PHP version, that recent change should be high on your list of suspects.
Configuration errors are especially likely after:
Do not restart a service repeatedly without finding out why it is failing.
Testing locally helps separate a web server problem from an external network or DNS problem.
For a basic HTTP site:
curl -I http://127.0.0.1
However, a server hosting multiple websites may use the hostname to decide which site to serve.
Specify it explicitly:
curl -I \
-H "Host: example.com" \
http://127.0.0.1
If the site redirects immediately to HTTPS, you can test HTTPS against localhost while still supplying the correct hostname:
curl -kI \
--resolve example.com:443:127.0.0.1 \
https://example.com
If the website works locally but not externally, the application and web server are probably functioning.
Concentrate instead on:
This test can save a lot of time.
If the server responds, the status code provides useful direction.
403 ForbiddenA 403 normally means the server received the request but will not serve the requested resource.
Check:
Inspect the path:
namei -l /var/www/example.com/public/index.php
This shows the permissions on every directory between / and the target file.
404 Not FoundA 404 does not necessarily mean the application file is missing.
Check:
server_nametry_files configurationFor Nginx, print the active configuration:
sudo nginx -T
Search for the domain:
sudo nginx -T 2>/dev/null \
| grep -n "server_name.*example.com"
500 Internal Server ErrorA 500 usually means the web server reached the application but something failed while processing the request.
For PHP applications, check:
For Laravel, for example, application errors are commonly written beneath:
storage/logs/
The exact location depends on the application.
502 Bad GatewayWith Nginx and PHP, a 502 frequently means Nginx could not communicate successfully with PHP-FPM.
Common causes include:
We cover this in detail in our 502 Bad Gateway troubleshooting guide.
504 Gateway TimeoutA 504 means the web server or proxy waited for an upstream service and did not receive a response in time.
With a PHP application, possible causes include:
Increasing the Nginx timeout may make the error disappear, but do not assume the timeout itself is the problem.
Find out what the application was waiting for first.
Logs are often the fastest way to stop guessing.
Nginx commonly writes to:
/var/log/nginx/access.log
/var/log/nginx/error.log
Show the latest errors:
sudo tail -n 100 /var/log/nginx/error.log
Follow new errors while making another request:
sudo tail -f /var/log/nginx/error.log
Nginx supports separate access and error logs, and the configured locations can be changed per server or location block.
If you are not sure where a website writes its logs:
sudo nginx -T 2>/dev/null \
| grep -E 'access_log|error_log'
Useful messages might include:
connect() to unix:/run/php/php8.4-fpm.sock failed
Permission denied
No such file or directory
upstream timed out
rewrite or internal redirection cycle
Those messages are far more useful than the generic error shown in a browser.
ServerAuth's Log Viewer provides access to common logs including Nginx, Apache, PHP, Redis and SSH through the web interface, which can save you from finding and tailing each log manually when investigating a server.
If Nginx is healthy but a PHP website is failing, check PHP-FPM.
The service name depends on the installed PHP version.
For PHP 8.4 on Ubuntu, for example:
sudo systemctl status php8.4-fpm
Check its recent logs:
sudo journalctl \
-u php8.4-fpm \
-n 100 \
--no-pager
If PHP-FPM is stopped, investigate the logs before restarting it.
A configuration error, missing extension, exhausted resource or failed upgrade may explain why it stopped.
Search the Nginx configuration:
sudo nginx -T 2>/dev/null \
| grep fastcgi_pass
You might find:
fastcgi_pass unix:/run/php/php8.4-fpm.sock;
Check whether that socket exists:
ls -la /run/php/
If Nginx expects:
/run/php/php8.3-fpm.sock
but the server now runs PHP 8.4, you have probably found the problem.
This often occurs after changing or removing a PHP version.
ServerAuth can manage different PHP versions on a per-site basis, so if the website is managed through ServerAuth it is also worth checking that the expected PHP version is assigned to the site.
Once Nginx and PHP-FPM are working, move further into the application.
Frameworks usually have their own logs.
For Laravel:
tail -n 100 \
/var/www/example.com/storage/logs/laravel.log
Your application may use a different location or send logs to another service.
Look for errors around the time the outage began.
Useful clues include:
Avoid reading a thousand old log entries from the top of a file.
Start with the latest entries:
tail -n 100 application.log
or follow it while reproducing the problem:
tail -f application.log
If a specific request triggers the problem, reproduce that request while watching the logs.
A website can be online at the web-server level while remaining unusable because its database is unavailable.
For MySQL:
sudo systemctl status mysql
On some systems the service may be named:
sudo systemctl status mariadb
Check recent MySQL logs through systemd:
sudo journalctl \
-u mysql \
-n 100 \
--no-pager
Useful application errors include:
Connection refused
Access denied for user
Too many connections
Unknown database
These point towards different issues.
The database service may be stopped or the application may be using the wrong host or port.
Check:
Find the configured limit:
mysql -NBe "SHOW VARIABLES LIKE 'max_connections';"
Check current connections:
mysql -NBe "SHOW STATUS LIKE 'Threads_connected';"
Do not simply raise max_connections without understanding why connections have accumulated.
A badly behaving application can consume a much larger amount of server memory if you allow it to open hundreds of additional database connections.
ServerAuth provides web-based management for MySQL databases and users, including remote MySQL access, which can make checking the expected database and user configuration easier.
A full disk can cause surprisingly varied failures.
Check filesystem usage:
df -h
You might find:
Filesystem Size Used Avail Use%
/dev/vda1 50G 50G 0 100%
When a filesystem reaches 100%, you may see:
500 errorsAlso check inode usage:
df -i
A server can have free disk space but still be unable to create new files if all available inodes have been consumed.
Find large top-level directories:
sudo du -xhd1 /var \
| sort -h
Then continue into the largest one.
Common causes include:
Do not begin deleting unfamiliar files simply because they are large.
Find out what created them and whether the service still requires them.
ServerAuth monitors disk usage alongside load and memory, which can help identify a disk that has been steadily filling before it reaches the point where applications begin failing.
If everything is technically running but the website is extremely slow or timing out, check the server itself.
View memory:
free -h
Check load:
uptime
Inspect active processes:
top
or, if installed:
htop
To find processes using the most memory:
ps aux \
--sort=-%mem \
| head -20
For CPU usage:
ps aux \
--sort=-%cpu \
| head -20
High resource usage may come from:
Linux may terminate processes when the system becomes critically short of memory.
Search the kernel log:
sudo journalctl -k \
| grep -Ei 'out of memory|oom|killed process'
If PHP-FPM, MySQL or another important service was killed by the OOM killer, restarting that service only treats the immediate symptom.
You still need to find out why the server exhausted its memory.
ServerAuth stores server monitoring data for metrics including memory and system load, so the historical graphs can help distinguish a sudden spike from a problem that has been growing over time.
If the website works over HTTP but fails over HTTPS, investigate TLS.
A quick check is:
curl -Iv https://example.com
You can inspect the certificate directly using OpenSSL:
openssl s_client \
-connect example.com:443 \
-servername example.com \
</dev/null 2>/dev/null \
| openssl x509 \
-noout \
-subject \
-issuer \
-dates
Check:
An expired certificate usually produces a browser security warning rather than a conventional HTTP error.
If a certificate renewal recently failed, check your Let's Encrypt or ACME client logs and confirm that ports 80 and 443 are accessible as required by the selected validation method.
If the website works locally on the server but cannot be reached externally, firewall rules are a likely candidate.
On Ubuntu with UFW:
sudo ufw status verbose
You would normally expect public web traffic to be allowed on:
80/tcp
443/tcp
Do not assume the server firewall is the only one involved.
Cloud providers may also have:
A provider-level firewall can block a request before it ever reaches Linux.
If you use ServerAuth's Firewall Manager, you can review and manage port access from the control panel, including restricting SSH access and centrally managing firewall rules.
When a previously healthy website suddenly fails, ask what happened immediately before the outage.
Check:
Check server uptime:
uptime
See the last boot:
who -b
Inspect recent package changes on Ubuntu:
grep -E ' install | upgrade | remove ' \
/var/log/dpkg.log \
| tail -50
If the problem began immediately after a deployment, compare the failed release with the previous one before making unrelated server changes.
The timing of a failure is often one of your strongest clues.
Some websites appear online while important functionality has stopped.
For example:
Check queue workers and Supervisor:
sudo supervisorctl status
For a systemd-managed worker:
sudo systemctl status example-worker
Check cron where scheduled work is involved:
crontab -l
or for another user:
sudo crontab -u deploy -l
A failed queue worker may not make the homepage unavailable, but it can still represent a serious application outage.
ServerAuth can manage both cron jobs and Supervisor daemons through its web interface, so these are worth checking alongside the site itself when investigating applications that rely on background tasks.
When the cause is not immediately obvious, work through the stack in this order:
| Step | Check | Useful command |
|---|---|---|
| 1 | DNS | dig +short example.com |
| 2 | HTTP response | curl -I https://example.com |
| 3 | Listening ports | ss -lntp |
| 4 | Web server | systemctl status nginx |
| 5 | Nginx configuration | nginx -t |
| 6 | Local website response | curl -I -H "Host: example.com" http://127.0.0.1 |
| 7 | Web server logs | tail -n 100 /var/log/nginx/error.log |
| 8 | PHP-FPM | systemctl status php8.4-fpm |
| 9 | Application logs | tail -n 100 application.log |
| 10 | Database | systemctl status mysql |
| 11 | Disk | df -h and df -i |
| 12 | Memory and load | free -h and uptime |
| 13 | Firewall | ufw status verbose |
| 14 | Recent changes | Deployments, updates and configuration changes |
You will rarely need every step.
The purpose of the sequence is to prove each layer works before moving to the next one.
If DNS is wrong, PHP-FPM is irrelevant. If Nginx works locally but the website cannot be reached from the internet, your Laravel logs are unlikely to contain the answer.
A few habits make production incidents considerably more difficult than they need to be.
Restarting Nginx, PHP and MySQL may temporarily restore the website, but it can also remove useful evidence about which service failed.
Check status and logs first.
If you change permissions, restart PHP, alter Nginx and clear every application cache at the same time, you will not know which change fixed the issue.
Make one justified change, then test again.
Do not delete the evidence you need to investigate the outage.
If disk space is critical, rotate or archive logs carefully rather than blindly deleting everything beneath /var/log.
Raising PHP workers, database connections, memory limits or timeouts may delay the next failure while making its eventual impact larger.
Work out what exhausted the original limit first.
One failing component often creates errors elsewhere.
For example, a full disk might cause MySQL to fail. PHP then reports database errors, and Nginx eventually returns a 500 or 502.
The browser shows the final symptom, not necessarily the original problem.
The best time to improve your troubleshooting setup is while the website is healthy.
Make sure you know:
Monitoring is particularly useful because it gives you information from before the outage.
If a server normally uses 40% of its memory and suddenly reaches 100% just before PHP-FPM is killed, that history is much more useful than looking at memory usage after the service has restarted.
ServerAuth brings server monitoring, logs, website management, MySQL, cron jobs and Supervisor processes into the same web interface. That gives you several of the places you would normally check during an incident without needing separate SSH commands for each one.
A website depends on several layers working together:
Domain
↓
DNS
↓
Network and firewall
↓
Nginx or Apache
↓
PHP-FPM or application server
↓
Application
↓
Database, cache and other services
Troubleshooting becomes much easier when you work through those layers in order.
Start with the exact symptom. Confirm DNS. Check whether the server responds. Test the web server locally. Read the logs. Only then move into PHP, the application, database and server resources.
Most importantly, avoid changing things simply because they might help. Every check should either confirm a part of the stack is working or move you closer to the component that is failing.
That approach gives you a much better chance of fixing the immediate outage while also understanding what needs to change to stop it happening again.
ServerAuth provides a whole host of management tools, from controlling who can access your server, to managing your website deployments. And with an ever-growing suite of tools you'll always be one step ahead!
Start for free