web-dev-qa-db-ja.com

Facebookクローラーがサーバーに大きな負荷をかけ、ディレクティブを無視しています。同じリソースに複数回アクセスする

Facebookクローラーが毎秒複数回サーバーにアクセスしており、Expiresヘッダーとog:ttlプロパティの両方を無視しているようです。

場合によっては、1〜5分の間に同じog:imageリソースに複数回アクセスします。 1つの例では、クローラーが12の異なるIPアドレスを使用して、3分間で同じ画像に12回アクセスしました。

次の例をキャッチする前に、リクエストを10分間記録するだけで済みました。

1つのイメージの時間とクローラーIPアドレスのリスト:

2018-03-30 15:12:58 - 66.220.156.145
2018-03-30 15:13:13 - 66.220.152.7
2018-03-30 15:12:59 - 66.220.152.100
2018-03-30 15:12:18 - 66.220.155.248
2018-03-30 15:12:59 - 173.252.124.29
2018-03-30 15:12:15 - 173.252.114.118
2018-03-30 15:12:42 - 173.252.85.205
2018-03-30 15:13:01 - 173.252.84.117
2018-03-30 15:12:40 - 66.220.148.100
2018-03-30 15:13:10 - 66.220.148.169
2018-03-30 15:15:16 - 173.252.99.50
2018-03-30 15:14:50 - 69.171.225.134

Og:imageによると Facebookのドキュメント

Facebookにコンテンツを共有したときに表示される画像のURL。詳細については下記をご覧ください。高品質のプレビュー画像を指定する方法については、ベストプラクティスガイドをご覧ください。

私がog:imageで使用する画像には、Expiresヘッダーが+7日後に設定されています。最近、それを+ 1年に変更しました。どちらの設定でも違いはないようです。クローラーが無視しているように見えるヘッダー:

Cache-Control: max-age=604800
Content-Length: 31048
Content-Type: image/jpeg
Date: Fri, 30 Mar 2018 15:56:47 GMT
Expires: Sat, 30 Mar 2019 15:56:47 GMT
Pragma: public
Server: nginx/1.4.6 (Ubuntu)
Transfer-Encoding: chunked
X-Powered-By: PHP/5.5.9-1ubuntu4.23

Facebookの オブジェクトプロパティのドキュメント によると、og:ttlプロパティは次のとおりです。

このページが再スクレイピングされるまでの秒数。これを使用して、Facebookコンテンツクローラーをレート制限します。最小許容値は345600秒(4日)です。低い値を設定すると、最小値が使用されます。このタグを含めない場合、ttlはWebサーバーから返された「Expires」ヘッダーから計算されます。それ以外の場合は、デフォルトで7日になります。

このog:ttlプロパティを2419200に設定しました。これは28日後です。

私はこのようなものを使いたくなりました:

header("HTTP/1.1 304 Not Modified"); 
exit;

しかし、私の恐怖は、Facebookのクローラーがヘッダーを無視し、画像に破損のマークを付けることです。これにより、共有されたストーリーから画像プレビューが削除されます。

レートを示すビデオ クローラーからのこれらのリクエストが入ってくるところ。

クローラーがこれらのリソースにすぐにアクセスするのを防ぐ方法はありますか?

開いているグラフとメタプロパティがどのように見えるかを示すサンプルコード:

<meta property="fb:app_id" content="MyAppId" />
<meta property="og:locale" content="en_GB" />
<meta property="og:type" content="website" />
<meta property="og:title" content="My title" />
<meta property="og:description" content="My description" />
<meta property="og:url" content="http://example.com/index.php?id=1234" />
<link rel="canonical" href="http://example.com/index.php?id=1234" />
<meta property="og:site_name" content="My Site Name" />
<meta property="og:image" content="http://fb.example.com/img/image.php?id=123790824792439jikfio09248384790283940829044" />
<meta property="og:image:width" content="940"/>
<meta property="og:image:height" content="491"/>
<meta property="og:ttl" content="2419200" />
13
Wayne Whitty

キャッシング、ヘッダーなどを使って他のほとんどすべてを試した後、サーバーを「過度に熱狂的な」Facebookクローラーから救った唯一のこと(user agent facebookexternalhitアクセスを拒否して送り返すだけですHTTP/1.1 429 Too Many Requests HTTP応答、クローラーが「クロールしすぎた」場合。

確かに、クローラーにクロールさせたい数千の画像がありましたが、Facebookクローラーは実際にサーバーをDDOS処理していました何万ものリクエストがありました(はい、同じURLを何度も繰り返しています)、1時間あたり。私はそれが1時間あたり40 000リクエストある時点でfacebookexternalhitユーザーエージェントを使用して別のFacebookのIPアドレスからだったことを覚えています。

クローラーを完全にブロックしたくなかったため、IPアドレスでブロックすることもできませんでした。 FBクローラーは少しバックオフ(かなり)するだけで済みました。

これは、これまで使用していたPHPコードの一部です。

.../images/index.php

<?php

// Number of requests permitted for facebook crawler per second.
const FACEBOOK_REQUEST_THROTTLE = 5;
const FACEBOOK_REQUESTS_JAR = __DIR__ . '/.fb_requests';
const FACEBOOK_REQUESTS_LOCK = __DIR__ . '/.fb_requests.lock';

function handle_lock($lockfile) {
    flock(fopen($lockfile, 'w'), LOCK_EX);
}

$ua = $_SERVER['HTTP_USER_AGENT'] ?? false;
if ($ua && strpos($ua, 'facebookexternalhit') !== false) {

    handle_lock(FACEBOOK_REQUESTS_LOCK);

    $jar = @file(FACEBOOK_REQUESTS_JAR);
    $currentTime = time();
    $timestamp = $jar[0] ?? time();
    $count = $jar[1] ?? 0;

    if ($timestamp == $currentTime) {
        $count++;
    } else {
        $count = 0;
    }

    file_put_contents(FACEBOOK_REQUESTS_JAR, "$currentTime\n$count");

    if ($count >= FACEBOOK_REQUEST_THROTTLE) {
        header("HTTP/1.1 429 Too Many Requests", true, 429);
        header("Retry-After: 60");
        die;
    }

}

// Everything under this comment happens only if the request is "legit". 

$filePath = $_SERVER['DOCUMENT_ROOT'] . $_SERVER['REQUEST_URI'];
if (is_readable($filePath)) {
    header("Content-Type: image/png");
    readfile($filePath);
}

また、画像に向けられたすべてのリクエストをこのPHPスクリプト:

.../images/.htaccess(Apacheを使用している場合)

RewriteEngine On
RewriteRule .* index.php [L] 

それはクローラーが「これを理解した」アプローチで、効果的に試行率を減らしたから数万 1時間あたりのリクエスト数数百/ thousands 1時間あたりのリクエスト数。

10
Smuuf

304 Not Modifiedヘッダーを盲目的に送信することはあまり意味がなく、Facebookのクローラーをさらに混乱させる可能性があります。あなたが本当にいくつかのリクエストをブロックすることに決めたなら、あなたは検討するかもしれません 429 Too Many Requests ヘッダー-それは問題が何であるかを少なくとも明確に示すでしょう。

より穏やかな解決策として、次のことを試してください。

  • いくつかの静的な値を含むLast-Modifiedヘッダーを追加します。 Facebookのクローラーは、絶えず変化するコンテンツに対してExpiresヘッダーを無視する必要があるが、欠落しているヘッダーを適切に処理するのに十分ではないことを検出するのに十分賢い場合があります。
  • 適切な304 Not Modifiedサポート付きのETagヘッダーを追加します。
  • 画像が静的な場合は、Cache-Controlヘッダーをmax-age=315360000, public, immutableに変更します。

また、キャッシュされた画像を保存し、PHPを使用せずにWebサーバー経由で提供することもできます。 URLをhttp://fb.example.com/img/image/123790824792439jikfio09248384790283940829044のようなものに変更した場合、書き換えルールにより、存在しないファイルのフォールバックを作成できます。

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^img/image/([0-9a-z]+)$ img/image.php?id=$1 [L]

最初のリクエストのみをPHPで処理する必要があります。これにより、リクエストされたURLのキャッシュが保存されます(例:/img/image/123790824792439jikfio09248384790283940829044)。その後、すべてのリクエストに対して、ウェブサーバーはキャッシュファイルからコンテンツを提供し、適切なヘッダーを送信して304 Not Modifiedを処理します。 レート制限 のnginxを設定することもできます。これは、提供する画像をPHPに委任するよりも効率的です。

3
rob006

Facebookのクローラーがいつもそれほど尊敬しているわけではないようです。過去に、ここで提案を実装しました: facebookexternalhit botからの過剰なトラフィック

Facebookがリクエストのレートを制限するのはいいことだと思うので、それは最善の解決策ではありませんが、明らかにそうしません。

3
Simon R

FacebookチームからWordを受け取った。うまくいけば、クローラーが画像のURLをどのように処理するかがある程度明確になります。

ここに行く:

クローラーは、画像のURLを他のURLとは異なる方法で処理します。

物理的な領域が異なり、それぞれが画像をフェッチする必要があるため、画像を複数回スクレイピングします。約20の異なるリージョンがあるため、開発者は各イメージに対して最大20の呼び出しを期待する必要があります。これらの要求を行うと、キャッシュに約1か月間保持されます-プラットフォームでの悪用を防ぐために、これらの画像を頻繁に再スクレイピングする必要があります(悪意のあるアクターが無害な画像をスクレイピングして、不快な画像に置き換える可能性があります)。 。

したがって、基本的には、og:imageで指定された画像が共有された後20回ヒットすることを期待する必要があります。その後、1か月後、再び削られます。

3
Wayne Whitty

FBクローラーがキャッシュヘッダーを無視する場合、この場合は「ETag」ヘッダーを追加して、正しい304応答を返し、サーバーの負荷を減らすことができます。

初めて画像を生成するときは、その画像のハッシュを(たとえばmd5を使用して)「ETag」応答ヘッダーとして計算します。サーバーが「If-None-Match」ヘッダーを含むリクエストを受け取った場合は、そのハッシュをすでに返したかどうかを確認してください。答えが「はい」の場合、304応答を返します。そうでない場合は、イメージを生成します。

特定のハッシュが既に返されているかどうかを確認する(イメージの再生成を避けながら)ことは、ハッシュをどこかに保存する必要があることを意味します... tmpフォルダーにイメージを保存し、ハッシュをファイル名として使用している可能性がありますか?

"ETag" + "If-None-Match"ヘッダーの詳細

0
Rober MH

Facebookのドキュメント states 「画像はURLに基​​づいてキャッシュされ、URLが変更されない限り更新されません。」つまり、ページに追加するヘッダーやメタタグは関係ありません。ボットはとにかく画像をキャッシュすることになっています。

これは私に考えさせました:

  1. 各ユーザーはページの少し異なるURLを共有していますか?これにより、共有イメージが毎回再キャッシュされます。
  2. 少し異なるURLを使用して共有イメージにアクセスしていますか?
  3. たぶん画像がどこか別の場所にリンクされているのでしょうか?

私はページログを監視して何が起こるかを正確に確認します。ページのURLまたは画像のURLが少しでも異なる場合、キャッシュメカニズムは機能しません。幸いなことに、これはヘッダー/タグタイプの問題のようには見えません。

0
Walter White

Facebookによると ドキュメント Facebotクローラーのみがクロールディレクティブを尊重します。しかし、彼らはこれも示唆しています

これらのユーザーエージェントの1つを対象にして、メタデータのみが含まれ、実際のコンテンツが含まれていないページの非公開バージョンをクローラーに提供できます。これはパフォーマンスの最適化に役立ち、ペイウォールコンテンツを安全に保つのに役立ちます。

一部の人々は、facebookexternalhitのアクセスをレート制限することを提案していますが、クローラーがコンテンツを更新できなくなる可能性があるため、これは良い考えではありません。

アーキテクチャによっては、異なるIPから複数のヒットが表示されても、同じボットが許容される場合があります。同じリソースがクロールされる頻度を確認する必要があります。 og:ttlは、ドキュメントが推奨し、役立つはずです。

0
Emil

@ Nicoが提案

ウェブサイト/サーバーでも同じ問題が発生しました。問題はog:urlメタタグ。それを削除した後、問題はほとんどのfacebookexternalhit呼び出しで解決されました。

それを削除してみて、問題が解決するかどうかを確認してください

0
serv-inc