• Indonesian
  • English
  • Fix Apache Slot Full 503 Service Unavailable

    Kecepatan:

    Introduction

    Last week at 2 AM, my phone buzzed — monitoring notification: “HTTP 503 on client X’s main website.” What made it frustrating was that this client had just migrated from shared hosting to a dedicated VPS, and they had 12 active websites on one server. The moment I SSH’d in, I immediately knew the problem: Apache slots were full. All workers were occupied, and new requests were being flat-out rejected.

    The first time I encountered this kind of problem, I was confused because a 503 error can mean many things — database down, PHP-FPM crash, even Nginx misconfiguration. But once I checked server-status, it became crystal clear: all 256 Apache slots were filled, and to make matters worse, some slots had been waiting over 60 seconds with zero progress. That meant stuck requests were blocking slots for other requests.

    From that experience, I learned how to read Apache server-status correctly, identify which requests were stuck, and most importantly — find which website was consuming the most slots. All using simple but powerful commands. In this article I’ll show you these methods in full detail.

    Symptoms of Apache Slots Being Full

    Before troubleshooting, it’s important to know the symptoms that indicate Apache is running out of slots:

    • Error 503 Service Unavailable — the most classic symptom. The website suddenly becomes inaccessible and the server returns a 503 error.
    • Website loads extremely slowly — requests can get through but have to wait in a very long queue because slots are full.
    • Some websites down while others are still alive — this happens because one or more websites are consuming excess slots, blocking other websites on the same server.
    • Apache log full of “MaxClients reached” errors — if you check the error log, you’ll see messages like [notice] MaxClients of 256 servers not reached, some servers could not be allocated.
    • RAM completely used up — each Apache slot consumes RAM. If slots are full and RAM runs out, the server can start swapping or even trigger the OOM killer.
    • CPU load rises but requests don’t complete — this indicates requests are stuck at a certain stage (waiting for database, waiting for external API, or stuck in PHP execution).

    Root Cause: Why Do Apache Slots Run Out?

    Apache slots don’t fill up on their own. There’s always a cause, and from my experience, here are the most common ones:

    1. Websites with Slow PHP Execution

    Slow PHP websites — whether from unindexed database queries, infinite loops, or time-consuming extensions — hold slots longer than they should. A single request executing for 30 seconds means that slot is locked for 30 seconds. If there are 10 such requests simultaneously, that’s 10 slots gone.

    2. PHP-FPM Timeout Not Configured

    If Apache is configured with mod_php or PHP-FPM without proper timeouts, stuck requests will keep consuming slots until Apache itself times out. The default Apache timeout is usually 300 seconds — way too long.

    3. Traffic Spike or DDoS

    Sudden traffic spikes — whether from a marketing campaign, viral content, or a DDoS attack — can instantly drain all slots within seconds.

    4. KeepAlive Timeout Too Long

    A KeepAliveTimeout configuration that’s too long (e.g., 15 seconds) keeps slots occupied even after the client has stopped being active. The client loads the page, finishes, but the slot is held for another 15 seconds.

    5. Aggressive Bots or Crawlers

    Bots crawling a website too aggressively without delays can exhaust slots in a short time. Especially if the bot is executing JavaScript or waiting for responses from complex pages.

    Command #1: View All Apache Slot Status

    The first step when you suspect Apache slots are full is to view the status of all active slots. From here we can see which slots are in use, which are idle, and which are stuck.

    For cPanel servers, we can access status through WHM server status. Here’s the command:

    links --dump 1 http://localhost:80/whm-server-status|grep ^[0-9]|awk 'BEGIN {print "Seconds, PID, State, IP, Domain, TYPE, URLn--"} $4 !~ /[GCRK_.]/ {print $6, $2, $4, $11, $12, $13 " " $14|"sort -n"}'

    Let’s break down this command one by one so you understand what each part does:

    Breakdown of Each Part

    1. links --dump 1 http://localhost:80/whm-server-status — Fetches the WHM server status page. --dump 1 means only grab 1 frame (don’t follow links to other frames). The output is a text table from the Apache server-status page.

    2. | grep ^[0-9] — Only show lines that start with a number. On the server-status page, lines starting with numbers are active slot lines — each line represents one Apache slot currently processing a request.

    3. awk 'BEGIN {print "Seconds, PID, State, IP, Domain, TYPE, URLn--"} ...' — The awk section with table header and data processing. The BEGIN part prints column headers: Seconds (duration), PID (process ID), State (slot status), IP (client address), Domain (virtual host), TYPE (HTTP method), and URL (requested address).

    4. $4 !~ /[GCRK_.]/ — Filter to exclude slots in certain states. The character G = Graceful shutdown, C = Closing connection, R = Reading request, K = Keepalive, . = empty open slot, _ = idle slot. So this command only shows slots that are truly processing requests — stuck, busy, or waiting slots.

    5. {print $6, $2, $4, $11, $12, $13 " " $14|"sort -n"} — Prints the relevant columns: $6 (Seconds/duration), $2 (PID), $4 (State), $11 (Client IP), $12 (domain/vhost), $13 $14 (type + URL). Results are sorted by duration (sort -n) — slots waiting the longest appear first.

    Example Output

    Seconds, PID, State, IP, Domain, TYPE, URL
    --
    2 14233 . 192.168.1.10 clientA.com GET /wp-admin/admin-ajax.php
    5 14234 . 192.168.1.20 clientB.com POST /xmlrpc.php
    18 14250 . 45.76.100.5 clientC.com GET /wp-login.php
    62 14301 . 103.28.12.88 clientD.com GET /wp-cron.php
    127 14180 . 178.62.55.200 clientA.com GET /wp-login.php
    248 14099 . 89.248.165.12 clientE.com POST /xmlrpc.php

    Interpreting the Output

    From the output above, we can see several important things:

    • First slot (2 seconds) — still normal, request just came in.
    • Sixth slot (248 seconds) — this is very suspicious. The request to /xmlrpc.php has been running for over 4 minutes. This is likely a brute force attack or XML-RPC pingback abuse. This slot has been consuming server resources for 4 minutes without any progress.
    • Notice the pattern — almost all stuck requests are hitting wp-login.php, xmlrpc.php, and wp-cron.php. This indicates your server is being targeted by a brute force attack.

    Command #2: Identify the Website Consuming the Most Slots

    After finding stuck slots, the next step is to determine which website is consuming the most slots. This is important for prioritizing action — whether to block a specific IP, disable a specific website, or just increase the number of slots.

    links --dump http://127.0.0.1/server-status | grep '^[0-9]-[0-9]' | awk '{for(i=1;i<=NF;i++) if($i ~ /^http/1.[0-1]$|^h2$/) {print $(i+1); break}}' | sed 's/:80//;s/:443//' | sort | uniq -c | sort -nr

    Let's break down this command too:

    Breakdown of Each Part

    1. links --dump http://127.0.0.1/server-status — Fetches the server-status page without frames (--dump without a number). We use 127.0.0.1 because it must be accessed from localhost only (there's usually an IP restriction on Apache).

    2. | grep '^[0-9]-[0-9]' — Filters lines starting with the pattern number-letter-number (like 1- 23, 2- 45). These are the detail slot lines containing complete information about one Apache slot, including the virtual host (domain) serving the request.

    3. awk '{for(i=1;i<=NF;i++) if($i ~ /^http/1.[0-1]$|^h2$/) {print $(i+1); break}}' — Loops through each field in the line and searches for a field matching http/1.0, http/1.1, or h2 (HTTP version). Once found, it takes the next field ($(i+1)) — which is the virtual host name or IP port. This is how we determine which domain that slot is serving.

    4. | sed 's/:80//;s/:443//' — Removes port numbers (:80 or :443) from the domain name for clean output.

    5. | sort | uniq -c | sort -nr — Sorts, counts occurrences of each domain, then sorts by highest count first.

    Example Output

        142 clientA.com
         67 clientD.com
         31 clientB.com
          8 clientC.com
          5 clientE.com
          3 (helicon.nginx)

    From this output, it's crystal clear: clientA.com is consuming 142 slots out of 256 total — meaning this single website alone is using more than 55% of all Apache slots. This website needs immediate action.

    Command #3: Check Slots in Waiting or Stuck State

    Sometimes we need to know which slots are truly stuck — slots that have been waiting extremely long with no progress. This is different from slots processing requests that genuinely take a long time (like generating large reports). Stuck slots usually indicate a problem in PHP execution, database, or external resources.

    links --dump 1 http://localhost:80/whm-server-status | grep "W" | awk '{print $2, $6, $12}' | sort -rn | head -20

    Or if you want to see slots in any state that have been running for over 30 seconds:

    links --dump 1 http://localhost:80/whm-server-status | grep '^[0-9]' | awk '$6 > 30 {print $6, $2, $4, $11, $12, $13, $14}' | sort -rn

    Interpretation

    Slots running for over 30 seconds without completing can be a warning sign. Several possibilities:

    • Slow database query — a request waiting for a query to finish. Can be checked with SHOW PROCESSLIST in MariaDB/MySQL.
    • External API timeout — a website calling an external API that isn't responding.
    • PHP infinite loop — a script trapped in an endless loop.
    • File I/O bottleneck — a website reading large files from a slow disk.

    Command #4: Count Total Slots Used vs Available

    For an overall picture, here's the command to count how many slots are in use and how many are still available:

    links --dump 1 http://localhost:80/whm-server-status | grep '^[0-9]' | wc -l

    Or the more detailed version:

    links --dump 1 http://localhost:80/whm-server-status | grep 'servers available|servers busy|Total accesses' | head -5

    If MaxClients is 256 and you see 256 active slots with only 2 or 3 in idle state (_ or .), then your server is nearing its limit. If all slots are busy (W or .. without idle), then the server has truly run out of slots.

    How to Fix Apache Slots Being Full

    ⚠️ SECURITY WARNING: Backup Configuration Before Making Changes

    Before making any Apache configuration changes, you MUST backup the existing configuration files first. A wrong configuration change could prevent Apache from starting at all.

    cp /etc/apache2/conf/httpd.conf /etc/apache2/conf/httpd.conf.bak-$(date +%Y%m%d-%H%M)
    cp -r /etc/apache2/conf.d/ /etc/apache2/conf.d.bak-$(date +%Y%m%d-%H%M)

    IMPORTANT: After backup, verify it succeeded:

    ls -lh /etc/apache2/conf/httpd.conf.bak-*
    ls -lh /etc/apache2/conf.d.bak-* 2>/dev/null

    Never make configuration changes without a backup — because if something goes wrong, you need to restore the correct configuration as quickly as possible.

    Solution 1: Optimize KeepAliveTimeout

    One of the quickest and most effective changes is reducing KeepAliveTimeout. The default is usually 5-15 seconds. For servers that frequently run out of slots, lower it to 2-3 seconds:

    KeepAliveTimeout 3
    MaxKeepAliveRequests 100

    This change ensures slots aren't locked too long by finished connections. After making the change, restart Apache:

    systemctl restart httpd

    Verify: Make sure Apache started successfully after the restart:

    systemctl status httpd
    curl -s -o /dev/null -w "%{http_code}" http://localhost/

    Solution 2: Increase MaxClients (If RAM Allows)

    If your server has sufficient RAM, you can increase the number of MaxClients. First calculate how many slots can be added:

    free -m | head -2
    ps -C apache2 -o pid,rss,cmd --no-headers | awk '{sum+=$2; count++} END {print "Average RSS per slot:", sum/count/1024, "MB"; print "Total slots:", count}'

    From this output, you can calculate: if the average slot uses 30MB RAM and the server has 2GB free, you can add around 60 more slots. But don't forget to reserve RAM for the operating system and database.

    Solution 3: Block Aggressive IPs

    If Commands #1 and #2 show a specific IP consuming many slots with suspicious requests (like brute force against xmlrpc.php or wp-login.php), block it immediately:

    # First check which IP has the most requests
    links --dump 1 http://localhost:80/whm-server-status | grep '^[0-9]' | awk '{print $11}' | sort | uniq -c | sort -rn | head -10

    After identifying the IP, add it to CSF firewall or .htaccess:

    # Via .htaccess (if CSF isn't available)
    # Add at the beginning of the affected website's .htaccess file:
    # Order Deny,Allow
    # Deny from 103.28.12.88

    Solution 4: Disable xmlrpc.php (If Not Needed)

    XML-RPC is the primary entry point for brute force attacks on WordPress. If your website doesn't need remote posting or mobile apps that use XML-RPC, just disable it:

    # Add to .htaccess or virtual host config:
    # <Files xmlrpc.php>
    #   Require all denied
    # </Files>

    Solution 5: Add Rate Limiting to Apache

    Rate limiting can prevent a single IP from consuming too many slots:

    # In .htaccess or virtual host:
    # <IfModule mod_evasive20.c>
    #   DOSHashTableSize 3097
    #   DOSPageInterval 1
    #   DOSSiteInterval 1
    #   DOSPageCount 5
    #   DOSSiteCount 50
    #   DOSBlockingPeriod 10
    # </IfModule>

    Pro Tips: Preventing Apache Slots from Filling Up

    1. Monitor slots regularly — don't wait for 503 errors. Set up a cron job for alerts: links --dump 1 http://localhost:80/whm-server-status | grep '^[0-9]' | wc -l | awk '{if ($1 > 200) print "ALERT: Apache slots high - "$1"/256"}'
    2. Optimize PHP execution time — set max_execution_time in php.ini to 30 seconds. Requests longer than 30 seconds are likely already problematic.
    3. Use opcache — PHP opcache significantly reduces execution time. Make sure opcache is enabled on all websites.
    4. Set up mod_remoteip — if your server is behind Cloudflare or a load balancer, make sure Apache sees the client's real IP, not the proxy's IP.
    5. Use a Reverse Proxy (Nginx) in front of Apache — Nginx handles static connections more efficiently and can filter requests before they reach Apache.
    6. Automated alerts — set up monitoring like Zabbix or even a simple cron to alert via Telegram/Email when slots reach a certain threshold.
    7. Review logs regularly — check /var/log/apache2/error_log for recurring error patterns.

    FAQ

    What's the ideal MaxClients for a cPanel server?

    It depends on RAM and website types. For a 4GB RAM server with lightweight WordPress sites, MaxClients of 150-200 is sufficient. For an 8GB server with heavier websites, it can go up to 300-400. The key is monitoring — see how many slots are normally in use and adjust from there.

    What's the difference between 503 Service Unavailable and 502 Bad Gateway?

    Error 503 means Apache can't accept new requests because slots are full or the service is down. Error 502 means there's a problem with the upstream — for example PHP-FPM isn't responding or the proxy can't reach the backend. For 503, check Apache slots. For 502, check PHP-FPM and proxy configuration.

    How do I know if slots are full due to a DDoS attack?

    Check Apache logs for abnormal request patterns: the same IP requesting thousands of times within seconds, requests to weird paths (e.g., /wp-login.php repeatedly), or requests from the same IP range. If the pattern looks suspicious, enable rate limiting and consider using Cloudflare or ModSecurity.

    Is it safe to restart Apache in production?

    Yes, restarting Apache in production is generally safe as long as you've backed up the configuration first. A normal restart (not a graceful stop) will disconnect all active connections, but clients will automatically reconnect. What to watch out for: make sure there are no deployments or code changes in progress, as the restart could interrupt them.

    Related Issues

    Conclusion

    Apache slots being full is a problem that can strike at any time, but if you know how to read server-status correctly, you can catch it before it's too late. The two core commands you need to memorize are:

    • links --dump 1 http://localhost:80/whm-server-status|grep ^[0-9]|awk '...' — to view the status of each slot and find stuck requests
    • links --dump http://127.0.0.1/server-status | grep ... | awk ... | sort | uniq -c | sort -nr — to find which website is consuming the most slots

    With these two commands, you can quickly identify the root cause and take appropriate action — whether that's blocking aggressive IPs, optimizing KeepAliveTimeout, or increasing MaxClients. Most importantly, don't wait for 503 errors to appear in monitoring. Monitor slots regularly, set up automated alerts, and always backup configuration before making changes. Better to prevent than to have to restart Apache at 3 AM while the client is calling — trust me, I've been there and don't want to go through it again.