PHP & MySQL
CKEditor - Image & Audio File Upload with PHP
Configure CKEditor for file uploads - image and audio embedding, PHP server handler, drag-and-drop, and security.
What is CKEditor?
CKEditor is a popular WYSIWYG rich text editor for web applications. It allows users to compose formatted content - including embedded images, audio, and video - directly in the browser. This guide covers setting up file upload functionality with a PHP backend.
CKEditor 5 Setup with Upload Adapter
<!-- Include CKEditor 5 from CDN -->
<script src="https://cdn.ckeditor.com/ckeditor5/41.0.0/classic/ckeditor.js"></script>
<textarea id="editor"></textarea>
<script>
ClassicEditor
.create(document.querySelector('#editor'), {
// Simple upload adapter
simpleUpload: {
uploadUrl: 'upload.php',
headers: {
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content
}
},
toolbar: [
'heading', '|', 'bold', 'italic', 'link',
'|', 'imageUpload', 'mediaEmbed',
'|', 'bulletedList', 'numberedList',
'|', 'undo', 'redo'
]
})
.catch(error => console.error(error));
</script>
PHP Upload Handler
<?php
// upload.php - handles CKEditor Simple Upload Adapter
header('Content-Type: application/json');
$uploadDir = __DIR__ . '/uploads/';
$allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp',
'audio/mpeg', 'audio/ogg', 'audio/wav'];
$maxSize = 10 * 1024 * 1024; // 10MB
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || empty($_FILES['upload'])) {
http_response_code(400);
echo json_encode(['error' => ['message' => 'No file uploaded']]);
exit;
}
$file = $_FILES['upload'];
// Validate
if ($file['error'] !== UPLOAD_ERR_OK) {
echo json_encode(['error' => ['message' => 'Upload error: ' . $file['error']]]);
exit;
}
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_file($finfo, $file['tmp_name']);
finfo_close($finfo);
if (!in_array($mimeType, $allowedTypes)) {
echo json_encode(['error' => ['message' => 'File type not allowed']]);
exit;
}
if ($file['size'] > $maxSize) {
echo json_encode(['error' => ['message' => 'File too large (max 10MB)']]);
exit;
}
// Generate safe filename
$ext = pathinfo($file['name'], PATHINFO_EXTENSION);
$safeName = bin2hex(random_bytes(16)) . '.' . strtolower($ext);
if (!is_dir($uploadDir)) mkdir($uploadDir, 0755, true);
if (move_uploaded_file($file['tmp_name'], $uploadDir . $safeName)) {
$url = '/uploads/' . $safeName;
echo json_encode(['url' => $url]);
} else {
echo json_encode(['error' => ['message' => 'Failed to save file']]);
}
Audio & Media Embedding
// For audio files, create a custom CKEditor plugin
// that renders <audio> tags instead of <img>
// Or use the mediaEmbed plugin for external sources:
mediaEmbed: {
previewsInData: true,
providers: [
{ name: 'youtube', url: /youtube\.com/ },
{ name: 'soundcloud', url: /soundcloud\.com/ },
]
}
Security Best Practices
| Risk | Mitigation |
|---|---|
| Executable file upload | Validate MIME type with finfo, not just extension |
| Path traversal | Generate random filenames, never use user input in paths |
| XSS via SVG | Block SVG uploads or sanitize with a library |
| Denial of service | Enforce file size limits, rate limiting |
| Directory listing | Add .htaccess or nginx config to block directory browsing |
CKEditor 4 (Legacy)
// CKEditor 4 uses a different upload config:
CKEDITOR.replace('editor', {
filebrowserUploadUrl: 'upload.php?type=Files',
filebrowserImageUploadUrl: 'upload.php?type=Images',
});
// The PHP response format differs for CKEditor 4:
// echo "<script>window.parent.CKEDITOR.tools.callFunction(
// {$_GET['CKEditorFuncNum']}, '$url', '');</script>";
Last updated: 2026 • Browse all courses