PHP API Security Checklist: Validation, Authentication and SQL Safety
Start protecting a PHP API with validation, authentication, safe SQL and thoughtful error handling.

A new PHP endpoint often starts with a few lines of code and grows quickly. Before the route handles real account or customer data, it needs basic boundaries: reject unwanted methods, validate each input, authenticate protected requests, use parameterized SQL, and keep internal errors out of public responses.
The checklist below is a starting point rather than a complete security audit. Threats depend on your infrastructure, authentication model and type of data. Treat security controls as layers, not a single helper you paste into a file.
1. Start with HTTPS and a clear request contract
For public APIs, serve the endpoint over HTTPS and redirect insecure requests at your server or load balancer. Decide which HTTP methods and content types are supported. A POST-only JSON endpoint can reject requests that do not follow that contract.
<?php
declare(strict_types=1);
header('Content-Type: application/json; charset=utf-8');
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
header('Allow: POST');
http_response_code(405);
echo json_encode(['error' => 'Method not allowed']);
exit;
}
$contentType = strtolower(trim(explode(';', $_SERVER['CONTENT_TYPE'] ?? '')[0]));
if ($contentType !== 'application/json') {
http_response_code(415);
echo json_encode(['error' => 'Send application/json']);
exit;
}A content-type check does not make the payload trustworthy. It only tells your API how the client claims to have encoded the body. You still need to parse and validate it.
2. Validate the parsed input, not just the raw request
Put a reasonable body-size limit at your web server, then validate the data type and required fields in PHP. Handle malformed JSON without displaying PHP warnings or stack traces.
<?php
try {
$body = json_decode(
file_get_contents('php://input'),
true,
32,
JSON_THROW_ON_ERROR
);
} catch (JsonException $e) {
http_response_code(400);
echo json_encode(['error' => 'Invalid JSON body']);
exit;
}
if (!is_array($body) || array_is_list($body)) {
http_response_code(422);
echo json_encode(['error' => 'Expected a JSON object']);
exit;
}
$name = $body['name'] ?? null;
if (!is_string($name) || trim($name) === '' || mb_strlen($name) > 80) {
http_response_code(422);
echo json_encode(['error' => 'Name must be 1–80 characters']);
exit;
}mbstring extension, mb_strlen() is unavailable. Install/enable the extension or use a length rule that matches the character encoding used by your application.3. Authenticate and authorize separately
Authentication proves who the caller is; authorization decides what that caller can do. Never assume a user may read another user’s record just because the request has a valid token. Check ownership or role permissions at the operation that accesses the data.
For a simple server-to-server endpoint, you might compare an API key against a secret supplied through an environment variable. This is a simplified learning example, not a replacement for a full identity system.
<?php
$expected = getenv('CODEMASTER_API_KEY');
$provided = $_SERVER['HTTP_X_API_KEY'] ?? '';
if (!is_string($expected) || $expected === '' ||
!is_string($provided) || !hash_equals($expected, $provided)) {
http_response_code(401);
echo json_encode(['error' => 'Unauthorized']);
exit;
}
// Still check what this authenticated caller is allowed to do.Never ship the secret in public JavaScript or commit it to GitHub. Browser apps normally need a different authentication design. Rotate exposed keys and monitor failed authentication attempts.

4. Use prepared statements for database values
Do not concatenate untrusted values into SQL queries. PHP PDO can bind values safely to placeholders. See our dedicated PDO prepared statements walkthrough for a complete example.
$stmt = $pdo->prepare('SELECT id, name FROM customers WHERE id = :id');
$stmt->bindValue(':id', $customerId, PDO::PARAM_INT);
$stmt->execute();
$customer = $stmt->fetch(PDO::FETCH_ASSOC);5. Handle errors safely and log useful context
Unexpected server errors should return a stable generic message to callers and produce an appropriate server-side log entry. Avoid returning SQL connection strings, stack traces, passwords or full request headers. Logs themselves must be access-controlled, retained sensibly and protected from sensitive data leakage.
6. Do not confuse CORS with authentication
CORS tells browsers which origins are allowed to read cross-origin responses. It is not an authentication mechanism and does not stop scripts, curl or server-side clients. Set allowed origins narrowly where browser access is required, and avoid treating Access-Control-Allow-Origin: * as a security feature.
7. Deployment checklist
- Force HTTPS and store credentials outside public source code.
- Validate method, body size, content type, data type and acceptable field ranges.
- Authenticate protected endpoints and authorize access to each resource.
- Use prepared SQL queries and a least-privilege database account.
- Rate-limit sensitive routes such as login and password-reset endpoints.
- Use CSRF protection for cookie-authenticated state-changing browser requests.
- Use logs and monitoring while keeping secrets and personal data out of error responses.
- Update PHP and dependencies, and test negative cases before deployment.
Frequently asked questions
Is htmlspecialchars() enough to secure an API?
No. It is useful for HTML output encoding in the right context. API security also needs validation, authentication, authorization, parameterized queries and safe server configuration.
Can I store an API key in a .env file?
Yes, if it remains outside the web-accessible document root and is never committed to a public repository. In managed infrastructure, a secrets manager can offer better rotation and access controls.