Skip to content
prod 352bb92
Browse

HTTP Error Triage

Use this guide when staging or production returns a 4xx/5xx you do not immediately recognize. The goal is to classify the failure by status code + first log fragment + layer, then fix the right thing instead of guessing.

You will learn the first-response path for common deployed Laravel failures: 500 app crashes, 419 CSRF/session failures, 404 and 403 layer problems, and quick 429 / 422 paths. When you diagnose a new pattern, add it to this living ledger so the next operator starts with the answer.

flowchart TD
R["4xx / 5xx response"] --> S{Status code}
S -->|500| L["Tail laravel.log"]
L --> C["Classify first error fragment"]
S -->|419| X["Run curl CSRF test"]
X --> K["302 = server OK<br/>browser-only 419 = cookie collision"]
S -->|404 / 403| W["Find the layer"]
W --> A["Laravel route → .htaccess → Cloudflare WAF → symlink/perms"]
S -->|429 / 422| Q["Use quick path"]
Q --> E["Rate limiter or validation body"]

You need SSH access to the environment, the domain, and the app path. Treat server output as evidence: capture the command and the first useful log fragment in the incident note or project tracker.

Start with the log, not the browser page. The first exception fragment usually tells you whether this is schema drift, an environment mismatch, stale autoload, or a vendor-family seeder issue.

Terminal window
ssh <host> "tail -3 ~/domains/<domain>/deploy/current/storage/logs/laravel.log | \
grep -oE 'ERROR.*[A-Z][a-z]+: [^{]{1,200}'"
# Expected: a short exception fragment you can classify below

If the log is empty, check both release and shared storage:

Terminal window
ssh <host> "ls -la ~/domains/<domain>/deploy/current/storage/logs/ && \
ls -la ~/domains/<domain>/deploy/shared/storage/logs/"
# Expected: log files exist in one of the two locations, or logging is routed to stderr/Sentry
Error fragmentLikely causeWhere to fix
Table '[x]' doesn't existMigration or seeder did not runPost-installer DB check; verify php artisan migrate:status
Attempt to read property "[x]" on nullSeeder order bug or expected vendor row is missingUse the vendor-family rule reference_codecanyon_vendor_families.md and rerun prerequisite-aware seeders
DecryptExceptionAPP_KEY mismatch or encrypted column copied across environmentsUse the production copy skip list; rotate APP_KEY only if compromise is suspected
Connection refusedRedis/MySQL down or .env host wrongCheck Redis ping and mysql -h 127.0.0.1 -e 'SELECT 1'
View [x] not foundVite build missing or view cache staleBuild assets, redeploy, then run php artisan optimize:clear
class '[X]' not foundComposer autoload stale or package excluded under --no-devcomposer dump-autoload --optimize; verify production dependencies
Unable to resolve NULL parameter / [Y] not foundContainer binding or provider registration issueCheck App\Providers\ and interface bindings
Broken pipe / max execution time exceededLong first install or migration timeoutRaise .user.ini execution limits temporarily
SQLSTATE[HY000]: General error: 1366 Incorrect string valueCharset mismatch (utf8 vs utf8mb4)Verify DB charset matches config/database.php

3. Escalate only after classification fails

Section titled “3. Escalate only after classification fails”
Terminal window
ssh <host> "tail -100 ~/domains/<domain>/deploy/current/storage/logs/laravel.log"
# Expected: enough stack trace to attach to the incident note

If it is a repeatable vendor-family bug, add the pattern to Templates/Rules/reference_codecanyon_vendor_families.md.

A browser-only 419 is often a cookie collision, not a Laravel server failure. Prove that with a fresh curl session before changing app code.

Terminal window
curl -c /tmp/cookies.txt -s https://<domain>/login | \
grep -oE 'name="_token" value="[^"]+"' | sed 's/.*value="//;s/"$//' > /tmp/csrf.txt
curl -b /tmp/cookies.txt -c /tmp/cookies.txt \
-X POST https://<domain>/login \
-d "_token=$(cat /tmp/csrf.txt)&email=test@example.com&password=wrong" \
-sI | head -1
# Expected: HTTP/1.1 302. Auth failed, but the server accepted the form token.

If curl returns 302 and only browsers return 419, inspect duplicate session cookies in DevTools. Use Templates/Rules/feedback_session_domain_collision.md for the full cookie-collision recipe. If curl also returns 419, check session storage and driver permissions:

Terminal window
ssh <host> "ls -la ~/domains/<domain>/deploy/shared/storage/framework/sessions/ | tail -5"
ssh <host> "grep '^SESSION_DRIVER=' ~/domains/<domain>/deploy/shared/.env"
# Expected: recent session files, or a driver setting you can explain

For 404 and 403, the question is not just “is the route real?” It is “which layer answered?” Walk Laravel route registration, Apache .htaccess, Cloudflare WAF, and symlink/permissions in that order.

LayerSymptomCheck
Laravel routeRoute missing, or middleware aborts`php artisan route:list
Apache .htaccessExplicit forbidden route or rewrite-level 404grep -n 'RewriteRule.*\\[F' public/.htaccess
Cloudflare WAFOrigin looks open but edge returns 403Cloudflare → Security → WAF → matching rule
Symlink/permsAsset or path 403/404, especially public/storagels -la public_html and target file permissions
Terminal window
ssh <host> "cd ~/domains/<domain>/deploy/current && php artisan route:list | grep -i '<route-fragment>'"
# Expected: route is listed, or you have found the Laravel-side reason for a 404

If the route exists but still returns 404, inspect middleware such as canInstall, auth, or vendor custom abort logic.

Terminal window
ssh <host> "grep -n 'RewriteRule.*\\[F' ~/domains/<domain>/deploy/current/public/.htaccess"
curl -I https://<domain>/<route> 2>&1 | head -3
ssh <host> "ls -la ~/domains/<domain>/public_html"
# Expected: a layer you can name: Laravel, Apache, Cloudflare, or filesystem

For /install, a 403 after setup is intentional. During installer windows, unblock both .htaccess and Cloudflare WAF, finish the wizard, then re-block and verify 403 again.

429 means a limiter fired. 422 means validation returned a body. Do not debug either from the status code alone.

StatusFirst checkCommon fix
429Cloudflare rate limits, then Laravel RateLimiter::for rulesNarrow the edge rule or adjust the app throttle
422DevTools → Network → failing request → Response JSONMatch form field names, CSRF state, file upload limits, or FormRequest rules

For Laravel throttles:

Terminal window
ssh <host> "grep -rn 'RateLimiter::for' ~/domains/<domain>/deploy/current/app/Providers/ ~/domains/<domain>/deploy/current/config/ 2>/dev/null"
# Expected: the limiter names and limits that can explain the 429

When a 4xx/5xx does not fit this page and you had to diagnose it from scratch, add a row to the relevant section. The value of this guide is the accumulated mapping from fragment → cause → fix location.

Classify before fixing: 500 starts with laravel.log, 419 starts with the curl CSRF test, 404/403 starts by naming the layer, and 429/422 starts with the limiter or validation body. The faster you name the layer, the less likely you are to rotate keys, redeploy, or change code when the real fix is a route block, cookie collision, or schema baseline mismatch.