Many 500 errors are caused by overlooked database connection limits in hosting, which block new connections during peak loads or faulty scripts. I clearly show how the Cause arises, how you can recognize it, and how you can reliably deliver your site again.
Key points
To help you act more quickly, I will briefly summarize the most important aspects.
- CauseReaching MySQL connection limits often triggers 500 errors.
- RecognitionLogs with „Too many connections“ and high Threads_connected.
- remedyConnection pooling, query tuning, caching, and raising limits.
- HostingShared plans have strict limits, while VPS allows for fine-tuning.
- WordPressPlugins, cron jobs, and backups generate an excessive number of connections.
These points are interrelated and explain why individual adjustments are often not enough. That's why I rely on a mix of Optimization and clean operating setup. This helps you avoid relapses after traffic spikes. You also benefit from shorter response times. This in turn stabilizes conversion and SEO signals.
What is behind 500 errors?
A 500 Internal Server Error may seem random, but it often signals a clear backend problem. Typical causes include PHP scripts running hot, the database slowing down, or incorrect permissions. If requests reach the connection limit, the next access fails and the app throws a 500 error. I first check logs and error messages, because that's where the Notes After that, I focus on database access, because connections are more expensive than many people think.
Classify error patterns correctly
Not every failure is the same: 500 errors often originate from the application, 502 indicates proxy/gateway problems, and 503 indicates temporary overload. In practice, I see mixed scenarios – e.g., 503 from the web server because PHP-FPM has no more workers available, and 500 from PHP because the database is not accepting connections. I separate the levels: web server and PHP-FPM logs for process shortages, application logs for exceptions, MySQL logs for limits and locks. This way, I avoid turning the wrong knob.
How limits work in MySQL
MySQL limits simultaneous connections via max_connections; hosting providers often set conservative values for this. In shared environments, 100–200 connections per customer are common, globally sometimes 500. When threads_connected climbs toward the limit, wait times and abortions increase. The error „SQLSTATE[08004] [1040] Too many connections“ indicates exactly that. I observe the Threads-Metrics and compare them with traffic spikes, cron runs, and crawler activity.
Set limits and timeouts correctly
In addition to max_connections, max_user_connections (per user) and wait_timeout (idle time) also play a role. Timeouts that are too long keep connections open unnecessarily, while those that are too short lead to frequent reconnections. I set timeouts so that typical requests run completely, but idle time is quickly released. thread_cache_size also reduces handshake costs because threads are reused. Important: Every additional connection consumes RAM (thread stack, buffer) – blindly increasing max_connections risks swapping and even more latency.
Common triggers in everyday life
Spikes caused by bots or campaigns can cause connections to skyrocket in seconds. Inefficient SQLs block slots longer than necessary and create backlogs. Many WordPress plugins collect queries with every page view, which add up. Parallel backups, cron jobs, or importers exacerbate the situation. I first throttle aggressive crawlers and clear out old Plugins before I delve deeper into tuning.
More accurate diagnosis in practice
I activate the slow query log with a reasonable threshold value and look at the top causes in terms of runtime and frequency. performance_schema provides wait times by type (locks, I/O), so I can see whether the database is calculating or waiting. SHOW PROCESSLIST shows blocked or sleeping connections; long „sleep“ entries indicate poor connection policy, long „query“ entries indicate expensive SQLs. For pattern comparisons, I export metrics and check whether peaks correlate with deployments, cron runs, or bot waves.
Recognizing and diagnosing
I start with the error logs and search for „Too many connections“ or „Error establishing a database connection.“ Then I check the current connection status, for example with SHOW STATUS LIKE ‚Threads_connected‘; and the set max_connections. If the counter is close to the limit, the bottleneck is obvious. In WordPress, I deactivate extensions on a trial basis and measure the query load again. This allows me to locate the driver and decide whether to use caching or refactoring prefer.
PHP-FPM and web servers working together
I keep the number of simultaneous PHP workers in line with the database connections. Too many workers cause congestion at the database, too few waste throughput. In PHP-FPM management (pm, pm.max_children, pm.max_requests), I set an upper limit that fits the DB and use queues instead of uncontrolled parallelism. On the web server side, connection and request limits help to cushion hard peaks without overloading the database. This eliminates many „random 500s“ because the load is processed in an orderly manner.
Immediate measures in the event of failures
For acute 500 errors, I rely on targeted emergency measures with low risk. I moderately increase the PHP memory limit, reduce simultaneous crawls, and pause backups. If necessary, I restart PHP-FPM to smooth out hanging processes. For finer control, I use tips from the guide to PHP-FPM Process Management. After that, I use IP rate limits and bot rules to ensure short-term Relief.
Connection management in the application
I make a strict distinction between short-lived and persistent connections. Persistent connections save handshakes, but can „stick“ slots in shared environments and reach limits more quickly. Without pooling, I therefore prefer to use short, clean connect–query–close cycles. In environments with their own proxy (e.g., pooling layer), persistent backends are worthwhile while the app communicates with the pool. It is important to prevent connection leaks: every code path must close cleanly, even in the case of exceptions and timeouts.
Permanent relief through connection pooling
Instead of opening a new DB connection for each request, pooling bundles connections and keeps them ready. This saves handshakes, reduces latency, and avoids hard limits. ProxySQL or similar layers are suitable for MySQL; for Postgres, pgbouncer is a good choice. I set pool parameters to match query duration, timeouts, and expected parallelism. If you want to familiarize yourself with this, start with this compact overview of database pooling. When set correctly, pooling reduces the Load drastically and smooths peaks.
SQL and schema optimization
I check slow queries, set missing indexes, and simplify joins. Often, a lean select that only pulls the necessary columns helps. For „hot“ tables, targeted partitioning or a sensible covering index is worthwhile. Object cache with Redis significantly reduces the read load because fewer queries are executed. This reduces the number of simultaneous connections and the risk of Timeouts falls.
Transactions, locks, and deadlocks
Long-running transactions hold locks and block other queries, resulting in growing queues and exploding connection numbers. I ensure short transactions, avoid large batch updates in live operation, and check the isolation level. I detect deadlocks in the logs or via status output; it is often sufficient to standardize the access sequence to tables or add indexes. Repetitions with backoff also reduce pressure peaks without creating new connections.
Hosting options and limits compared
Tight limits particularly affect projects with many simultaneous visitors. Switching to a more isolated environment prevents external loads from slowing down your app. On a VPS, you can control max_connections yourself and adjust the MySQL buffers to your data set. I also pay attention to NVMe SSDs and sufficient CPU cores. The following overview shows typical limits and uses that will help you with the Planning help.
| Hosting type | MySQL maximum connections per user | Suitable for |
|---|---|---|
| Shared Basic | 100 | Small sites, test instances |
| Shared Premium | 150–200 | WordPress with moderate traffic |
| VPS | Configurable | Shop, campaigns, API backends |
| Dedicated / Root | Freely selectable | High parallelism, large databases |
Scaling pattern: Scale reading, protect writing
I relieve the primary database by shifting read load to replicas. This is worthwhile if the proportion of reads is high and the application can handle slightly delayed data. However, connection limits apply separately to each instance, so I plan pooling and limits per role (primary/replica). For write spikes, I rely on queuing so that expensive jobs run asynchronously and front-end access remains fluid. This increases capacity without raising limits everywhere.
WordPress-specific steps
I start with a full backup, then check wp-config.php and deactivate unnecessary plugins. An object cache significantly reduces query load per page. Heartbeat intervals reduce editor and dashboard pressure on the database. For the server side, I look at the distribution of PHP workers; a quick glance at the PHP Worker Guide helps with bottlenecks. This is how I bring WordPress into a Balance, which rarely reaches the limits.
WordPress: Practical tuning for high parallelism
With page cache (where possible), I push anonymous requests out of PHP and significantly reduce the load on the database. For WooCommerce and other dynamic areas, I use selective caching and ensure that cart/checkout are excluded from the cache. I move WP-Cron to a system cron so that jobs can be scheduled and are not triggered by visitor access. I monitor admin-ajax and the REST API separately—they are often drivers for many small, simultaneous requests that occupy connections.
Monitoring and bot control
Without measurement points, the actual cause often remains hidden. I track connections, query duration, error rates, and CPU load at short intervals. Alarm rules warn me of peaks before users see errors. In robots.txt, I throttle crawlers, set crawl delays, and block aggressive bots. Combined with rate limits at the IP level, the Availability high, even when campaigns start.
Fallback strategies for reliability
I am planning a degradation behavior: In case of overload, the edge cache delivers „stale while revalidate“ instead of throwing a 500 error. For critical pages, there are static fallbacks that automatically kick in when backends are unavailable. A friendly, small-sized error page helps to absorb peak loads and retain users. Together with connection pooling, this results in robust behavior—the database remains accessible and the application remains usable even if individual components fail.
Quick checklist for less than 500
- Check threads_connected and logs, identify „too many connections.“.
- Limit PHP-FPM workers so that they match the DB capacity.
- Introduce pooling and adjust timeouts/sizes to the request profile.
- Evaluate slow query logs, set indexes, and streamline expensive SQLs.
- Enable page/object cache, throttle crawlers, set rate limits.
- Disconnect WP-Cron, remove unnecessary plugins, reduce heartbeat.
- Perform backups/imports outside of peak times and keep transactions brief.
- Set up monitoring with alarm thresholds, test fallback strategies.
Briefly summarized
Reached connection limits are a common, hidden cause of 500 errors. I solve this problem permanently by using pooling, streamlining queries, and choosing appropriate hosting limits. WordPress benefits noticeably from caching, fewer plugins, and cleanly configured PHP workers. Monitoring and bot rules prevent the next peaks from catching you off guard. If you implement these steps, your site will remain responsive and significantly reduces the risk of errors.


