Come generare/aggiornare una sitemap XML senza plugin?
Il seguente codice funziona subito. La tua sitemap sarà visibile all'indirizzo: https://your-website-name.com/sitemap.xml
Ogni volta che crei o aggiorni una pagina, articolo o custom post type apparirà nella sitemap. Assicurati di aggiungere il nome del tuo custom post type:
add_action( 'publish_post', 'ow_create_sitemap' );
add_action( 'publish_page', 'ow_create_sitemap' );
add_action( 'save_post', 'ow_create_sitemap' );
function ow_create_sitemap() {
$postsForSitemap = get_posts(array(
'numberposts' => -1,
'orderby' => 'modified',
// 'custom_post' dovrebbe essere sostituito con il tuo Custom Post Type (uno o più)
'post_type' => array( 'post', 'page', 'custom_post' ),
'order' => 'DESC'
));
$sitemap = '<?xml version="1.0" encoding="UTF-8"?>';
$sitemap .= '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">';
foreach( $postsForSitemap as $post ) {
setup_postdata( $post );
$postdate = explode( " ", $post->post_modified );
$sitemap .= '<url>'.
'<loc>' . get_permalink( $post->ID ) . '</loc>' .
'<lastmod>' . $postdate[0] . '</lastmod>' .
'<changefreq>monthly</changefreq>' .
'</url>';
}
$sitemap .= '</urlset>';
$fp = fopen( ABSPATH . 'sitemap.xml', 'w' );
fwrite( $fp, $sitemap );
fclose( $fp );
}

Non so se funzioni su multisite, ma per me sta funzionando perfettamente in un'installazione singola di WordPress.
Quando crei/aggiorni qualsiasi articolo o pagina, genererà un file sitemap.xml e aggiornerà i link (URL) con i più recenti per primi (ultima modifica).
Copia e incolla il codice qui sotto nel file functions.php del tuo tema attivo:
/* funzione per creare il file sitemap.xml nella directory principale del sito */
// add_action("publish_post", "eg_create_sitemap");
// add_action("publish_page", "eg_create_sitemap");
add_action( "save_post", "eg_create_sitemap" );
function eg_create_sitemap() {
$postsForSitemap = get_posts( array(
'numberposts' => -1,
'orderby' => 'modified',
'post_type' => array( 'post', 'page' ),
'order' => 'DESC'
) );
$sitemap = '<?xml version="1.0" encoding="UTF-8"?>';
$sitemap .= "\n" . '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . "\n";
foreach( $postsForSitemap as $post ) {
setup_postdata( $post );
$postdate = explode( " ", $post->post_modified );
$sitemap .= "\t" . '<url>' . "\n" .
"\t\t" . '<loc>' . get_permalink( $post->ID ) . '</loc>' .
"\n\t\t" . '<lastmod>' . $postdate[0] . '</lastmod>' .
"\n\t\t" . '<changefreq>monthly</changefreq>' .
"\n\t" . '</url>' . "\n";
}
$sitemap .= '</urlset>';
$fp = fopen( ABSPATH . "sitemap.xml", 'w' );
fwrite( $fp, $sitemap );
fclose( $fp );
}

Non credo che funzionerà su multisite - sta scrivendo sitemap.xml nella stessa posizione del file sul server, quindi ci sarebbe solo 1 file sitemap.xml effettivo che verrebbe sovrascritto ogni volta che un blog apporta una modifica...

Interrogare tutti i post del sito ad ogni salvataggio è un modo sicuro per far cadere un sito che ha una quantità non banale di contenuti. Inoltre renderà il salvataggio di nuovi post sempre più lento con ogni pubblicazione

@MarkKaplun - Cosa consiglieresti come alternativa all'interrogazione di tutti i post?

Prima di utilizzare il codice fornito nella risposta di w3uiguru, ho dovuto apportare alcuni miglioramenti che seguono lo standard accettato per i file XML. Il codice è riportato di seguito:
/* funzione per creare il file sitemap.xml nella directory root del sito */
// add_action("publish_post", "eg_create_sitemap");
// add_action("publish_page", "eg_create_sitemap");
add_action( "save_post", "eg_create_sitemap" );
function eg_create_sitemap() {
if ( str_replace( '-', '', get_option( 'gmt_offset' ) ) < 10 ) {
$tempo = '-0' . str_replace( '-', '', get_option( 'gmt_offset' ) );
} else {
$tempo = get_option( 'gmt_offset' );
}
if( strlen( $tempo ) == 3 ) { $tempo = $tempo . ':00'; }
$postsForSitemap = get_posts( array(
'numberposts' => -1,
'orderby' => 'modified',
'post_type' => array( 'post', 'page' ),
'order' => 'DESC'
) );
$sitemap .= '<?xml version="1.0" encoding="UTF-8"?>' . '<?xml-stylesheet type="text/xsl" href="' .
esc_url( home_url( '/' ) ) . 'sitemap.xsl"?>';
$sitemap .= "\n" . '<urlset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd" xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . "\n";
$sitemap .= "\t" . '<url>' . "\n" .
"\t\t" . '<loc>' . esc_url( home_url( '/' ) ) . '</loc>' .
"\n\t\t" . '<lastmod>' . date( "Y-m-d\TH:i:s", current_time( 'timestamp', 0 ) ) . $tempo . '</lastmod>' .
"\n\t\t" . '<changefreq>daily</changefreq>' .
"\n\t\t" . '<priority>1.0</priority>' .
"\n\t" . '</url>' . "\n";
foreach( $postsForSitemap as $post ) {
setup_postdata( $post);
$postdate = explode( " ", $post->post_modified );
$sitemap .= "\t" . '<url>' . "\n" .
"\t\t" . '<loc>' . get_permalink( $post->ID ) . '</loc>' .
"\n\t\t" . '<lastmod>' . $postdate[0] . 'T' . $postdate[1] . $tempo . '</lastmod>' .
"\n\t\t" . '<changefreq>Weekly</changefreq>' .
"\n\t\t" . '<priority>0.5</priority>' .
"\n\t" . '</url>' . "\n";
}
$sitemap .= '</urlset>';
$fp = fopen( ABSPATH . "sitemap.xml", 'w' );
fwrite( $fp, $sitemap );
fclose( $fp );
}

Potresti per favore spiegare il tuo codice e inviare una [modifica] per formattarlo in modo più leggibile? Grazie

@locutor-antonio-cezar @gabriel Devi cambiare Weekly
in weekly
a causa di Errore 1840: Elemento '{http://www.sitemaps.org/schemas/sitemap/0.9}changefreq': [facet 'enumeration'] Il valore 'Weekly' non è un elemento dell'insieme {'always', 'hourly', 'daily', 'weekly', 'monthly', 'yearly', 'never'}
.

WordPress include una funzionalità integrata per le mappe del sito XML a partire dalla versione 5.5 che puoi personalizzare.
Vedi questo articolo del blog per maggiori dettagli e alcuni esempi rapidi. Dai un'occhiata qui per vedere una lista di hook che puoi utilizzare per personalizzare la mappa del sito predefinita.

Ho modificato un po' il codice di @locutor-antonio-cezar poiché stavo cercando un caso d'uso molto specifico. Avevo bisogno di una sitemap scritta appositamente per Google News. Cosa c'è di diverso? L'intero markup segue le regole. Nel mio caso specifico, ho limitato il numero di articoli a 20. Inoltre, gli articoli più vecchi di 2 giorni scompaiono. Forse qualcuno ne avrà bisogno:
/* funzione per creare il file sitemap.xml nella directory root del sito */
// add_action("publish_post", "eg_create_sitemap");
// add_action("publish_page", "eg_create_sitemap");
add_action( "save_post", "eg_create_sitemap" );
function eg_create_sitemap() {
if ( str_replace( '-', '', get_option( 'gmt_offset' ) ) < 10 ) {
$tempo = '-0' . str_replace( '-', '', get_option( 'gmt_offset' ) );
} else {
$tempo = get_option( 'gmt_offset' );
}
if( strlen( $tempo ) == 3 ) { $tempo = $tempo . ':00'; }
$postsForSitemap = get_posts( array(
'numberposts' => 20,
'orderby' => 'modified',
'post_type' => array( 'post', 'page' ),
'order' => 'DESC',
'date_query' => array(
'after' => date('Y-m-d', strtotime('-2 days'))
)
) );
$sitemap .= '<?xml version="1.0" encoding="UTF-8"?>';
$sitemap .= "\n" . '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:mobile="http://www.google.com/schemas/sitemap-mobile/1.0" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1">' . "\n";
foreach( $postsForSitemap as $post ) {
setup_postdata( $post);
$postdate = explode( " ", $post->post_modified );
$sitemap .= "\t" . "<url>" . "\n";
$sitemap .= "\t\t" . "<loc>" . get_permalink( $post->ID ) . '</loc>';
$sitemap .= "\t\t" . '<news:news>' . "\n";
$sitemap .= "\t\t\t" . '<news:publication>' . "\n";
$sitemap .= "\t\t\t\t" . '<news:name><![CDATA[ IL TUO SITO ]]></news:name>' . "\n";
$sitemap .= "\t\t\t\t" . '<news:language>LINGUA</news:language>' . "\n";
$sitemap .= "\t\t\t" . '</news:publication>' . "\n";
$sitemap .= "\t\t\t<news:publication_date>" . $postdate[0] . 'T' . $postdate[1] . $tempo . "</news:publication_date>\n";
$sitemap .= "\t\t\t" . '<news:title><![CDATA[' . get_the_title( $post) . ']]></news:title>' . "\n";
$sitemap .= "\t\t" . '</news:news>' . "\n";
$sitemap .= "\t" . '</url>' . "\n";
}
$sitemap .= '</urlset>';
$fp = fopen( ABSPATH . "sitemap_news.xml", 'w' );
fwrite( $fp, $sitemap );
fclose( $fp );
}
