Fix Nginx FastCGI Timeouts & Broken Webhook Tokens on WordPress
Stop mid-request drops and failed webhooks on WordPress and custom PHP. Raise FastCGI timeouts in Nginx, validate webhook tokens correctly, and reload safely—practical steps for Australian site owners.
If your WordPress or custom PHP site in Australia is dropping long database transactions, killing webhooks mid-flight, or returning 504s from Nginx, this guide walks you through the exact server-block timeout fixes and token-validation repairs you can apply remotely—then reload the stack without cutting live users.
On many Australian small-business stacks, PHP-FPM and MySQL work finish after Nginx has already closed the upstream. The gateway terminates the connection mid-lifecycle, webhook signatures never complete, and payment or CRM callbacks fail. Fixwebnode WordPress support helps Australian owners diagnose FastCGI timeouts and broken webhook token validation directly—remote, production-safe, and without a freelance marketplace in the middle.
Why FastCGI timeouts and broken webhook tokens matter
When database transaction time exceeds the web server’s FastCGI read/send limits, Nginx returns 504 Gateway Timeout while PHP is still writing. Webhooks that depend on that response never receive a valid body, so HMAC or bearer-token checks fail on the next hop. The fix is not “make PHP faster only”—it is aligning fastcgi_read_timeout, fastcgi_send_timeout, PHP-FPM request_terminate_timeout, and webhook secret handling so the full lifecycle completes, then reloading Nginx and PHP-FPM cleanly.
Why is Nginx killing my WordPress webhooks with 504 FastCGI timeouts in Australia?
Because default FastCGI read timeouts (often 60s) are shorter than slow MySQL transactions on WordPress and custom PHP. Nginx closes the upstream mid-request; the client never gets a signed response, so webhook token validation fails downstream. Raise timeouts in the server block, match PHP-FPM limits, fix token comparison, then reload without a hard stop.
| Symptom | Quick fix | When to call Fixwebnode |
|---|---|---|
| 504 on long admin/API or webhook posts | Raise fastcgi_read_timeout / fastcgi_send_timeout in the server block | Timeouts return after reload or only on peak AU traffic |
| Webhook 401/403 “invalid signature” | Verify raw body HMAC, constant-time compare, correct secret | Multiple providers fail or secrets rotated across envs |
| PHP still running after Nginx 504 | Align PHP-FPM request_terminate_timeout and slow-log | DB locks or plugin queries need tuning |
Common issues unique to this failure mode
- Gateway FastCGI timeout mid-transaction — Nginx logs
upstream timed outwhile MySQL still holds a lock; browsers and webhook senders see 504. - Broken webhook token validation after partial responses — Body truncated or empty; HMAC over the wrong payload; header token stripped by a proxy.
- PHP-FPM still working after Nginx closed the socket — Orphaned workers, duplicate side-effects (double emails, double stock updates).
- Inconsistent timeouts across server blocks — www vs API vs webhook location blocks use different
fastcgi_*values; only some routes fail. - Reload disruption or config test skipped —
nginx -s reloadon a broken file, or full restart instead of graceful reload, drops keepalives.
Issue 1 — Nginx FastCGI timeouts killing long WordPress and PHP DB work
Symptom: error log shows upstream timed out (110: Connection timed out) while reading response header from upstream. Admin bulk edits, WooCommerce webhooks, or custom PHP batch jobs die around 60s.
Step 1 — Confirm the timeout in logs
sudo tail -n 100 /var/log/nginx/error.log | grep -i "timed out\|upstream"
sudo tail -n 50 /var/log/php*-fpm.log 2>/dev/null
# or
sudo journalctl -u php8.2-fpm -n 50 --no-pager
Note the upstream socket (e.g. unix:/run/php/php8.2-fpm.sock) and the URI that failed.
Step 2 — Inspect current FastCGI timeouts in the active vhost
sudo nginx -T 2>/dev/null | grep -E "fastcgi_read_timeout|fastcgi_send_timeout|fastcgi_connect_timeout|server_name|location"
Step 3 — Refactor timeouts natively in the server block
Edit the site config (path varies; common on Ubuntu/Debian):
sudo nano /etc/nginx/sites-available/example.com.conf
Inside the relevant server { } (and webhook-heavy location ~ \.php$ if split), set coherent values—example production-safe baseline for slow DB work:
server {
listen 443 ssl http2;
server_name example.com;
# ... ssl_certificate paths, root, index ...
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_connect_timeout 60s;
fastcgi_send_timeout 300s;
fastcgi_read_timeout 300s;
# optional: larger buffers if response headers/bodies are heavy
fastcgi_buffer_size 32k;
fastcgi_buffers 16 32k;
fastcgi_busy_buffers_size 64k;
}
# Dedicated webhook endpoint (optional tighter control)
location = /wp-json/your-plugin/v1/webhook {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root/index.php;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_read_timeout 300s;
fastcgi_send_timeout 300s;
}
}
Adjust socket/version to match your host (php8.1-fpm, php8.3-fpm, or TCP 127.0.0.1:9000).
Step 4 — Align PHP-FPM so it does not die before Nginx waits
sudo nano /etc/php/8.2/fpm/pool.d/www.conf
; allow long requests to finish (seconds); 0 = unlimited — prefer a finite ceiling
request_terminate_timeout = 300
request_slowlog_timeout = 30s
slowlog = /var/log/php8.2-fpm-slow.log
sudo nano /etc/php/8.2/fpm/php.ini
max_execution_time = 300
max_input_time = 300
Step 5 — Test config and reload the stack without user disruption
sudo nginx -t
sudo systemctl reload nginx
sudo systemctl reload php8.2-fpm
Graceful reload finishes in-flight workers; avoid restart on busy sites unless necessary.
Step 6 — Verify
curl -sI -o /dev/null -w "%{http_code} time=%{time_total}\n" https://example.com/
sudo tail -f /var/log/nginx/error.log
Re-run the slow admin or webhook action; 504s from read timeout should stop if DB finishes within the new window.
When to call Fixwebnode: if 504s persist after 300s, you likely have lock waits or missing indexes—not just gateway limits. Remote tuning of queries and pool sizing is then safer than endless timeout inflation.
Issue 2 — Broken webhook token validation (HMAC / bearer)
Symptom: provider retries; your endpoint returns 401/403; logs show “invalid signature” even when the secret matches the dashboard. Often the body Nginx/PHP saw is not the exact bytes the sender signed—or a timeout returned an empty body on a previous hop.
Step 1 — Capture raw request evidence
# temporary debug access log with request time
sudo tail -n 50 /var/log/nginx/access.log | grep webhook
# WordPress debug (enable briefly on staging first)
grep -i "webhook\|signature\|invalid" /var/www/html/wp-content/debug.log 2>/dev/null | tail -n 40
Step 2 — Validate the shared secret and header name
Confirm env/constant matches the provider (no trailing newline in .env). Typical WordPress pattern:
# wp-config.php or a small mu-plugin — example verification sketch
$secret = defined('MY_WEBHOOK_SECRET') ? MY_WEBHOOK_SECRET : '';
$sig_header = $_SERVER['HTTP_X_HUB_SIGNATURE_256'] ?? '';
$raw = file_get_contents('php://input');
$expected = 'sha256=' . hash_hmac('sha256', $raw, $secret);
if (!hash_equals($expected, $sig_header)) {
status_header(401);
exit('invalid signature');
}
Critical points: use php://input once (do not parse JSON first then re-encode); use hash_equals for constant-time compare; match the provider’s prefix (sha256= vs raw hex).
Step 3 — Stop proxies from altering the body or stripping headers
sudo nginx -T 2>/dev/null | grep -E "proxy_pass|underscores_in_headers|ignore_invalid_headers|client_max_body_size"
Ensure:
underscores_in_headers on; # if tokens arrive as headers with underscores
client_max_body_size 16m; # webhooks with large payloads
# inside location, pass original host/proto if behind another proxy
fastcgi_param HTTP_X_HUB_SIGNATURE_256 $http_x_hub_signature_256;
Reload after edits:
sudo nginx -t && sudo systemctl reload nginx
Step 4 — Prove validation with a controlled test
SECRET='your-test-secret'
BODY='{"id":1,"event":"test"}'
SIG="sha256=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print $2}')"
curl -sS -X POST "https://example.com/wp-json/your-plugin/v1/webhook" \
-H "Content-Type: application/json" \
-H "X-Hub-Signature-256: $SIG" \
-d "$BODY" -w "\nHTTP %{http_code}\n"
Expect 200 only when secret and raw body match. Wrong secret must return 401.
When to call Fixwebnode: multiple SaaS webhooks (Stripe, shipping, CRM) fail only in production, or secrets differ across staging/live and you need a controlled rotation without downtime.
Issue 3 — PHP still running after Nginx already closed the client
Symptom: user saw 504, but order emails sent twice; slow log shows the script continued past the gateway cut-off.
Step 1 — Correlate times
sudo tail -n 100 /var/log/php8.2-fpm-slow.log
sudo grep "timed out" /var/log/nginx/error.log | tail -n 20
Step 2 — Make terminate timeout match (or sit slightly under) FastCGI read timeout
# www.conf
request_terminate_timeout = 300
Keep Nginx fastcgi_read_timeout ≥ PHP terminate time so PHP can finish and return a clean status when possible; for hard runaway scripts, set terminate a few seconds below the gateway ceiling after measuring real p95 duration.
Step 3 — Reduce duplicate side-effects in app code
Webhook handlers should be idempotent: store provider event IDs, check before charging or emailing. That is application work, not only Nginx.
Step 4 — Reload PHP-FPM gracefully
sudo systemctl reload php8.2-fpm
sudo systemctl status php8.2-fpm --no-pager
Issue 4 — Database transaction time still exceeds safe thresholds
Symptom: even at 300s, some admin or API calls crawl; MySQL shows long-running queries.
Step 1 — Find blockers
sudo mysql -e "SHOW FULL PROCESSLIST;"
# or for WordPress table prefix wp_
sudo mysql -e "SELECT * FROM information_schema.innodb_trx\G"
Step 2 — Enable slow query log briefly
sudo mysql -e "SET GLOBAL slow_query_log = ON; SET GLOBAL long_query_time = 2;"
Reproduce once, then review and turn off when done:
sudo mysql -e "SET GLOBAL slow_query_log = OFF;"
Index or rewrite the worst queries; timeouts are a safety net, not a substitute for indexing. For deeper MySQL work, Fixwebnode’s Melbourne CBD database architecture and MySQL/PostgreSQL tuning covers structured remote tuning for Australian businesses.
Issue 5 — Safe reload checklist (no mid-lifecycle user disruption)
Step 1 — Always test before reload
sudo nginx -t
Only proceed on syntax is ok / test is successful.
Step 2 — Prefer reload over restart
sudo systemctl reload nginx
sudo systemctl reload php8.2-fpm
Step 3 — Confirm workers and listen sockets
sudo ss -lntp | grep -E 'nginx|php-fpm|9000'
curl -sI https://example.com/ | head -n 5
Step 4 — Watch one deploy window
sudo tail -n 0 -f /var/log/nginx/error.log /var/log/nginx/access.log
Trigger a test webhook; confirm 2xx and no new upstream timed out lines.
When DIY is enough vs when to book Fixwebnode
DIY is enough when you control the VPS, nginx -t is clean, a single vhost needs higher fastcgi_read_timeout, and a one-line HMAC fix restores webhooks on staging and production.
Book Fixwebnode when timeouts return under Australian peak load, several server blocks disagree, webhook providers still reject signatures after body-correct HMAC, or MySQL lock times dominate. We work as a direct specialist provider for WordPress support—remote digital delivery across Australia—not a bid board. See where coverage is described on all service areas. Enterprise-style Nginx hardening patterns we apply on WordPress stacks are related to the same gateway discipline documented for other regions, including enterprise Nginx support playbooks adapted carefully to your host OS.
Book a remote fix conversation
If your WordPress or custom PHP site still loses webhooks mid-lifecycle or Nginx closes FastCGI before database work finishes, talk through logs and server blocks with Fixwebnode. Start via WordPress support for Australian small businesses—we will help prioritise timeout refactors, token validation, and a disruption-free reload path for your stack.
Conclusion
Broken webhook token validation and gateway FastCGI timeouts usually share one story: the Nginx server block abandons the upstream before PHP and MySQL finish, so signatures and responses never complete. Raise and align fastcgi_read_timeout, fastcgi_send_timeout, and PHP-FPM terminate limits; verify HMAC on the raw body; test with nginx -t; reload Nginx and PHP-FPM gracefully. When DIY stops short—especially under real Australian traffic—Fixwebnode can take the remote session from diagnosis through a stable production reload.