السلسلة: Elementor 2026: Theme Builder والأداء · تأمين WordPress 2026 · أداء WordPress: Core Web Vitals
WordPress REST API هي القاعدة لكل تكامل خارجي حديث مع WordPress: تطبيقات جوّال، headless WordPress، automation عبر n8n أو Zapier، sync مع CRM، dashboards مخصصة. مُدمَجة في WordPress core منذ الإصدار 4.7 (ديسمبر 2016)، تكشف كل المحتوى — posts، pages، users، media، taxonomies، settings — كـ JSON عبر endpoints REST قياسية. مع WordPress 6.8 (أبريل 2025) و6.9 المُنتظَر نهاية 2026، الـ API تحسّنت كثيرًا: Application Passwords مُدمَجة، CORS قابلة للضبط، rate limiting أصبح ممارسة مُوثَّقة.
المتطلبات
- WordPress 6.8+ مُثبَّت ومُحدَّث
- وصول admin إلى wp-admin
- أداة لإرسال طلبات HTTP (curl، Postman، Insomnia)
- PHP 8.3 LTS على الخادم (8.4 مُفضَّلة)
- المستوى: مطوّر متوسط، معرفة JSON وHTTP
- الوقت المُقدَّر: 75 دقيقة
الخطوة 1 — اكتشاف endpoints المُدمَجة
كل WordPress يكشف API عند المسار /wp-json/wp/v2/. تصفّح https://moncite.com/wp-json/ يُعطي قائمة كاملة بالـ routes المتاحة.
# Lister tous les endpoints
curl -s https://moncite.com/wp-json/ | jq '.routes | keys'
# Lister les 10 derniers posts publiés
curl -s "https://moncite.com/wp-json/wp/v2/posts?per_page=10&_fields=id,title,date" | jq
# Filtrer par catégorie
curl -s "https://moncite.com/wp-json/wp/v2/posts?categories=5&per_page=20" | jq
# Récupérer un post par ID
curl -s https://moncite.com/wp-json/wp/v2/posts/123 | jq
الـ query parameters المفتاحية: per_page (1-100)، page، orderby، order، search، _fields (تصفية الحقول)، _embed (تضمين كائنات مرتبطة).
الخطوة 2 — Application Passwords للمصادقة
الـ Application Passwords مُدمَجة في WordPress 5.6+ هي الطريق الرسمي للمصادقة من تطبيقات خارجية دون كشف كلمة مرور المستخدم.
التوليد: wp-admin → Users → ملفك الشخصي → سلّ نزولًا إلى « Application Passwords »، أدخل اسم التطبيق، انقر « Add New Application Password ». تظهر كلمة المرور مرة واحدة فقط — انسخها فورًا.
curl -X POST https://moncite.com/wp-json/wp/v2/posts \
-u "admin:xxxx xxxx xxxx xxxx xxxx xxxx" \
-H "Content-Type: application/json" \
-d '{
"title": "مقال جديد من API",
"content": "<p>المحتوى هنا</p>",
"status": "draft",
"categories": [3, 5]
}'
# En PowerShell (Windows)
$cred = "admin:xxxx xxxx xxxx xxxx"
$b64 = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($cred))
Invoke-RestMethod -Uri "https://moncite.com/wp-json/wp/v2/posts" \`
-Method POST \`
-Headers @{Authorization="Basic $b64"; "Content-Type"="application/json"} \`
-Body '{"title":"test","status":"draft"}'
الـ Application Password مرتبطة بالمستخدم الذي ولّدها. تحترم كل صلاحياته. لإلغاء الوصول، احذف الـ Application Password — التطبيق المُتأثّر يتوقّف فورًا.
الخطوة 3 — JWT Authentication بديلة
للمنتجات حيث Application Passwords غير مناسبة، الـ JWT أنسب. الإضافة JWT Authentication for WP REST API أو Simple JWT Login تُقدّمان endpoints.
# wp-config.php : ajouter la clé secrète
define('JWT_AUTH_SECRET_KEY', 'votre-cle-secrete-32-caracteres-min');
define('JWT_AUTH_CORS_ENABLE', true);
# Login : récupérer un token
curl -X POST https://moncite.com/wp-json/jwt-auth/v1/token \
-H "Content-Type: application/json" \
-d '{"username":"user@email.com","password":"motdepasse"}'
# Utiliser le token
curl https://moncite.com/wp-json/wp/v2/users/me \
-H "Authorization: Bearer eyJ0eXAi..."
JWT يبقى صالحًا حسب مدة الانتهاء (افتراضيًا 7 أيام). للأمان، خفّض إلى ساعة واحدة وأضف refresh tokens. تنبيه: لا تستخدم JWT لـ admin actions — Application Passwords أأمن.
الخطوة 4 — إنشاء endpoints مخصصة
add_action('rest_api_init', function() {
register_rest_route('itsc/v1', '/stats/dashboard', [
'methods' => 'GET',
'callback' => 'itsc_get_dashboard_stats',
'permission_callback' => function() {
return current_user_can('edit_posts');
}
]);
register_rest_route('itsc/v1', '/produits/(?P<id>\d+)/reserver', [
'methods' => 'POST',
'callback' => 'itsc_reserver_produit',
'permission_callback' => function() {
return is_user_logged_in();
},
'args' => [
'id' => [
'validate_callback' => function($p) { return is_numeric($p); }
],
'quantite' => [
'required' => true,
'type' => 'integer',
'minimum' => 1
]
]
]);
});
function itsc_get_dashboard_stats(WP_REST_Request $request) {
return [
'posts_count' => wp_count_posts()->publish,
'comments_count' => wp_count_comments()->approved,
'users_count' => count_users()['total_users']
];
}
function itsc_reserver_produit(WP_REST_Request $request) {
$produit_id = (int) $request['id'];
$quantite = (int) $request['quantite'];
$stock = (int) get_post_meta($produit_id, 'stock', true);
if ($stock < $quantite) {
return new WP_Error('stock_insuffisant', 'Stock insuffisant', ['status' => 422]);
}
update_post_meta($produit_id, 'stock', $stock - $quantite);
return ['succes' => true, 'stock_restant' => $stock - $quantite];
}
ثلاث ممارسات مهمة. permission_callback إلزامية. args مع validate_callback يمنع البيانات غير الصالحة. إرجاع WP_Error مع status يُولّد كود HTTP الصحيح.
الخطوة 5 — تجاوز .htaccess hardened
كثير من المواقع المُؤمَّنة تحجب /wp-json/* في .htaccess. استخدم المسار البديل:
# Au lieu de :
curl https://moncite.com/wp-json/wp/v2/posts/123
# Utilise :
curl "https://moncite.com/index.php?rest_route=/wp/v2/posts/123"
# Avec paramètres (séparateur & au lieu de ?)
curl "https://moncite.com/index.php?rest_route=/wp/v2/posts&per_page=5"
# POST identique
curl -X POST "https://moncite.com/index.php?rest_route=/wp/v2/posts" \
-u "admin:app-password" \
-H "Content-Type: application/json" \
-d '{"title":"test","status":"draft"}'
هذا المسار يمرّ عبر index.php الذي لا يُحجب من .htaccess الـ hardened. الأداء مطابق.
الخطوة 6 — تحسين الأداء
// 1. Caching agressif des endpoints publics
add_filter('rest_post_dispatch', function($response, $server, $request) {
if ($request->get_method() === 'GET' && !is_user_logged_in()) {
$response->header('Cache-Control', 'public, max-age=300');
$response->header('Vary', 'Accept-Encoding');
}
return $response;
}, 10, 3);
// 2. Réduire payload via _fields
// /wp-json/wp/v2/posts?_fields=id,title,date,excerpt
// 3. Object cache (Redis ou Memcached)
define('WP_CACHE', true);
على موقع بـ 500 posts، استدعاء /wp/v2/posts?per_page=100 يستغرق عادةً 800ms-2s دون cache. مع object cache Redis + headers HTTP cache، ينخفض إلى 50-100ms. لـ APIs عامة عالية الحركة، أضف CDN (Cloudflare) أمام endpoints.
الخطوة 7 — Rate limiting وأمان
// 1. Rate limiting basique via filter
add_filter('rest_pre_dispatch', function($result, $server, $request) {
if (is_wp_error($result)) return $result;
$ip = $_SERVER['REMOTE_ADDR'] ?? '';
$key = 'rest_rl_' . md5($ip);
$count = (int) get_transient($key);
if ($count >= 60) {
return new WP_Error('rate_limit', 'Trop de requêtes', ['status' => 429]);
}
set_transient($key, $count + 1, 60);
return $result;
}, 10, 3);
// 2. Désactiver users endpoint pour non-authentifiés
add_filter('rest_endpoints', function($endpoints) {
if (!is_user_logged_in()) {
unset($endpoints['/wp/v2/users']);
unset($endpoints['/wp/v2/users/(?P<id>[\d]+)']);
}
return $endpoints;
});
// 3. CORS strict
add_action('rest_api_init', function() {
remove_filter('rest_pre_serve_request', 'rest_send_cors_headers');
add_filter('rest_pre_serve_request', function($value) {
$origin = $_SERVER['HTTP_ORIGIN'] ?? '';
$allowed = ['https://app.moncite.com'];
if (in_array($origin, $allowed, true)) {
header("Access-Control-Allow-Origin: $origin");
header('Access-Control-Allow-Credentials: true');
}
return $value;
});
}, 15);
للمواقع الإنتاجية، رصد محاولات إساءة عبر Wordfence أو fail2ban على access logs.
الخطوة 8 — استهلاك في JavaScript حديث
// Fetch natif avec gestion d'erreur
async function fetchPosts() {
try {
const response = await fetch(
'https://moncite.com/wp-json/wp/v2/posts?per_page=10&_fields=id,title,excerpt,date',
{ headers: { 'Accept': 'application/json' } }
);
if (!response.ok) {
throw new Error('HTTP ' + response.status);
}
return await response.json();
} catch (err) {
console.error('Erreur API :', err);
return [];
}
}
// Création authentifiée (Application Password)
async function creerPost(token, data) {
const auth = 'Basic ' + btoa('admin:' + token);
const response = await fetch('https://moncite.com/wp-json/wp/v2/posts', {
method: 'POST',
headers: {
'Authorization': auth,
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});
return await response.json();
}
// Avec @wordpress/api-fetch (plugin/theme JS)
import apiFetch from '@wordpress/api-fetch';
const posts = await apiFetch({ path: '/wp/v2/posts?per_page=10' });
أخطاء شائعة
| الخطأ | السبب | الحل |
|---|---|---|
| 401 Unauthorized عند POST | Application Password خاطئة | تحقّق من Application Password وصلاحيات المستخدم |
| 403 Forbidden على endpoint عام | .htaccess hardened أو security plugin | استخدم /index.php?rest_route= |
| 404 على endpoint مخصص | permalinks غير مُحدَّثة | Settings → Permalinks → Save |
| CORS error في المتصفّح | Origin غير مُصرَّح | أضف Access-Control-Allow-Origin |
| Endpoint مخصص يُرجع HTML | error PHP في callback | تحقّق من debug.log، فعّل WP_DEBUG=true |
| Body فارغ في POST | Content-Type غير مُعَدّ | أضف -H "Content-Type: application/json" |
الأسئلة الشائعة
REST API أم GraphQL؟
REST مُدمَج في core، صفر إعداد. GraphQL عبر إضافة WPGraphQL، أنسب للتطبيقات بـ schema معقد.
هل أُعطّل REST API؟
لا تمامًا — Gutenberg وكثير من core features تعتمد عليه. عطّل فقط للمستخدمين غير المصادَقين عبر filter rest_authentication_errors.
كيف نُؤمّن endpoint مخصص؟permission_callback إلزامية. لا تستخدم __return_true أبدًا للـ endpoints التي تكتب.
هل REST API يدعم webhooks؟
ليس أصلًا. إضافات مثل WP Webhooks تُقدّم webhooks خارجية.
أداء على موقع كبير؟
محدود بـ PHP/MySQL. لـ headless بـ 10k+ requests/يوم، object cache Redis + CDN إلزاميان.