n8n ile WordPress İçerik Yönetimi Otomasyonu
İçerik üretimi yapan bir ekibi yönetiyorsanız, WordPress’e elle içerik girmekle geçirilen zamanın ne kadar can sıkıcı olduğunu çok iyi biliyorsunuzdur. Yazardan Slack mesajı geliyor, sen o mesajı alıp WordPress’e kopyalıyorsun, görseli yüklüyorsun, kategorileri seçiyorsun, yayın zamanlamasını ayarlıyorsun. Bu döngü günde beş kez tekrarlandığında artık “otomasyon” kelimesi adeta bir kurtuluş çığlığına dönüşüyor. İşte tam bu noktada n8n devreye giriyor.
n8n, self-hosted çalıştırabildiğiniz, görsel iş akışı oluşturmanıza imkan tanıyan açık kaynaklı bir otomasyon platformu. Zapier veya Make’e alternatif olarak düşünebilirsiniz ama farkı şu: verileriniz sizin sunucunuzda kalıyor ve kaynak koduna erişebiliyorsunuz. WordPress ile entegrasyon açısından da son derece olgun bir ekosistemi var.
Ortamı Hazırlamak
Önce n8n’i Docker ile ayağa kaldıralım. Üretim ortamı için Docker Compose kullanmak en mantıklısı.
mkdir -p /opt/n8n && cd /opt/n8n
cat > docker-compose.yml << 'EOF'
version: '3.8'
services:
n8n:
image: docker.n8n.io/n8nio/n8n:latest
restart: unless-stopped
ports:
- "5678:5678"
environment:
- N8N_HOST=n8n.sirketiniz.com
- N8N_PORT=5678
- N8N_PROTOCOL=https
- WEBHOOK_URL=https://n8n.sirketiniz.com/
- GENERIC_TIMEZONE=Europe/Istanbul
- N8N_ENCRYPTION_KEY=guclu-bir-anahtar-buraya
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=postgres
- DB_POSTGRESDB_PORT=5432
- DB_POSTGRESDB_DATABASE=n8n
- DB_POSTGRESDB_USER=n8n
- DB_POSTGRESDB_PASSWORD=guclu-sifre
volumes:
- n8n_data:/home/node/.n8n
depends_on:
- postgres
postgres:
image: postgres:15
restart: unless-stopped
environment:
- POSTGRES_USER=n8n
- POSTGRES_PASSWORD=guclu-sifre
- POSTGRES_DB=n8n
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
n8n_data:
postgres_data:
EOF
docker compose up -d
SQLite yerine PostgreSQL kullanmak kritik önem taşıyor. Üretim ortamında yüzlerce iş akışı çalışmaya başladığında SQLite kilitleme sorunlarıyla karşılaşırsınız. Bunu acı tecrübeyle öğrendim.
WordPress REST API Bağlantısını Kurmak
n8n’in WordPress node’u, WordPress REST API’yi kullanıyor. WordPress tarafında Application Password oluşturmanız gerekiyor. WordPress yönetim panelinde Kullanıcılar > Profiliniz bölümüne gidin, “Application Passwords” kısmından yeni bir şifre oluşturun.
n8n arayüzünde Credentials bölümüne gidip WordPress credential’ı ekleyin:
- WordPress URL:
https://blog.sirketiniz.com - Username: WordPress kullanıcı adınız
- Application Password: Oluşturduğunuz application password
Bağlantıyı test etmek için önce basit bir HTTP isteği atalım:
# WordPress REST API'nin çalıştığını doğrulayın
curl -s https://blog.sirketiniz.com/wp-json/wp/v2/ | python3 -m json.tool | head -20
# Application Password ile kimlik doğrulamayı test edin
curl -s -u "kullanici:application-password"
https://blog.sirketiniz.com/wp-json/wp/v2/users/me
| python3 -m json.tool
API yanıt veriyorsa artık iş akışlarını oluşturmaya başlayabiliriz.
Senaryo 1: Google Sheets’ten Toplu İçerik Yayınlama
En yaygın ihtiyaçlardan biri şu: İçerik ekibi yazıları bir Google Sheets tablosunda tutuyor. Başlık, içerik, kategori, etiket, yayın tarihi her şey orada. Siz de bu tablodan otomatik olarak WordPress’e aktarmak istiyorsunuz.
iş akışı şu adımlardan oluşacak:
- Schedule Trigger: Her gün sabah 08:00’de çalışır
- Google Sheets Node: “Taslak” durumundaki satırları çeker
- IF Node: Yayın tarihinin bugün veya geçmişte olup olmadığını kontrol eder
- WordPress Node: Yazıyı yayınlar
- Google Sheets Node: Satırın durumunu “Yayınlandı” olarak günceller
- Slack Node: Ekibe bildirim gönderir
Google Sheets’teki veri yapısı şöyle olsun:
- A sütunu: Başlık
- B sütunu: İçerik (HTML formatında)
- C sütunu: Kategori ID
- D sütunu: Etiketler (virgülle ayrılmış)
- E sütunu: Yayın Tarihi (YYYY-MM-DD)
- F sütunu: Durum (Taslak/Yayınlandı)
- G sütunu: Öne Çıkan Görsel URL’i
WordPress node’undaki ayarlar için “Execute” modunda çalışan bir Function node eklemek gerekiyor. Çünkü etiketleri önce ID’ye çevirmeniz lazım:
// Function Node: Etiketleri işle
const items = $input.all();
const processedItems = [];
for (const item of items) {
const tagNames = item.json.etiketler.split(',').map(t => t.trim());
// Etiket adlarını WordPress tag ID'lerine çevireceğiz
// Bir sonraki HTTP Request node bunu halledecek
item.json.tagArray = tagNames;
item.json.publishDate = new Date(item.json.yayinTarihi).toISOString();
processedItems.push(item);
}
return processedItems;
Senaryo 2: Haber Kaynaklarından Otomatik İçerik Özeti
Bu senaryo biraz daha ileri seviye. RSS beslemelerini takip edip, önemli haberleri otomatik olarak WordPress’te taslak olarak oluşturuyoruz. Editörler sadece onaylama yapıyor.
// Function Node: RSS içeriğini WordPress formatına dönüştür
const items = $input.all();
const processedItems = [];
for (const item of items) {
const sourceUrl = item.json.link;
const sourceTitle = item.json.title;
const sourceContent = item.json.contentSnippet || item.json.content;
const pubDate = new Date(item.json.pubDate);
// İçerik uzunluğunu kontrol et - çok kısa olanları atla
if (sourceContent && sourceContent.length < 200) {
continue;
}
const wpContent = `
<div class="kaynak-ozet">
<p>${sourceContent}</p>
<p><strong>Kaynak:</strong> <a href="${sourceUrl}" target="_blank" rel="noopener">${sourceTitle}</a></p>
<p><em>Yayın Tarihi: ${pubDate.toLocaleDateString('tr-TR')}</em></p>
</div>
`.trim();
processedItems.push({
json: {
title: `[TASLAK] ${sourceTitle}`,
content: wpContent,
status: 'draft',
categories: [15], // "Haberler" kategorisi ID'si
meta: {
_kaynak_url: sourceUrl,
_otomatik_olusturuldu: 'evet'
}
}
});
}
return processedItems;
Bu iş akışında duplikasyon kontrolü kritik. Aynı haberi iki kez oluşturmamak için WordPress’te özel bir meta field kullanın ve her yazıdan önce bu URL’in daha önce işlenip işlenmediğini sorgulayın:
# WordPress'te kaynak URL'e göre sorgulama
curl -s -u "kullanici:password"
"https://blog.sirketiniz.com/wp-json/wp/v2/posts?meta_key=_kaynak_url&meta_value=https://kaynak.com/haber-url&status=any"
| python3 -c "import sys,json; data=json.load(sys.stdin); print(len(data))"
Senaryo 3: Görsel İşleme ve Medya Yükleme
WordPress’e içerik girerken en sinir bozucu kısım görselleri yüklemek. n8n bu işi de otomatikleştirebilir.
// Function Node: Görsel URL'ini işle ve medya yüklemeye hazırla
const item = $input.first();
const imageUrl = item.json.featuredImageUrl;
if (!imageUrl) {
return [{ json: { ...item.json, skipImage: true } }];
}
// Dosya adını URL'den çıkar
const urlParts = imageUrl.split('/');
const filename = urlParts[urlParts.length - 1].split('?')[0];
// Dosya uzantısını kontrol et
const allowedExtensions = ['jpg', 'jpeg', 'png', 'webp', 'gif'];
const ext = filename.split('.').pop().toLowerCase();
if (!allowedExtensions.includes(ext)) {
return [{ json: { ...item.json, skipImage: true, imageError: 'Desteklenmeyen format' } }];
}
return [{
json: {
...item.json,
imageFilename: filename,
imageMimeType: ext === 'jpg' ? 'image/jpeg' : `image/${ext}`,
skipImage: false
}
}];
Görseli indirip WordPress Medya Kütüphanesi’ne yüklemek için HTTP Request node kullanıyoruz. İlk node görseli binary olarak indiriyor, ikinci node bunu multipart/form-data olarak WordPress’e gönderiyor.
WordPress medya yükleme endpoint’i için özel başlıklar gerekiyor:
- Content-Disposition:
attachment; filename="gorsel.jpg" - Content-Type:
image/jpeg
Görsel yüklendikten sonra dönen medya ID’yi yazının featured_media alanına bağlıyorsunuz.
Senaryo 4: Yorum Moderasyonu Otomasyonu
Yüzlerce yorum günlük olarak geliyorsa manuel moderasyon imkansız hale geliyor. n8n ile yorumları otomatik olarak filtreleyebilirsiniz.
// Function Node: Yorum spam analizi
const comment = $input.first().json;
let spamScore = 0;
const spamIndicators = [];
// Çok fazla link varsa şüpheli
const linkCount = (comment.content.rendered.match(/href=/g) || []).length;
if (linkCount > 3) {
spamScore += 30;
spamIndicators.push(`Çok fazla link: ${linkCount}`);
}
// Bilinen spam kelimeleri
const spamKeywords = ['casino', 'bahis', 'kredi', 'viagra', 'forex', 'kripto kazan'];
for (const keyword of spamKeywords) {
if (comment.content.rendered.toLowerCase().includes(keyword)) {
spamScore += 25;
spamIndicators.push(`Spam kelime: ${keyword}`);
}
}
// İçerik çok kısaysa ve sadece URL içeriyorsa
if (comment.content.rendered.replace(/<[^>]*>/g, '').trim().length < 20) {
spamScore += 20;
spamIndicators.push('Çok kısa içerik');
}
// İlk yorumsa (güvenilirlik skoru düşük)
if (comment.author_name === 'Anonymous' || !comment.author_email.includes('@')) {
spamScore += 15;
}
return [{
json: {
commentId: comment.id,
postId: comment.post,
authorEmail: comment.author_email,
spamScore,
spamIndicators,
action: spamScore >= 50 ? 'trash' : spamScore >= 30 ? 'hold' : 'approve'
}
}];
Bu iş akışı WordPress’in yorum webhook’unu dinliyor, yukarıdaki skoru hesaplıyor ve sonuca göre yorumu onaylıyor, moderasyon kuyruğuna alıyor ya da çöpe gönderiyor.
Senaryo 5: SEO Metadata Otomasyonu
İçerik ekibinin SEO konusunda her zaman yeterli bilgisi olmayabilir. n8n ile yayınlanan her yazı için otomatik SEO kontrolü yapabilirsiniz:
// Function Node: SEO kontrolü ve Yoast meta güncellemesi
const post = $input.first().json;
const title = post.title.rendered;
const content = post.content.rendered.replace(/<[^>]*>/g, '');
const slug = post.slug;
const seoIssues = [];
const seoMeta = {};
// Başlık uzunluğu kontrolü
if (title.length < 30) {
seoIssues.push('Başlık çok kısa (min 30 karakter)');
} else if (title.length > 60) {
seoIssues.push('Başlık çok uzun (max 60 karakter)');
}
// İçerik uzunluğu
const wordCount = content.split(/s+/).length;
if (wordCount < 300) {
seoIssues.push(`İçerik çok kısa: ${wordCount} kelime (min 300)`);
}
// Slug kontrolü
if (slug.includes('_')) {
seoIssues.push('Slug alt çizgi içeriyor, tire kullanın');
}
// Otomatik meta açıklama oluştur
const firstParagraph = content.substring(0, 160).trim();
seoMeta.metaDescription = firstParagraph + (firstParagraph.length >= 160 ? '...' : '');
// Focus keyword tahmini (başlıktan)
const stopWords = ['ve', 'ile', 'için', 'bir', 'bu', 'de', 'da', 'ki'];
const keywords = title.toLowerCase().split(' ')
.filter(w => !stopWords.includes(w) && w.length > 3);
seoMeta.focusKeyword = keywords[0] || '';
return [{
json: {
postId: post.id,
seoIssues,
seoMeta,
needsAttention: seoIssues.length > 0
}
}];
Webhook ile Gerçek Zamanlı Tetikleme
Schedule bazlı iş akışları yerine WordPress’ten doğrudan webhook göndermek çok daha verimli. WordPress’e şu kodu ekleyin:
<?php
// functions.php veya özel eklenti
function n8n_post_published_webhook($new_status, $old_status, $post) {
if ($new_status !== 'publish' || $old_status === 'publish') {
return;
}
$n8n_webhook_url = 'https://n8n.sirketiniz.com/webhook/wp-yeni-yazi';
$payload = [
'postId' => $post->ID,
'title' => get_the_title($post->ID),
'url' => get_permalink($post->ID),
'author' => get_the_author_meta('display_name', $post->post_author),
'categories' => wp_get_post_categories($post->ID, ['fields' => 'names']),
'publishedAt'=> current_time('c'),
'secretKey' => defined('N8N_SECRET') ? N8N_SECRET : ''
];
wp_remote_post($n8n_webhook_url, [
'body' => json_encode($payload),
'headers' => ['Content-Type' => 'application/json'],
'timeout' => 5,
'blocking' => false // Performans için non-blocking
]);
}
add_action('transition_post_status', 'n8n_post_published_webhook', 10, 3);
n8n tarafında bu webhook’u yakalayan iş akışında güvenlik doğrulaması yapın:
// Function Node: Webhook güvenlik doğrulaması
const body = $input.first().json;
const secretKey = 'wordpress-n8n-gizli-anahtar';
if (body.secretKey !== secretKey) {
throw new Error('Geçersiz güvenlik anahtarı. İstek reddedildi.');
}
// Güvenli, işleme devam et
return [{ json: body }];
Hata Yönetimi ve İzleme
Üretim ortamında iş akışları başarısız olabilir. n8n’in Error Trigger node’unu kullanarak merkezi hata yönetimi yapın:
// Error Workflow - Function Node
const error = $input.first().json;
const errorReport = {
workflowName: error.workflow.name,
workflowId: error.workflow.id,
nodeName: error.execution.lastNodeExecuted,
errorMessage: error.execution.error.message,
errorTime: new Date().toLocaleString('tr-TR', {timeZone: 'Europe/Istanbul'}),
executionId: error.execution.id,
reviewUrl: `https://n8n.sirketiniz.com/workflow/${error.workflow.id}/executions/${error.execution.id}`
};
// Slack'e hata bildirimi
return [{
json: {
channel: '#n8n-hatalar',
text: `🔴 *n8n İş Akışı Hatası*n` +
`*İş Akışı:* ${errorReport.workflowName}n` +
`*Node:* ${errorReport.nodeName}n` +
`*Hata:* ${errorReport.errorMessage}n` +
`*Zaman:* ${errorReport.errorTime}n` +
`*İnceleme:* ${errorReport.reviewUrl}`
}
}];
İş akışlarınızı düzenli olarak izlemek için basit bir kontrol scripti:
#!/bin/bash
# n8n workflow sağlık kontrolü
N8N_URL="https://n8n.sirketiniz.com"
N8N_API_KEY="api-anahtariniz"
# Son 1 saatteki başarısız execution'ları say
FAILED=$(curl -s
-H "X-N8N-API-KEY: $N8N_API_KEY"
"$N8N_URL/api/v1/executions?status=error&limit=50"
| python3 -c "
import sys, json
data = json.load(sys.stdin)
now = __import__('datetime').datetime.utcnow()
recent = [e for e in data.get('data', [])
if (now - __import__('datetime').datetime.fromisoformat(
e['startedAt'].replace('Z',''))).seconds < 3600]
print(len(recent))
")
if [ "$FAILED" -gt 5 ]; then
echo "UYARI: Son 1 saatte $FAILED başarısız iş akışı tespit edildi!"
# Alarm gönder
fi
echo "Son 1 saatte başarısız iş akışı sayısı: $FAILED"
Performans Optimizasyonu
Yüksek trafikli ortamlarda dikkat etmeniz gerekenler:
- Eşzamanlı execution limiti: n8n’de aynı anda çok fazla iş akışı çalışmasın.
EXECUTIONS_PROCESS=mainveN8N_CONCURRENCY_PRODUCTION_LIMIT=10ayarlarını yapın - Webhook kuyruğu: Ani yük artışlarında webhook’ların kaybolmaması için Redis ile kuyruk kullanın.
QUEUE_BULL_REDIS_HOSTveEXECUTIONS_MODE=queueayarlarını aktif edin - WordPress API rate limiting: Çok sayıda istek gönderirken
wp_is_block_editor()ve diğer hook’ların gereksiz yere tetiklenmemesi için WordPress tarafında da önlem alın - Binary veri bellekte tutma: Görsel işlerken büyük dosyalar için
N8N_DEFAULT_BINARY_DATA_MODE=filesystemayarı belleği rahatlatır
Sonuç
n8n ile WordPress entegrasyonu ilk bakışta karmaşık görünebilir ama doğru adımları takip ettiğinizde inanılmaz derecede güçlü bir otomasyon sistemi ortaya çıkıyor. Anlattığım beş senaryo birbirinden bağımsız çalışabileceği gibi birbirine bağlanarak daha büyük bir ekosistem de oluşturabilirsiniz.
Başlamak için en iyi nokta en çok tekrar eden ve en az değer katan görevinizi tespit etmek. İçerik ekibinizin WordPress’e kopyala-yapıştır yaptığı o Google Sheets tablosu mükemmel bir başlangıç noktası. İlk iş akışını çalışır hale getirdiğinizde zaten diğerlerini kendiniz üretmeye başlıyorsunuz.
Dikkat etmeniz gereken en önemli nokta güvenlik. WordPress application password’larını, n8n şifreleme anahtarlarını ve webhook gizli anahtarlarını her zaman güvenli bir secret manager’da tutun. Test ortamında çalışıyor diye üretim ortamında aynı credentials’ı kullanmayın. Self-hosted n8n’in en büyük avantajı verilerinizin kontrolünün sizde olması ama bu sorumluluk da sizin omuzlarınızda demek.
