PHP AJAX, Fetch, JSON and FormData Debugger for Student Projects
Find whether your PHP AJAX problem is caused by the endpoint URL, request method, body format, Content-Type, PHP input reader, invalid JSON, warnings, session, credentials, duplicate submission or JavaScript response handling.
Use this tool when: Fetch returns HTTP 500, PHP receives empty data, JSON parsing shows an unexpected token, FormData loses fields or files, the request runs twice, an AJAX endpoint loses the login session, or JavaScript shows success even when PHP failed.
Describe Your Failed JavaScript Request
Enter the clues visible in the browser Network tab, console and PHP endpoint.
Likely failing request layer
Most Likely Root Causes
Fix in This Order
Request and PHP Reader Checks
JSON, Session and Response Checks
Request Compatibility Result
Stage-by-Stage Test Matrix
| Request stage | Assessment | Exact test |
|---|
Personalized Fetch and PHP Templates
Adapt endpoint paths, database statements, validation, authentication and CSRF protection to the real project. Do not paste private tokens or credentials into frontend JavaScript.
Safe Fetch request with JSON
Safe Fetch request with FormData
PHP JSON endpoint
PHP FormData and file endpoint
PUT, PATCH or DELETE request pattern
Consistent PHP JSON response helper
Loading-state and error cleanup pattern
Security and Data-Safety Warnings
AJAX Test Cases
Viva-Ready Explanation
Copyable Debugging Report
How a JavaScript Request Reaches PHP
An AJAX feature is not one operation. JavaScript builds a request, resolves an endpoint URL, selects an HTTP method, serializes the body, sends headers and waits for a response. The browser may apply cookie, origin and CORS rules. PHP then reads the request using a mechanism that must match the transmitted format. The endpoint performs validation and database work before returning an HTTP status and response body.
A failure at any stage may look like “Fetch does not work.” The endpoint
may return a 404 HTML page. A JSON body may be read incorrectly from
$_POST. A warning may appear before otherwise valid JSON.
The database operation may succeed, but JavaScript may fail while
parsing an empty response. The browser may also resolve the Fetch
promise even though the server returned an HTTP error response.
Stage 1: Event handling
The button or form handler must run once. A normal form submission should be prevented when JavaScript is responsible for sending it.
Stage 2: Endpoint resolution
Relative URLs are resolved from the current page URL. A path that works from one directory can point somewhere else from another page.
Stage 3: Request encoding
JSON, FormData, URL-encoded forms and query strings use different body formats and require matching PHP input handling.
Stage 4: PHP input
Standard form POST fields normally populate $_POST.
Raw JSON generally needs php://input and decoding.
Stage 5: Backend result
PHP should validate input, execute the operation and return an appropriate HTTP status with one clean response document.
Stage 6: Frontend parsing
JavaScript should check the status, safely parse the body and reset loading state whether the request succeeds or fails.
Match the Request Body to the PHP Reader
| Browser request body | Typical Content-Type | PHP reading method |
|---|---|---|
| JSON string | application/json |
Read php://input, then use
json_decode().
|
| FormData |
Browser-generated multipart/form-data with a
boundary
|
Use $_POST for text fields and
$_FILES for files.
|
| URLSearchParams or encoded form | application/x-www-form-urlencoded |
Standard POST fields normally appear in $_POST.
|
| GET query parameters | No body required | Use $_GET. |
| Raw PUT, PATCH or DELETE JSON | application/json |
Read php://input and decode it. Do not assume
$_POST contains the raw body.
|
Sending JSON while reading only $_POST is one of the
most common PHP AJAX mismatches. The request can contain valid JSON
while every expected $_POST key remains absent.
Why “Unexpected Token <” Usually Means HTML
JSON must begin with a valid JSON token such as a brace, bracket, quote,
number, true, false or null.
A less-than character often means the response begins with an HTML tag.
The HTML may be:
- A PHP warning formatted with HTML.
- An Apache or hosting error page.
- A 404 page from the wrong endpoint URL.
- A login page returned after the session was lost.
- A complete website page instead of the AJAX endpoint.
Read the raw text before forcing JSON parsing:
const response = await fetch("ajax/save-record.php");
const rawText = await response.text();
console.log(rawText);
Once the raw response is known, fix the PHP or routing problem. Do not
remove response.json() merely to hide an endpoint that is
returning the wrong format.
Fetch Does Not Reject Every HTTP Error
A Fetch promise generally rejects for request or network failures, not merely because the server returned a status such as 400, 404 or 500. The response must be inspected.
const response = await fetch("ajax/save-record.php", options);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
After the HTTP status is checked, validate the application-level
response. A server can return status 200 with
{"success": false}, although using a suitable error status
is normally clearer.
const data = await response.json();
if (!data.success) {
throw new Error(data.message || "The operation failed.");
}
Do Not Manually Set FormData’s Multipart Header
A multipart request separates its fields and files using a boundary.
When a browser sends a FormData object, the browser builds
that multipart body and supplies the matching Content-Type boundary.
Avoid this browser code:
fetch("upload.php", {
method: "POST",
headers: {
"Content-Type": "multipart/form-data"
},
body: formData
});
Send the FormData object without overriding that header:
fetch("upload.php", {
method: "POST",
body: formData
});
Authorization or CSRF headers can still be added when required. The warning applies specifically to replacing the browser-generated multipart Content-Type and boundary.
Return One Clean JSON Document
A PHP endpoint should not mix JSON with warning output, HTML, debug dumps or text from included files. Even one extra character can make a response invalid for strict JSON parsing.
Common sources of corruption include:
var_dump()andprint_r().- Debugging
echostatements. - PHP warnings and notices displayed in the response.
- HTML templates included by the endpoint.
- Authentication code redirecting to a full login page.
- Whitespace or text outside the intended PHP response.
During production, log technical information on the server and return a safe JSON message with a suitable status code.
{
"success": false,
"code": "VALIDATION_FAILED",
"message": "Check the submitted fields.",
"errors": {
"title": "Enter a title."
}
}
Why an AJAX Endpoint Can Lose the Login Session
A normal page and an AJAX endpoint are separate HTTP requests. The
endpoint must start or resume the PHP session before reading
$_SESSION.
session_start();
if (empty($_SESSION["user_id"])) {
http_response_code(401);
echo json_encode([
"success" => false,
"message" => "Authentication required."
]);
exit;
}
Same-origin requests normally use relevant cookies. Cross-origin requests may require an explicit credentials mode and matching server-side CORS configuration. The cookie’s domain, path, SameSite, Secure setting, hostname and port can also affect whether the expected authenticated request reaches PHP.
Do not solve a session or CORS issue by exposing a private password, database credential or secret API key in frontend JavaScript.
Why the Page Reloads Before Fetch Completes
A button inside a form acts as a submit button unless its type says otherwise. If a submit handler starts Fetch without preventing the normal submission, the browser can navigate or reload before the asynchronous request completes.
form.addEventListener("submit", async function (event) {
event.preventDefault();
// Send Fetch request here.
});
Another option is type="button" for a button that should
never submit the form normally. Avoid using a click handler and a form
submit handler for the same operation unless their responsibilities are
deliberately separated.
Why an AJAX Request Runs Twice
Use the Network tab to determine whether the server receives one request or two. Duplicate database rows are not proof that JavaScript sent two requests; the PHP endpoint might execute the same query twice.
Frontend causes include:
- An inline
onclickplus an event listener. - A form submit listener plus a button click listener.
- The same script loaded twice.
- The same listener registered after every modal opening.
- A double-click on an enabled button.
- Automatic retry logic.
Disable the submit control while the request is pending and restore it
in a finally block. The database should also enforce
uniqueness where duplicate records are never valid.
Handle Empty Responses Safely
Status 204 means the response intentionally has no content.
Calling response.json() on an empty response can cause a
parsing failure. A PHP endpoint can also accidentally return an empty
body after updating the database.
Choose one consistent contract:
- Return status 204 and do not attempt JSON parsing.
- Return status 200 or 201 with one documented JSON response body.
Avoid randomly returning JSON from some success branches and an empty body from others.
Safe PHP AJAX Debugging Order
- Confirm that the event handler runs once.
- Inspect the exact endpoint URL and method in the Network tab.
- Read the status code and raw response text.
- Compare the body format with the Content-Type.
- Compare the request body with the PHP input reader.
- Remove warnings, HTML and debug output from the JSON response.
- Check PHP logs for the first backend error.
- Confirm the session and authorization state.
- Check
response.okand the application success field. - Reset loading and disabled controls inside
finally.
Continue Your CodeZips Debugging Workflow
Technical References
Frequently Asked Questions
Why do I get Unexpected token < in JSON?
The response probably begins with HTML from a PHP warning, login page, 404 page, fatal-error page or hosting response. Read the raw response text before parsing JSON.
Why is $_POST empty when Fetch sends JSON?
A raw application/json body is not normally converted into standard PHP form fields. Read php://input and decode the JSON body.
Why does FormData stop working after I set Content-Type?
The browser normally creates the multipart boundary. Manually setting multipart/form-data without the matching boundary can make PHP unable to parse the fields and files.
Why does Fetch enter the success path for HTTP 500?
Fetch can resolve with a Response object for HTTP error statuses. Check response.ok or the status code before treating the request as successful.
Why does the database update but response.json() fail?
PHP may return an empty body, warning, HTML, invalid JSON or extra output after the database operation. Inspect the raw response and return one consistent JSON document.
Why is the PHP session missing only in AJAX?
The endpoint may not call session_start(), the request may reach a different origin or port, or cross-origin credentials and cookies may not be configured correctly.
Why does my form send the AJAX request twice?
Check for both click and submit handlers, inline onclick code, repeated script loading, duplicate listener registration and normal form submission running alongside Fetch.
How should PHP read PUT, PATCH or DELETE JSON?
Read the raw request body through php://input and decode it. Do not assume that raw non-POST JSON automatically appears in $_POST.
Does this tool call my PHP endpoint?
No. It analyzes the selected clues and pasted text locally in the browser. It does not send a request to the endpoint.
Final Takeaway
A reliable PHP AJAX feature must align the endpoint URL, method, request body, Content-Type and PHP input reader. The endpoint should return an appropriate status and one clean JSON response. JavaScript should inspect the status, parse the body safely, handle application errors and restore loading state on every outcome.

