
Sora Fujimoto
AI Solutions Architect

AWS WAF(Web Application Firewall)は、インターネット上の最大級のウェブサイトで利用されているAmazonのエンタープライズ級ボット保護システムです。画像パズルやチェックボックスを表示する従来のCAPTCHAとは異なり、AWS WAFは目に見えないチャレンジとトークンベースの検証を使用するため、自動化ツールでの処理が特に困難です。
n8nワークフロー内でAWS WAFチャレンジを自動的に解決できたらどうでしょうか?再利用可能なソルバーAPIを構築する場合でも、CAPTCHAで保護されたウェブサイトをスクレイピングする場合でも、ログインフォームを自動化する場合でも、従来のコードを1行も書くことなく、これらすべてを実行できます。
このガイドでは、n8n(ビジュアルワークフロー自動化ツール)と**CapSolver**(AI駆動のCAPTCHA解決サービス)を組み合わせて、AWS WAFチャレンジをオンデマンドで解決する方法を学びます。これは、スタンドアロンのAPIエンドポイントとして、または大規模な自動化ワークフロー内のステップとして機能します。
構築するもの:
ソルバーAPI — 他のツールが呼び出すことができる再利用可能なエンドポイント:
直接使用ワークフロー — CapSolverを大規模な自動化のステップとして組み込む:
AWS WAF(Web Application Firewall)は、Amazon Web Servicesのボット軽減およびウェブセキュリティサービスです。一般的なウェブエクスプロイト、ボットトラフィック、自動化された悪用からウェブサイトを保護します。多くの高トラフィックウェブサイト、特にEコマースプラットフォーム、金融サービス、エンタープライズアプリケーションは、目に見えないCAPTCHAチャレンジの背後でアクセスを制限するためにAWS WAFを使用しています。
AWS WAFがreCAPTCHAやTurnstileと異なる点:
websiteURLだけです。aws-waf-token Cookieを返します。これは、後続のリクエストでCookie HTTPヘッダーを介して送信する必要があります。awsKey、awsIv、awsContext、awsChallengeJS、およびその他のパラメータを公開する場合があります。これらはオプションであり、特定の実装でのみ必要です。
CloudFrontとは異なります。 AWS WAFはウェブアプリケーションの前面にあるファイアウォール層です。CloudFrontはAmazonのCDNです。サイトはAWS WAFなしでCloudFrontを使用することも、CloudFrontなしでAWS WAFを使用することもできます。これらは独立したサービスですが、通常は一緒に使用されます。
始める前に、以下があることを確認してください。
重要: CapSolverアカウントに十分な残高があることを確認してください。AWS WAF解決タスクは、使用量に基づいてクレジットを消費します。
CapSolverはn8nで公式統合として利用できます。コミュニティノードのインストールは不要です。ワークフローを構築する際に、ノードパネルで直接見つけることができます。
公式統合であるため、CapSolverノードがアカウントで認証できるように、n8nで資格情報を作成する必要があります。
n8nインスタンスに移動し、Settings -> Credentialsに移動します。ここに設定されているすべての資格情報が表示されます。

All(デフォルト)のままにしますn8nは自動的に接続をテストします。APIキーが有効であることを確認する緑色の**"Connection tested successfully"**バナーが表示されるはずです。

重要: ワークフロー内の各CapSolverノードは、この資格情報を参照します。一度作成するだけで済みます。すべてのソルバーワークフローは同じ資格情報を共有します。
これで、AWS WAFソルバーワークフローを構築する準備が整いました!
AWS WAFチャレンジを解決する前に、ターゲットサイトがAWS WAFを使用していることを確認し、オプションのパラメータを収集する必要があります。最も簡単な方法は、CapSolverブラウザ拡張機能と手動検査を組み合わせて使用することです。
ターゲットウェブサイトに移動し、DevTools (F12)を開きます。これらの明確な兆候を探してください。
aws-waf-token Cookieを確認しますcaptcha.awswaf.comへのリクエスト、またはchallenge.jsを含むURLを探しますx-amzn-waf-*ヘッダーを返しますほとんどのAWS WAF解決ではwebsiteURLのみが必要です。ただし、一部のサイトでは、解決の精度を向上させる可能性のある追加パラメータを公開しています。
| パラメータ | どこで探すか | 説明 |
|---|---|---|
awsKey |
ページソース / JS変数 | CAPTCHAページから返されるキー値 |
awsIv |
ページソース / JS変数 | CAPTCHAページから返されるiv値 |
awsContext |
ページソース / JS変数 | CAPTCHAページから返されるコンテキスト値 |
awsChallengeJS |
ネットワークタブ | challenge.jsのURL(例:https://captcha.awswaf.com/.../challenge.js) |
awsApiJs |
ネットワークタブ | jsapi.jsのURL |
awsProblemUrl |
ネットワークタブ | 問題エンドポイントのURL |
awsApiKey |
ネットワークタブ | 問題エンドポイントのapi_key値 |
awsExistingToken |
Cookies | 更新中の場合は以前のaws-waf-token |
ヒント: ほとんどのサイトでは、
websiteURLのみが必要です。まずそのパラメータのみを使用し、トークンが受け入れられない場合にのみオプションパラメータを追加してください。
CAPTCHAパラメータの識別に関する詳細なガイドについては、CapSolver公式ドキュメントを参照してください。
このワークフローは、AWS WAFパラメータを受け入れ、解決されたCookieトークンを返すPOST APIエンドポイントを作成します。

ワークフローは4つのノードで構成されています。
$json.errorが空でないかどうか)に基づいて分岐するIFノード{"error": "..."}を返します| 設定 | 値 |
|---|---|
| HTTPメソッド | POST |
| パス | solver-aws-waf |
| レスポンス | Response Node |
これにより、https://your-n8n-instance.com/webhook/solver-aws-wafにエンドポイントが作成されます。
| パラメータ | 値 | 説明 |
|---|---|---|
| 操作 | AWS WAF |
AWS WAFに設定する必要があります |
| タイプ | AntiAwsWafTaskProxyLess |
プロキシは不要(デフォルト)。より厳格なサイトの場合は、プロキシ付きのAntiAwsWafTaskを使用します |
| ウェブサイトURL | {{ $json.body.websiteURL }} |
AWS WAFチャレンジがあるページのURL |
| awsKey | (オプション) | CAPTCHAページから返されるキー値 |
| awsIv | (オプション) | CAPTCHAページから返されるiv値 |
| awsContext | (オプション) | CAPTCHAページから返されるコンテキスト値 |
| awsChallengeJS | (オプション) | challenge.jsリンク |
| awsApiJs | (オプション) | jsapi.jsリンク |
Turnstile/reCAPTCHAとの主な違い: AWS WAFは
websiteKeyを必要としません。websiteURLのみが必須です。このノードでCapSolver資格情報も選択してください。
| 設定 | 値 |
|---|---|
| 条件 | ={{ $json.error }}が空ではない |
| Trueブランチ | エラー応答Webhookノードにルーティングします |
| Falseブランチ | 成功応答Webhookノードにルーティングします |
これにより、キャンバス上のエラーパスが明確になります。CapSolverノードはエラー時に続行する(onError: continueRegularOutput)ため、失敗はワークフローをクラッシュさせるのではなく、{"error": "..."}としてここに到達します。
成功ブランチ(CapSolverエラー?のFalse出力):
| 設定 | 値 |
|---|---|
| 応答形式 | JSON |
| 応答本文 | ={{ JSON.stringify($json.data) }} |
エラーブランチ(CapSolverエラー?のTrue出力):
| 設定 | 値 |
|---|---|
| 応答形式 | JSON |
| 応答本文 | ={{ JSON.stringify({ error: $json.error }) }} |
エラーメッセージは、失敗が発生した場所に応じて、次の2つの形式のいずれかに従います。
| 失敗点 | 形式 |
|---|---|
| タスク作成が拒否された(誤ったキー、残高不足、無効なデータなど) | {"error": "Failed to create task: Request failed with status code 400"} |
| タスクは作成されたがCAPTCHAを解決できなかった | {"error": "Solve failed: <reason>"} |
| 120秒後に解決がタイムアウトした | {"error": "Get task result timeout: unable to solve within 120000 seconds"} |
注: タスク作成の失敗(誤ったAPIキー、残高ゼロ、無効なタスクデータ)はすべて同じHTTP 400メッセージとして表示されます。n8nノードは、CapSolverのエラー本文を読み取る前にHTTP例外をキャッチします。
一般的なエラーの例:
{"error": "Failed to create task: Request failed with status code 400"}
{"error": "Solve failed: Invalid parameters"}
{"error": "Get task result timeout: unable to solve within 120000 seconds"}
WebhookエンドポイントにPOSTリクエストを送信します。
curl -X POST https://your-n8n-instance.com/webhook/solver-aws-waf \
-H "Content-Type: application/json" \
-d '{
"websiteURL": "https://example.com/login"
}'
期待される応答:
{
"taskId": "abc123...",
"solution": {
"cookie": "aws-waf-token=AQAAAA..."
},
"status": "ready"
}
以下のJSONをコピーし、Menu -> Import from JSONを介してn8nにインポートします。
{
"name": "AWS WAF Solver API",
"nodes": [
{
"parameters": {
"content": "## AWS WAFソルバーAPI\n\n**対象:** 他のツールでAWS WAFチャレンジを解決する必要があるチーム。\n\n**機能:** AWS WAFパラメータを受け入れ、チャレンジをCapSolverに送信し、解決された`aws-waf-token` Cookieを返します。\n\n**仕組み:**\n1. Webhookは`websiteURL`とオプションパラメータを含むPOSTリクエストを受け取ります。\n2. CapSolverはAWS WAFチャレンジを解決します。\n3. 成功した場合、Webhookは`aws-waf-token` Cookieを含むJSONレスポンスを返します。\n4. 失敗した場合、Webhookはエラーメッセージを返します。\n\n**設定:**\n1. **Settings → Credentials**でCapSolver APIキーを追加します。\n2. ワークフローをアクティブ化します。\n3. Webhook URLをメモします。\n\n**重要:**\n- AWS WAFは`websiteKey`を必要としません。`websiteURL`のみです。\n- 解決策は`aws-waf-token` Cookieであり、HTTP `Cookie`ヘッダーとして送信する必要があります。",
"height": 480,
"width": 460,
"color": 1
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-728,
-400
],
"id": "sticky-blog-main-1773678228095-1",
"name": "Sticky Note"
},
{
"parameters": {
"httpMethod": "POST",
"path": "solver-aws-waf",
"responseMode": "responseNode",
"options": {}
},
"type": "n8n-nodes-base.webhook",
"typeVersion": 2.1,
"position": [
-400,
0
],
"id": "aw111111-1111-1111-1111-aw1111111101",
"name": "Webhook Trigger",
"webhookId": "aw111111-aaaa-bbbb-cccc-aw1111111101",
"onError": "continueRegularOutput"
},
{
"parameters": {
"operation": "AWS WAF",
"type": "AntiAwsWafTaskProxyLess",
"websiteURL": "={{ $json.body.websiteURL }}",
"awsKey": "={{ $json.body.awsKey }}",
"awsIv": "={{ $json.body.awsIv }}",
"awsContext": "={{ $json.body.awsContext }}",
"awsChallengeJS": "={{ $json.body.awsChallengeJS }}",
"awsApiJs": "={{ $json.body.awsApiJs }}",
"optional": {}
},
"type": "n8n-nodes-capsolver.capSolver",
"typeVersion": 1,
"position": [
-96,
0
],
"id": "aw111111-1111-1111-1111-aw1111111102",
"name": "Solve AWS WAF",
"credentials": {
"capSolverApi": {
"id": "YOUR_CREDENTIAL_ID",
"name": "CapSolver account"
}
}
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "aws-if-001",
"leftValue": "={{ $json.error }}",
"operator": {
"type": "string",
"operation": "isEmpty",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
208,
0
],
"id": "aw111111-1111-1111-1111-aw1111111103",
"name": "CapSolver Error?"
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ JSON.stringify($json.data) }}",
"options": {}
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.5,
"position": [
512,
-80
],
"id": "aw111111-1111-1111-1111-aw1111111104",
"name": "Respond to Webhook (Success)"
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ JSON.stringify({ error: $json.error }) }}",
"options": {}
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.5,
"position": [
512,
128
],
"id": "aw111111-1111-1111-1111-aw1111111105",
"name": "Respond to Webhook (Error)"
}
],
"connections": {
"Webhook Trigger": {
"main": [
[
{
"node": "Solve AWS WAF",
"type": "main",
"index": 0
}
]
]
},
"Solve AWS WAF": {
"main": [
[
{
"node": "CapSolver Error?",
"type": "main",
"index": 0
}
]
]
},
"CapSolver Error?": {
"main": [
[
{
"node": "Respond to Webhook (Success)",
"type": "main",
"index": 0
}
],
[
{
"node": "Respond to Webhook (Error)",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
}
}
上記のソルバーAPIと送信例は、AWS WAFチャレンジを解決し、Cookieを送信し、結果を処理するというコアパターンを示しています。以下のワークフローは、このパターンを本番環境対応のユースケースに拡張します。それぞれにデュアルトリガー(スケジュール+Webhook)、永続的な状態追跡、構造化された出力が含まれています。
| ワークフロー | 目的 |
|---|---|
AWS WAFスクレイピング — 価格と製品の詳細 — CapSolver + スケジュール + Webhook |
6時間ごとに価格と製品名をスクレイピングし、staticDataに保存されている以前の値と比較し、変更があった場合にアラートを送信します |
AWS WAFアカウントログイン — CapSolver + スケジュール + Webhook |
AWS WAFで保護されたサイトに自分のアカウントでログインします。まず解決し、次にCookieで認証情報をPOSTします |
このワークフローは、6時間ごと(スケジュール)またはオンデマンド(Webhook)で製品ページをスクレイピングし、HTMLノードを使用して価格を抽出し、以前に保存された値と比較します。
スケジュールパス:
6時間ごと -> AWS WAFを解決 -> 製品ページを取得 -> データを抽出
-> データを比較 -> データが変更されたか? -> アラートを構築 / 変更なし
主な動作:
Cookieヘッダーとして送信されます(これはAWS WAFトークンを送信する正しい方法です。フォームフィールドとしてではありません).product-price、h1)を介して価格と製品名を抽出します$workflow.staticData.lastPriceは実行間で以前の価格を永続化しますdeal)と上昇(重要度:info)の両方を検出します{
"name": "AWS WAF Scraping — Price & Product Details — CapSolver + Schedule + Webhook",
"nodes": [
{
"parameters": {
"content": "## AWS WAFスクレイピング — 価格と製品モニター\n\n**対象:** AWS WAFで保護されたサイトの価格または製品データを監視するチーム。\n\n**機能:** AWS WAFを解決し、製品ページを取得し、CSSセレクターを介して価格と名前を抽出し、保存された値と比較し、変更があった場合にアラートを送信します。\n\n**仕組み:**\n1. スケジュール(6時間ごと)またはWebhookがフローをトリガーします\n2. CapSolverがAWS WAFチャレンジを解決します\n3. HTTPリクエストが解決されたトークンで製品ページを取得します\n4. HTMLノードが価格と製品名を抽出します\n5. コードノードが現在の価格と保存された価格を比較します → 変更があった場合にアラートを送信します\n\n**設定:**\n1. **Settings → Credentials**でCapSolver APIキーを追加します\n2. プレースホルダーURLとサイトキーを置き換えます\n3. 「データを抽出」のCSSセレクターをターゲットページに合わせて更新します\n4. 「アラートを構築」の出力を通知チャネルに接続します",
"height": 560,
"width": 460,
"color": 1
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-920,
-380
],
"id": "sticky-blog-main-1773678228094-1",
"name": "Sticky Note"
},
{
"parameters": {
"content": "### スケジュールパス\n6時間ごとに自動的に実行されます。\n結果はワークフロースタティックデータに保存され、実行間の比較に使用されます。",
"height": 480,
"width": 1900,
"color": 6
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-440,
-280
],
"id": "sticky-blog-section-1773678228094-2",
"name": "Sticky Note1"
},
{
"parameters": {
"content": "### Webhookパス\nオンデマンドトリガー — 同じロジックで、結果をJSONレスポンスとして返します。",
"height": 480,
"width": 1900,
"color": 6
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-440,
140
],
"id": "sticky-blog-section-1773678228094-3",
"name": "Sticky Note2"
},
{
"parameters": {
"rule": {
"interval": [
{
"field": "hours",
"hoursInterval": 6
}
]
}
},
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.3,
"position": [
-400,
0
],
"id": "aw333333-3333-3333-3333-aw3333333301",
"name": "Every 6 Hours"
},
{
"parameters": {
"operation": "AWS WAF",
"type": "AntiAwsWafTaskProxyLess",
"websiteURL": "https://YOUR-TARGET-SITE.com/product-page",
"optional": {}
},
"type": "n8n-nodes-capsolver.capSolver",
"typeVersion": 1,
"position": [
-96,
0
],
"id": "aw333333-3333-3333-3333-aw3333333302",
"name": "Solve AWS WAF",
"credentials": {
"capSolverApi": {
"id": "YOUR_CREDENTIAL_ID",
"name": "CapSolver account"
}
}
},
{
"parameters": {
"url": "https://YOUR-TARGET-SITE.com/product-page",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "user-agent",
"value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36"
},
{
"name": "Cookie",
"value": "={{ $json.data.solution.cookie }}"
}
]
},
"options": {
"response": {
"response": {
"fullResponse": false
}
}
}
},\n "type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.3,
"position": [
208,
0
],
"id": "aw333333-3333-3333-3333-aw3333333303",
"name": "Fetch Product Page"
},
{
"parameters": {
"operation": "extractHtmlContent",
"sourceData": "json",
"dataPropertyName": "data",
"extractionValues": {
"values": [
{
"key": "price",
"cssSelector": ".product-price, [data-price], .price",
"returnValue": "text",
"returnArray": false
},
{
"key": "productName",
"cssSelector": "h1, .product-title",
"returnValue": "text",
"returnArray": false
}
]
},
"options": {}
},
"type": "n8n-nodes-base.html",
"typeVersion": 1.2,
"position": [
512,
0
],
"id": "aw333333-3333-3333-3333-aw3333333304",
"name": "Extract Data"
},
{
"parameters": {
"jsCode": "// Get current and previous price from workflow static data\nconst staticData = $workflow.staticData;\nconst currentPrice = $input.first().json.price;\nconst previousPrice = staticData.lastPrice;\nconst productName = $input.first().json.productName || 'Product';\n\n// Parse numeric values for comparison\nconst parsePrice = (str) => {\n if (!str) return null;\n const match = str.match(/[\\d,]+\\.?\\d*/);\n return match ? parseFloat(match[0].replace(',', '')) : null;\n};\n\nconst currentNum = parsePrice(currentPrice);\nconst previousNum = parsePrice(previousPrice);\n\n// Update stored price\nstaticData.lastPrice = currentPrice;\nstaticData.lastChecked = new Date().toISOString();\n\nconst changed = previousNum !== null && currentNum !== null && currentNum !== previousNum;\nconst direction = changed ? (currentNum < previousNum ? 'dropped' : 'increased') : 'unchanged';\nconst diff = changed ? Math.abs(currentNum - previousNum).toFixed(2) : '0';\n\nreturn [{\n json: {\n productName,\n currentPrice,\n previousPrice: previousPrice || 'first check',\n changed,\n direction,\n diff: changed ? `$${diff}` : null,\n checkedAt: new Date().toISOString()\n }\n}];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
800,
0
],
"id": "aw333333-3333-3333-3333-aw3333333305",
"name": "Compare Data"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "price-if-001",
"leftValue": "={{ $json.changed }}",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
1104,
0
],
"id": "aw333333-3333-3333-3333-aw3333333306",
"name": "Data Changed?"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "alert-001",
"name": "alert",
"value": "=Price {{ $json.direction }} for {{ $json.productName }}: {{ $json.previousPrice }} → {{ $json.currentPrice }} ({{ $json.direction === 'dropped' ? '-' : '+' }}{{ $json.diff }})",
"type": "string"
},
{
"id": "alert-002",
"name": "severity",
"value": "={{ $json.direction === 'dropped' ? 'deal' : 'info' }}",
"type": "string"
},
{
"id": "alert-003",
"name": "checkedAt",
"value": "={{ $json.checkedAt }}",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1408,
-80
],
"id": "aw333333-3333-3333-3333-aw3333333307",
"name": "Build Alert"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "nc-001",
"name": "status",
"value": "no_change",
"type": "string"
},
{
"id": "nc-002",
"name": "currentPrice",
"value": "={{ $json.currentPrice }}",
"type": "string"
},
{
"id": "nc-003",
"name": "checkedAt",
"value": "={{ $json.checkedAt }}",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1408,
128
],
"id": "aw333333-3333-3333-3333-aw3333333308",
"name": "No Change"
},
{
"parameters": {
"httpMethod": "POST",
"path": "price-monitor-aws-waf",
"responseMode": "responseNode",
"options": {}
},
"type": "n8n-nodes-base.webhook",
"typeVersion": 2.1,
"position": [
-400,
420
],
"id": "aw333333-3333-3333-3333-aw3333333309",
"name": "Webhook Trigger",
"webhookId": "aw333333-aaaa-bbbb-cccc-aw3333333309",
"onError": "continueRegularOutput"
},
{
"parameters": {
"operation": "AWS WAF",
"type": "AntiAwsWafTaskProxyLess",
"websiteURL": "https://YOUR-TARGET-SITE.com/product-page",
"optional": {}
},
"type": "n8n-nodes-capsolver.capSolver",
"typeVersion": 1,
"position": [
-96,
420
],
"id": "aw333333-3333-3333-3333-aw3333333310",
"name": "Solve AWS WAF [Webhook]",
"credentials": {
"capSolverApi": {
"id": "YOUR_CREDENTIAL_ID",
"name": "CapSolver account"
}
}
},
{
"parameters": {
"url": "https://YOUR-TARGET-SITE.com/product-page",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "user-agent",
"value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36"
},
{
"name": "Cookie",
"value": "={{ $json.data.solution.cookie }}"
}
]
},
"options": {
"response": {
"response": {
"fullResponse": false
}
}
}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.3,
"position": [
208,
420
],
"id": "aw333333-3333-3333-3333-aw3333333311",
"name": "Fetch Product Page [Webhook]"
},
{
"parameters": {
"operation": "extractHtmlContent",
"sourceData": "json",
"dataPropertyName": "data",
"extractionValues": {
"values": [
{
"key": "price",
"cssSelector": ".product-price, [data-price], .price",
"returnValue": "text",
"returnArray": false
},
{
"key": "productName",
"cssSelector": "h1, .product-title",
"returnValue": "text",
"returnArray": false
}
]
},
"options": {}
},
"type": "n8n-nodes-base.html",
"typeVersion": 1.2,
"position": [
512,
420
],
"id": "aw333333-3333-3333-3333-aw3333333312",
"name": "Extract Data [Webhook]"
},
{
"parameters": {
"jsCode": "// Get current and previous price from workflow static data\nconst staticData = $workflow.staticData;\nconst currentPrice = $input.first().json.price;\nconst previousPrice = staticData.lastPrice;\nconst productName = $input.first().json.productName || 'Product';\n\n// Parse numeric values for comparison\nconst parsePrice = (str) => {\n if (!str) return null;\n const match = str.match(/[\\d,]+\\.?\\d*/);\n return match ? parseFloat(match[0].replace(',', '')) : null;\n};\n\nconst currentNum = parsePrice(currentPrice);\nconst previousNum = parsePrice(previousPrice);\n\n// Update stored price\nstaticData.lastPrice = currentPrice;\nstaticData.lastChecked = new Date().toISOString();\n\nconst changed = previousNum !== null && currentNum !== null && currentNum !== previousNum;\nconst direction = changed ? (currentNum < previousNum ? 'dropped' : 'increased') : 'unchanged';\nconst diff = changed ? Math.abs(currentNum - previousNum).toFixed(2) : '0';\n\nreturn [{\n json: {\n productName,\n currentPrice,\n previousPrice: previousPrice || 'first check',\n changed,\n direction,\n diff: changed ? `$${diff}` : null,\n checkedAt: new Date().toISOString()\n }\n}];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
800,
420
],
"id": "aw333333-3333-3333-3333-aw3333333313",
"name": "Compare Data [Webhook]"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "price-if-002",
"leftValue": "={{ $json.changed }}",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
1104,
420
],
"id": "aw333333-3333-3333-3333-aw3333333314",
"name": "Data Changed? [Webhook]"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "alert-004",
"name": "alert",
"value": "=Price {{ $json.direction }} for {{ $json.productName }}: {{ $json.previousPrice }} → {{ $json.currentPrice }} ({{ $json.direction === 'dropped' ? '-' : '+' }}{{ $json.diff }})",
"type": "string"
},
{
"id": "alert-005",
"name": "severity",
"value": "={{ $json.direction === 'dropped' ? 'deal' : 'info' }}",
"type": "string"
},
{
"id": "alert-006",
"name": "checkedAt",
"value": "={{ $json.checkedAt }}",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1408,
340
],
"id": "aw333333-3333-3333-3333-aw3333333315",
"name": "Build Alert [Webhook]"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "nc-004",
"name": "status",
"value": "no_change",
"type": "string"
},
{
"id": "nc-005",
"name": "currentPrice",
"value": "={{ $json.currentPrice }}",
"type": "string"
},
{
"id": "nc-006",
"name": "checkedAt",
"value": "={{ $json.checkedAt }}",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1408,
548
],
"id": "aw333333-3333-3333-3333-aw3333333316",
"name": "No Change [Webhook]"
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ JSON.stringify($json) }}",
"options": {}
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.5,
"position": [
1712,
420
],
"id": "aw333333-3333-3333-3333-aw3333333317",
"name": "Respond to Webhook [Webhook]"
}
],
"connections": {
"Every 6 Hours": {
"main": [
[
{
"node": "Solve AWS WAF",
"type": "main",
"index": 0
}
]
]
},
"Solve AWS WAF": {
"main": [
[
{
"node": "Fetch Product Page",
"type": "main",
"index": 0
}
]
]
},
"Fetch Product Page": {
"main": [
[
{
"node": "Extract Data",
"type": "main",
"index": 0
}
]
]
},
"Extract Data": {
"main": [
[
{
"node": "Compare Data",
"type": "main",
"index": 0
}
]
]
},
"Compare Data": {
"main": [
[
{
"node": "Data Changed?",
"type": "main",
"index": 0
}
]
]
},
"Data Changed?": {
"main": [
[
{
"node": "Build Alert",
"type": "main",
"index": 0
}
],
[
{
"node": "No Change",
"type": "main",
"index": 0
}
]
]
},
"Webhook Trigger": {
"main": [
[
{
"node": "Solve AWS WAF [Webhook]",
"type": "main",
"index": 0
}
]
]
},
"Solve AWS WAF [Webhook]": {
"main": [
[
{
"node": "Fetch Product Page [Webhook]",
"type": "main",
"index": 0
}
]
]
},
"Fetch Product Page [Webhook]": {
"main": [
[
{
"node": "Extract Data [Webhook]",
"type": "main",
"index": 0
}
]
]
},
"Extract Data [Webhook]": {
"main": [
[
{
"node": "Compare Data [Webhook]",
"type": "main",
"index": 0
}
]
]
},
"Compare Data [Webhook]": {
"main": [
[
{
"node": "Data Changed? [Webhook]",
"type": "main",
"index": 0
}
]
]
},
"Data Changed? [Webhook]": {
"main": [
[
{
"node": "Build Alert [Webhook]",
"type": "main",
"index": 0
}
],
[
{
"node": "No Change [Webhook]",
"type": "main",
"index": 0
}
]
]
},
"Build Alert [Webhook]": {
"main": [
[
{
"node": "Respond to Webhook [Webhook]",
"type": "main",
"index": 0
}
]
]
},
"No Change [Webhook]": {
"main": [
[
{
"node": "Respond to Webhook [Webhook]",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
}
}
このワークフローは、AWS WAFで保護されたログインフォームの最初から最後まで送信を自動化します。認証情報を送信する前にAWS WAFチャレンジを解決します。
このワークフローは5つのノードで構成されています。
| 設定 | 値 |
|---|---|
| HTTPメソッド | POST |
| パス | login-aws-waf |
| レスポンス | Response Node |
これにより、https://your-n8n-instance.com/webhook/login-aws-wafにエンドポイントが作成されます。
| パラメータ | 値 |
|---|---|
| 操作 | AWS WAF |
| ウェブサイトURL | https://YOUR-TARGET-SITE.com/login |
CapSolver資格情報も選択してください。
| 設定 | 値 |
|---|---|
| メソッド | POST |
| URL | https://YOUR-TARGET-SITE.com/login |
| コンテンツタイプ | form-urlencoded |
| 本文パラメータ | username=YOUR_USERNAME、password=YOUR_PASSWORD、Cookie={{ $('Solve AWS WAF').item.json.data.solution.cookie }} |
重要:
YOUR_USERNAMEとYOUR_PASSWORDを実際の認証情報に置き換えてください。さらに、ターゲットサイトのHTMLに合わせてフォームフィールド名を調整する必要がある場合があります。
| 設定 | 値 |
|---|---|
| 応答形式 | JSON |
| 応答本文 | ={{ JSON.stringify($json) }} |
WebhookエンドポイントにPOSTリクエストを送信します。
curl -X POST https://your-n8n-instance.com/webhook/login-aws-waf \
-H "Content-Type: application/json" \
-d '{
"websiteURL": "https://example.com/login",
"username": "testuser",
"password": "testpass"
}'
期待される応答(成功):
{
"status": "success",
"message": "Login successful",
"solution": {
"cookie": "aws-waf-token=AQAAAA..."
}
}
期待される応答(失敗):
{
"status": "failed",
"message": "Login failed: Invalid credentials or captcha",
"error": "..."
}
以下のJSONをコピーし、Menu -> Import from JSONを介してn8nにインポートします。
{
"name": "AWS WAF Account Login — CapSolver + Schedule + Webhook",
"nodes": [
{
"parameters": {
"content": "## AWS WAFアカウントログイン自動化 — CapSolver + スケジュール + Webhook\n\n**対象:** AWS WAFで保護されたアカウントへのログインを自動化する必要があるチーム。\n\n**機能:** AWS WAFチャレンジを解決し、解決されたCookieとユーザー認証情報でログインフォームを送信します。\n\n**仕組み:**\n1. Webhookがフローをトリガーします。\n2. CapSolverがAWS WAFチャレンジを解決します。\n3. HTTPリクエストが解決されたCookieとユーザー認証情報でログインフォームを送信します。\n4. Webhookがログイン結果を返します。\n\n**設定:**\n1. **Settings → Credentials**でCapSolver APIキーを追加します。\n2. プレースホルダーURLと認証情報を置き換えます。\n3. ターゲットサイトに合わせてフォームフィールド名を調整します。\n4. ワークフローをアクティブ化します。",
"height": 480,
"width": 460,
"color": 1
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-728,
-400
],
"id": "sticky-blog-main-1773678228096-1",
"name": "Sticky Note"
},
{
"parameters": {
"httpMethod": "POST",
"path": "login-aws-waf",
"responseMode": "responseNode",
"options": {}
},
"type": "n8n-nodes-base.webhook",
"typeVersion": 2.1,
"position": [
-400,
0
],
"id": "aw444444-4444-4444-4444-aw4444444401",
"name": "Webhook Trigger",
"webhookId": "aw444444-aaaa-bbbb-cccc-aw4444444401",
"onError": "continueRegularOutput"
},
{
"parameters": {
"operation": "AWS WAF",
"type": "AntiAwsWafTaskProxyLess",
"websiteURL": "={{ $json.body.websiteURL }}",
"optional": {}
},
"type": "n8n-nodes-capsolver.capSolver",
"typeVersion": 1,
"position": [
-96,
0
],
"id": "aw444444-4444-4444-4444-aw4444444402",
"name": "Solve AWS WAF",
"credentials": {
"capSolverApi": {
"id": "YOUR_CREDENTIAL_ID",
"name": "CapSolver account"
}
}
},
{
"parameters": {
"method": "POST",
"url": "={{ $json.body.websiteURL }}",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "user-agent",
"value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36"
},
{
"name": "Content-Type",
"value": "application/x-www-form-urlencoded"
},
{
"name": "Cookie",
"value": "={{ $('Solve AWS WAF').item.json.data.solution.cookie }}}"
}
]
},
"sendBody": true,
"contentType": "form-urlencoded",
"bodyParameters": {
"parameters": [
{
"name": "username",
"value": "={{ $json.body.username }}"
},
{
"name": "password",
"value": "={{ $json.body.password }}"
}
]
},
"options": {
"response": {
"response": {
"fullResponse": false
}
}
}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.3,
"position": [
208,
0
],
"id": "aw444444-4444-4444-4444-aw4444444403",
"name": "Submit Login Form"
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ JSON.stringify($json) }}",
"options": {}
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.5,
"position": [
512,
0
],
"id": "aw444444-4444-4444-4444-aw4444444404",
"name": "Respond to Webhook"
}
],
"connections": {
"Webhook Trigger": {
"main": [
[
{
"node": "Solve AWS WAF",
"type": "main",
"index": 0
}
]
]
},
"Solve AWS WAF": {
"main": [
[
{
"node": "Submit Login Form",
"type": "main",
"index": 0
}
]
]
},
"Submit Login Form": {
"main": [
[
{
"node": "Respond to Webhook",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
}
}
このエラーは、CapSolverアカウントまたはプランにAWS WAFアクセスが含まれていないことを示します。プランにこのサービスが含まれていることを確認するには、CapSolverダッシュボードを確認してください。
このエラーは、CapSolverノードで指定したwebsiteURLパラメータが正しくないことを示します。ターゲットサイトの開発者ツールのネットワークタブを再確認して、正しい値を使用していることを確認してください。
AWS WAFが正常に解決されたにもかかわらずログインに失敗する場合は、次の問題が考えられます。
このエラーは、n8nで設定したCapSolver APIキーが正しくないか、期限切れになっていることを示します。 CapSolverダッシュボードを確認し、n8nの認証情報を更新してください。
websiteURLと、オプションのawsKey、awsIv、awsContext、awsChallengeJS、awsApiJsパラメータが正しいことを再確認してください。これらはAWS WAFの解決を成功させるための鍵です。始める準備はできましたか? CapSolverに登録し、ボーナスコードn8nを使用して、最初のチャージで8%の追加ボーナスを獲得してください!

n8nとCapSolverを使用して、AWS WAFソルバーAPIとアカウントログイン自動化ワークフローを構築する方法を学びました。
まとめ:
AWS WAFの主な複雑さは、目に見えないチャレンジとCookieベースの認証にあります。HTTPリクエストでaws-waf-token Cookieを適切に処理することで、AWS WAFの解決を効果的に自動化できます。その後、これらの解決されたトークンをログインまたはスクレイピングワークフローにシームレスに統合できます。
ヒント: これらのワークフローはWebhookトリガーを使用しますが、トリガーノードを他のn8nトリガー(手動、アプリケーションイベント、フォーム送信など)に交換できます。AWS WAFが解決されたら、n8nの組み込みノードを使用して、結果をGoogleスプレッドシート、データベース、クラウドストレージに保存したり、Telegram/Slack/メールでアラートを送信したりできます。
AWS WAFは、Amazonのエンタープライズ級ウェブアプリケーションファイアウォールであり、目に見えないチャレンジとトークンベースの検証により、ボットトラフィックや悪用からウェブサイトを保護します。
AWS WAFはサイトキーを必要とせず、Cookieベースの認証を使用し、aws-waf-token Cookieを返します。通常、ユーザーが操作する目に見えるウィジェットのない目に見えないチャレンジです。
料金は使用量によって異なります。現在のAWS WAFの料金については、CapSolverの料金ページを確認してください。AWS WAF解決タスクは、単純な画像からテキストへの認識よりも高価ですが、他の複雑なCAPTCHAタイプよりも安価です。
AWS WAF解決タスクは通常、チャレンジの複雑さとCapSolverサーバーの負荷に応じて10〜30秒かかります。ImageToTextTaskとは異なり、AWS WAFの解決にはタスクの作成とポーリングが含まれるため、即座ではありません。
CapSolverのAWS WAF解決サービスでは、通常プロキシを提供する必要はありません。CapSolverはプロキシ要件を内部で処理します。websiteURLとオプションパラメータのみを提供する必要があります。
aws-waf-tokenが受け入れられないのはなぜですか?aws-waf-tokenが受け入れられない場合は、次の点を確認してください。
awsKey、awsIvなどのオプションパラメータを使用している場合は、それらを正しく提供していることを確認してください。AWS WAFが正常に解決されたにもかかわらずログインに失敗する場合は、次の点を確認してください。
はい。このワークフローは、セルフホスト型n8nとn8n Cloudの両方で機能します。CapSolverノードは公式統合としてすでに利用可能です。API認証情報を追加するだけです。