I like to hardcode everything on my WordPress websites, without using any plugins. Is there any way to generate or update my sitemap everytime I publish/update a post on one of my Multisite blogs, without using plugins?
-
1You realize that there is no real difference between "hardcode" and a plugin besides the fact the plugins are portable and thus easier to work with?– WyckCommented Jan 20, 2012 at 18:53
-
3Downvote for the "without a Plugin" restriction.– Chip BennettCommented Feb 20, 2012 at 17:47
-
Not wanting to add ANOTHER plugin on a CLIENT site is perfectly acceptable for such a simple task. It's true the code is the same but perhaps the OP didn't know that– JasonDavisCommented May 23, 2013 at 3:45
-
as chip said it is a pointless question, in addition, doing a sitemap code that will not break the site when it is generated, is not trivial and good answer will not fit the format of the site– Mark KaplunCommented May 25, 2017 at 7:10
5 Answers
The following code works right off the bat. Your sitemap will show up on: https://your-website-name.com/sitemap.xml
Every time you create or update a page, post or custom post type it will show. Make sure to add the name of your 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' should be replaced with your own Custom Post Type (one or many)
'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 );
}
Before using the code provided in w3uiguru's answer, I had to make some improvements that follow the accepted standard for XML files. The code is below:
/* function to create sitemap.xml file in root directory of site */
// 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 );
}
-
2
-
@locutor-antonio-cezar @gabriel You need to change the
Weekly
toweekly
because ofError 1840: Element '{http://www.sitemaps.org/schemas/sitemap/0.9}changefreq': [facet 'enumeration'] The value 'Weekly' is not an element of the set {'always', 'hourly', 'daily', 'weekly', 'monthly', 'yearly', 'never'}
.– YuriCommented Aug 7, 2015 at 21:28
I don't know whether this works on multisite, but it is working perfect in a single WordPress installation for me.
When you create/update any posts, or pages, it will generate a sitemap.xml file and update the links (URLs) with the most recent first (last modified).
Copy and paste the below code in your active theme's functions.php file:
/* function to create sitemap.xml file in root directory of site */
// 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 );
}
-
I don't think that's going to work on multisite - it's writing sitemap.xml to the same file location on the server, so there would only be 1 actual sitemap.xml file that would get overwritten whenever any blog makes a change...– SatorsCommented May 19, 2017 at 18:50
-
querying all the posts in the site on each save is a sure way to bring down a site which has non trivial amount of content. in addition it will make save new posts slower and slower with every published post Commented May 25, 2017 at 7:14
-
@MarkKaplun - What would you recommend as an alternative to querying all posts? Commented Sep 8, 2022 at 8:37
-
@Motivated don't know, depends on what is it that you need to do. (sitemaps are part of core since 5.2, so at least this answer is irelevant for the specific use case) Commented Sep 8, 2022 at 13:14
Wordpress has an XML sitemap feature built in since 5.5 that you can customize.
See this blog post for more details and some quick examples. Take a look here to see a list of hooks you can use to customize the default out-of-the-box sitemap.
I changed the code from @locutor-antonio-cezar a little bit as I was looking for a very specific use case. I needed a sitemap specially written for Google News. What is different? The whole markup follows the rules. In my specific case, I limited the number of posts to 20. Also posts older than 2 days disappear. Maybe somebody needs this:
/* function to create sitemap.xml file in root directory of site */
// 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[ YOUR SITE ]]></news:name>' . "\n";
$sitemap .= "\t\t\t\t" . '<news:language>YOUR LANGUAGE</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 );
}
-
What if you need to generate an output for all posts? Limiting it to 20 doesn't scale. What alternative options are there? Commented Sep 8, 2022 at 8:39