Google Web Search Indexing API. <alebal web Blog> | Appunti di PHP, MySql, javascript, Css, HTML, HTML5 e altro...

Home PHP MySql Server Javascript GTA 5 Tristi storie Varie Open menu

Zzo sono? Sembra l'ultimo modo rimasto per costringere Google a vedere i nuovi URL del tuo sito web dopo qualche minuto invece che dopo 30 anni.

Sono il solito casino, ma se ne può uscire.

Sono limitate a 200 invii al giorno per progetto, quindi se ne invii tanti ti serve un differente progetto per ogni sito. Si può aumentare la quota giornaliera, va fatta una richiesta e ti rispondono dopo 3-4 settimane, per una cosa che dovrebbe servire a fargli vedere i tuoi URL dopo 5 minuti... A Google si sono completamente rincoglioniti!

Comunque...


Google Cloud

Si parte da Google Cloud https://console.cloud.google.com

Crea un nuovo progetto (dagli solo il nome)

Seleziona il progetto e nella barra in alto cerca e abilita

Poi menu in alto a sinistra con le tre barre -> IAM e amministrazione -> Service account

Crea service account (solo il nome)

Poi Azioni, i tre quadratini -> Gestisci chiavi

Aggiungi chiave -> Nuova-> JSON

Creala e scaricala sulla directory del tuo sito, salvala come gsak.json, caricala sul server.


Google Search Console

Bisogna far capire a search console che sei sempre tu, devi essere verificato proprietario del siot (tipo con TXT sui dns), in teoria basterebbe aggiungere la mail del service account creato in google cloud tra i proprietari del sito in search console, ma non funziona da mesi, non sincronizzano i database, non trova la mail fa un casino, e da google non risolvono... si sono rincoglioniti del tutto.

Ma un tizio ha trovato un modo che in qualche modo lo fa funzionare.

Sono tre file, da lanciare con il terminale del tuo pc locale, o del server o come vuoi.

In tutti e tre i file devi cambiare il dominio e le righe che vanno da project_id a client_id inclusa.

Il primo file è questo:

<?php

// ==========================================
// 1. CONFIGURATION
// ==========================================
$domainName = 'yourdomain.com'; // <-- Replace with your domain

// Paste the EXACT contents of your service account JSON key file here
$saJsonString = '{
  "type": "service_account",
  "project_id": "project_id",
  "private_key_id": "private_key_id",
  "private_key": "-----BEGIN PRIVATE KEY----------END PRIVATE KEY-----n",
  "client_email": "client_email",
  "client_id": "client_id",
  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  "token_uri": "https://oauth2.googleapis.com/token",
  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
  "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/..."
}';

// ==========================================
// 2. GENERATE OAUTH 2.0 ACCESS TOKEN VIA NATIVE JWT
// ==========================================
$sa = json_decode($saJsonString, true);
if (!$sa || !isset($sa['private_key'])) {
    die("Error: Invalid Service Account JSON string.n");
}

// JWT Header
$header = json_encode(['alg' => 'RS256', 'typ' => 'JWT']);

// JWT Payload (Assertion)
$now = time();
$payload = json_encode([
    'iss'   => $sa['client_email'],
    'scope' => 'https://www.googleapis.com/auth/siteverification',
    'aud'   => $sa['token_uri'],
    'exp'   => $now + 3600,
    'iat'   => $now
]);

// Base64Url Encode Helper Function
function base64UrlEncode($data) {
    return str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($data));
}

$base64UrlHeader = base64UrlEncode($header);
$base64UrlPayload = base64UrlEncode($payload);

// Sign the JWT with the Private Key using OpenSSL
$signatureInput = $base64UrlHeader . "." . $base64UrlPayload;
$privateKey = $sa['private_key'];

if (!openssl_sign($signatureInput, $signature, $privateKey, OPENSSL_ALGO_SHA256)) {
    die("Error: Failed to sign JWT. Check your private key or OpenSSL extension.n");
}
$base64UrlSignature = base64UrlEncode($signature);

// Construct the completed JWT
$jwt = $signatureInput . "." . $base64UrlSignature;

// Exchange JWT for an actual Access Token via cURL
$ch = curl_init($sa['token_uri']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
    'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer',
    'assertion'  => $jwt
]));

$tokenResponse = json_decode(curl_exec($ch), true);
curl_close($ch);

if (!isset($tokenResponse['access_token'])) {
    die("Error fetching access token: " . json_encode($tokenResponse) . "n");
}

$accessToken = $tokenResponse['access_token'];

// ==========================================
// 3. CALL SITE VERIFICATION API
// ==========================================
$url = 'https://www.googleapis.com/siteVerification/v1/token';

$apiPayload = [
    "verificationMethod" => "DNS_TXT",
    "site" => [
        "identifier" => $domainName,
        "type" => "INET_DOMAIN"
    ]
];

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($apiPayload));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer ' . $accessToken,
    'Content-Type: application/json'
]);

$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

// ==========================================
// 4. OUTPUT RESULTS
// ==========================================
if ($httpCode === 200) {
    echo "Success!n";
    $data = json_decode($response, true);
    print_r($data);
} else {
    echo "Error (HTTP Status $httpCode):n";
    echo $response . "n";
}

Poi si lancia sul terminale tipo cosi (metti a posto directory)

php 'GoogleWebPush/verify_1.php'

E ti da un record TXT da aggiungere ai dns del tuo dominio.

Il secondo file è questo:

<?php

// ==========================================
// 1. CONFIGURATION (Same as before)
// ==========================================
$domainName = 'yourdomain.com'; // <-- Replace with your domain

// Paste the EXACT contents of your service account JSON key file here
$saJsonString = '{
  "type": "service_account",
  "project_id": "project_id",
  "private_key_id": "private_key_id",
  "private_key": "-----BEGIN PRIVATE KEY----------END PRIVATE KEY-----n",
  "client_email": "client_email",
  "client_id": "client_id",
  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  "token_uri": "https://oauth2.googleapis.com/token",
  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
  "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/googlewebpush%40hopeful-expanse-496201-v2.iam.gserviceaccount.com",
  "universe_domain": "googleapis.com"
}';

// ==========================================
// 2. GENERATE OAUTH 2.0 ACCESS TOKEN (Native JWT)
// ==========================================
$sa = json_decode($saJsonString, true);
$header = json_encode(['alg' => 'RS256', 'typ' => 'JWT']);
$now = time();
$payload = json_encode([
    'iss'   => $sa['client_email'],
    'scope' => 'https://www.googleapis.com/auth/siteverification', // Scope stays the same
    'aud'   => $sa['token_uri'],
    'exp'   => $now + 3600,
    'iat'   => $now
]);

function base64UrlEncode($data) { return str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($data)); }
$signatureInput = base64UrlEncode($header) . "." . base64UrlEncode($payload);
openssl_sign($signatureInput, $signature, $sa['private_key'], OPENSSL_ALGO_SHA256);
$jwt = $signatureInput . "." . base64UrlEncode($signature);

$ch = curl_init($sa['token_uri']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query(['grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer', 'assertion' => $jwt]));
$tokenResponse = json_decode(curl_exec($ch), true);
curl_close($ch);

$accessToken = $tokenResponse['access_token'];

// ==========================================
// 3. CALL INSERT ENDPOINT
// ==========================================
// Note the verificationMethod parameter appended to the URL
$url = 'https://www.googleapis.com/siteVerification/v1/webResource?verificationMethod=DNS_TXT';

$apiPayload = [
    "site" => [
        "identifier" => $domainName,
        "type" => "INET_DOMAIN"
    ]
];

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($apiPayload));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer ' . $accessToken,
    'Content-Type: application/json'
]);

$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($httpCode === 200) {
    echo "Success! The Service Account is now a Verified Owner.n";
    print_r(json_decode($response, true));
} else {
    echo "Error (HTTP Status $httpCode):n" . $response . "n";
}

Verifica il dns, ci può volere un po finche il dns si propaga.

Il terzo file è questo:

<?php

// ==========================================
// 1. CONFIGURATION
// ==========================================
$domainName = 'yourdomain.com'; // <-- Replace with your domain

// Paste the EXACT contents of your service account JSON key file here
$saJsonString = '{
  "type": "service_account",
  "project_id": "project_id",
  "private_key_id": "private_key_id",
  "private_key": "-----BEGIN PRIVATE KEY----------END PRIVATE KEY-----n",
  "client_email": "client_email",
  "client_id": "client_id",
  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  "token_uri": "https://oauth2.googleapis.com/token",
  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
  "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/googlewebpush%40hopeful-expanse-496201-v2.iam.gserviceaccount.com",
  "universe_domain": "googleapis.com"
}';

// ==========================================
// 2. GENERATE OAUTH 2.0 ACCESS TOKEN (New Scope!)
// ==========================================
$sa = json_decode($saJsonString, true);
$header = json_encode(['alg' => 'RS256', 'typ' => 'JWT']);
$now = time();
$payload = json_encode([
    'iss'   => $sa['client_email'],
    'scope' => 'https://www.googleapis.com/auth/webmasters', // <-- CRITICAL: Changed scope
    'aud'   => $sa['token_uri'],
    'exp'   => $now + 3600,
    'iat'   => $now
]);

function base64UrlEncode($data) { return str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($data)); }
$signatureInput = base64UrlEncode($header) . "." . base64UrlEncode($payload);
openssl_sign($signatureInput, $signature, $sa['private_key'], OPENSSL_ALGO_SHA256);
$jwt = $signatureInput . "." . base64UrlEncode($signature);

$ch = curl_init($sa['token_uri']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query(['grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer', 'assertion' => $jwt]));
$tokenResponse = json_decode(curl_exec($ch), true);
curl_close($ch);

$accessToken = $tokenResponse['access_token'];

// ==========================================
// 3. CALL SEARCH CONSOLE SITES.ADD (PUT Request)
// ==========================================
// We use 'sc-domain:' prefix and URL encode it as 'sc-domain%3A'
$siteId = 'sc-domain:' . $domainName;
$url = 'https://www.googleapis.com/webmasters/v3/sites/' . urlencode($siteId);

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT"); // <-- Search Console requires a PUT request here
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer ' . $accessToken,
    'Content-Length: 0' // No body payload is required for this call
]);

$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

// A successful sites.add call returns HTTP 204 (No Content)
if ($httpCode === 204) {
    echo "Success! The domain has been registered to your Service Account's Search Console.n";
    echo "You can now fetch its search traffic data directly via the API using this SA.n";
} else {
    echo "Error (HTTP Status $httpCode):n" . $response . "n";
}

Questo se tutto va bene aggiunge la mail del service account di google cloud come proprietario del tuo sito su search console.


Classe GoogleIndexer

una classe da caricare sul tuo server

<?php
class GoogleIndexer {
    private $key;
    private $email;

    public function __construct($jsonKeyPath) {
        $config = json_decode(file_get_contents($jsonKeyPath), true);
        $this->key = $config['private_key'];
        $this->email = $config['client_email'];
    }

    private function base64UrlEncode($data) {
        return str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($data));
    }

    public function getAccessToken() {
        $header = $this->base64UrlEncode(json_encode(['alg' => 'RS256', 'typ' => 'JWT']));
        $iat = time();
        $exp = $iat + 3600;
        
        $payload = $this->base64UrlEncode(json_encode([
            'iss' => $this->email,
            'scope' => 'https://www.googleapis.com/auth/indexing',
            'aud' => 'https://oauth2.googleapis.com/token',
            'iat' => $iat,
            'exp' => $exp
        ]));

        $signature = '';
        openssl_sign("$header.$payload", $signature, $this->key, 'SHA256');
        $jwt = "$header.$payload." . $this->base64UrlEncode($signature);

        $ch = curl_init('https://oauth2.googleapis.com/token');
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
            'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer',
            'assertion' => $jwt
        ]));

        $response = json_decode(curl_exec($ch), true);
        curl_close($ch);
        return $response['access_token'] ?? null;
    }

    // NEW METHOD: Send multiple URLs one by one using the same token
    public function indexUrls(array $urls) {
        $token = $this->getAccessToken();
        if (!$token) return ["error" => "Auth Failed"];

        $results = [];
        
        // Initialize cURL once to reuse the connection (faster execution)
        $ch = curl_init('https://indexing.googleapis.com/v3/urlNotifications:publish');
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            "Content-Type: application/json",
            "Authorization: Bearer $token"
        ]);

        foreach ($urls as $url) {
            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
                'url' => $url,
                'type' => 'URL_UPDATED'
            ]));

            $response = curl_exec($ch);
            $results[$url] = json_decode($response, true);
        }

        curl_close($ch);
        return $results;
    }
}

Come si usa

Tutto questo casino alla fine si usa in questo file:

<?
//carica questo su server
require_once 'google_jwt_class.php'; // Path to your class file

//carica anche questo su server
$indexer = new GoogleIndexer(__DIR__ . '/gsak.json');

$googlewebpush = array();

$query_photo = "SELECT * FROM ".$prefix."photo WHERE insitemap='0' ORDER BY id_photo ASC ";
    $photo_query = $db->prepare($query_photo);
    $photo_query->execute();

//verifico se trovato qualche riga, se non trovate non tocco i file
if($photo_query->rowCount() >= 1){

//ciclo nuove righe trovate
		while($row_photo = $photo_query->fetch(PDO::FETCH_ASSOC)){

$googlewebpush[] = _URL.'/'.$row_photo['link'].'';

}//while
}//if

// Call the new batch method
$responses = $indexer->indexUrls($googlewebpush);

// Print out the results for each URL
//header('Content-Type: application/json');
echo json_encode($responses, JSON_PRETTY_PRINT);
?>

La mia query trova tutti i nuovi link, la classe li invia a google, che se tutto va bene li dovrebbe guardare in qualche minuto.

Se tutto è andato bene dice tipo "urlNotificationMetadata" o poco di più.

Google Digg Reddit Tumblr Pinterest StumbleUpon Email

Rating: 4 out of 5 by 176 visitors

Leave your comment