How to generate/update a XML sitemap without plugins?

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?

Related posts

Leave a Reply

4 comments

  1. 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 );
    }
    
  2. 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" .
            "tt" . '<loc>' . esc_url( home_url( '/' ) ) . '</loc>' .
            "ntt" . '<lastmod>' . date( "Y-m-dTH:i:s", current_time( 'timestamp', 0 ) ) . $tempo . '</lastmod>' .
            "ntt" . '<changefreq>daily</changefreq>' .
            "ntt" . '<priority>1.0</priority>' .
            "nt" . '</url>' . "n";
        foreach( $postsForSitemap as $post ) {
            setup_postdata( $post);
            $postdate = explode( " ", $post->post_modified );
            $sitemap .= "t" . '<url>' . "n" .
                "tt" . '<loc>' . get_permalink( $post->ID ) . '</loc>' .
                "ntt" . '<lastmod>' . $postdate[0] . 'T' . $postdate[1] . $tempo . '</lastmod>' .
                "ntt" . '<changefreq>Weekly</changefreq>' .
                "ntt" . '<priority>0.5</priority>' .
                "nt" . '</url>' . "n";
        }
        $sitemap .= '</urlset>';
        $fp = fopen( ABSPATH . "sitemap.xml", 'w' );
        fwrite( $fp, $sitemap );
        fclose( $fp );
    }
    
  3. 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" .
                "tt" . '<loc>' . get_permalink( $post->ID ) . '</loc>' .
                "ntt" . '<lastmod>' . $postdate[0] . '</lastmod>' .
                "ntt" . '<changefreq>monthly</changefreq>' .
                "nt" . '</url>' . "n";
        }     
        $sitemap .= '</urlset>';     
        $fp = fopen( ABSPATH . "sitemap.xml", 'w' );
        fwrite( $fp, $sitemap );
        fclose( $fp );
    }
    
  4. 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 .= "tt" . "<loc>" . get_permalink( $post->ID ) . '</loc>';
            $sitemap .= "tt" . '<news:news>' . "n";
            $sitemap .= "ttt" . '<news:publication>' . "n";
            $sitemap .= "tttt" . '<news:name><![CDATA[ YOUR SITE ]]></news:name>' . "n";
            $sitemap .= "tttt" . '<news:language>YOUR LANGUAGE</news:language>' . "n";
            $sitemap .= "ttt" . '</news:publication>' . "n";
            $sitemap .= "ttt<news:publication_date>" . $postdate[0] . 'T' . $postdate[1] . $tempo . "</news:publication_date>n";
            $sitemap .= "ttt" . '<news:title><![CDATA[' . get_the_title( $post) . ']]></news:title>' . "n";
            $sitemap .= "tt" . '</news:news>' . "n";
            $sitemap .= "t" . '</url>' . "n";
        }
        $sitemap .= '</urlset>';
        $fp = fopen( ABSPATH . "sitemap_news.xml", 'w' );
        fwrite( $fp, $sitemap );
        fclose( $fp );
    }