Stack Guides & Tips

How to configure nginx for Yoast SEO sitemaps

Published · Last updated

If you’ve been following my WordPress fast stack guides, you’ll know I’m a big fan of nginx for the speed and scalability it provides. Some of you asked how to configure nginx with that stack guide to work with Yoast SEO sitemaps. The guide below covers our stacks, but will also work for any nginx users.

Did you know?

Since WordPress 5.5 (2020), core generates its own XML sitemaps at /wp-sitemap.xml. Yoast SEO switches those off and serves its own at /sitemap_index.xml, which is why nginx needs the rewrite rules below to route the pretty URLs through to Yoast.

Add the Yoast sitemap rewrite rules to your sites-available config file

With our rocket stack guide, the nginx file you need to edit will be:

/etc/nginx/sites-available/rocketstack.conf

So – either edit that file through FileZilla, or log into your server using SSH and run the following:

vi /etc/nginx/sites-available/rocketstack.conf
Why nginx needs these rules

nginx does not read .htaccess files, so any pretty-permalink rewrite that Apache would handle automatically has to be declared in your server config instead. That is why Yoast sitemaps 404 on nginx until you add the rules below.

The only thing that really matters in terms of where to place the following code is to make sure it goes inside your server { … } blocks. With our guide there are two of these, one for HTTP and one for HTTPS, so you’ll need to paste this code twice.

# Rewrites for Yoast SEO XML Sitemap
rewrite ^/sitemap_index.xml$ /index.php?sitemap=1 last;
rewrite ^/([^/]+?)-sitemap([0-9]+)?.xml$ /index.php?sitemap=$1&sitemap_n=$2 last;

location ~ ([^/]*)sitemap(.*).x(m|s)l$ {
    ## this rewrites sitemap.xml to /sitemap_index.xml
    rewrite ^/sitemap.xml$ /sitemap_index.xml permanent;
    ## this makes the XML sitemaps work
    rewrite ^/([a-z]+)?-?sitemap.xsl$ /index.php?xsl=$1 last;
    rewrite ^/sitemap_index.xml$ /index.php?sitemap=1 last;
    rewrite ^/([^/]+?)-sitemap([0-9]+)?.xml$ /index.php?sitemap=$1&sitemap_n=$2 last;
    ## The following lines are optional for the premium extensions
    ## News SEO
    rewrite ^/news-sitemap.xml$ /index.php?sitemap=wpseo_news last;
    ## Local SEO
    rewrite ^/locations.kml$ /index.php?sitemap=wpseo_local_kml last;
    rewrite ^/geo-sitemap.xml$ /index.php?sitemap=wpseo_local last;
    ## Video SEO
    rewrite ^/video-sitemap.xsl$ /index.php?xsl=video last;
}

Once you’ve edited the file, save it, then exit your editor.

Test for errors then restart nginx

On your server (putty, ssh etc), run the following command to double check you didn’t break your nginx config before you restart:

nginx -t

Once it has confirmed the file is ok, you can restart nginx using:

service nginx restart

Test your sitemap

From your wp-admin, visit SEO->General->Features tab then scroll to the sitemap and click the (i) button. It’ll tell you the URL for your sitemap.

Alternatively, just visit yourdomain.com/sitemap.xml or yourdomain.com/sitemap_index.xml.

A tidier way to do this

I’ve added an extra Yoast-specific snippet to our Rocket Stack github repo. So, another way to do this would be to download those snippets and then have just this line inside both your server blocks:

include snippets/yoast-sitemaps.xml

You can find instructions to downloading our repo in our full WordPress Rocket Stack guide.

Summary

If you are using other sitemap plugins, there should be nginx-specific instructions that come with it to tell your web-server how to rewrite the pretty permalinks to feed the correct parameters to your sitemap generation plugin.

In the guide above, I’ve shown how to do this for Yoast specifically, but similar steps will work for all sitemap plugins.

If you’d rather not maintain nginx rewrite rules for sitemaps at all, our own Super Speedy Sitemaps plugin generates ultra-fast XML sitemaps and is built for large WooCommerce and WordPress sites where the standard sitemap generators struggle.

Further reading

8 comments

  1. lloan alas

    Will this work with subdirectory sites? meaning, example.com/subdirectory

    1. Dave H. Customer

      Hi - yes - it should do. Let me know if it doesn't work for you and I'll investigate.

  2. Eric

    I have been using your config stacks for a few websites and had no issues but I cannot get the nginx rewrites to actually work on this one site. I have gone and looked at all configs and setup and find no reason it shouldn't work but the sitemaps only show on /?sitemap=1 then the sub sitemaps will 404 on me. What am i doing wrong here?

    1. Dave H. Customer

      I'm not sure - can you share your nginx config file? Maybe a different rule is being matched first?

      I presume you restarted nginx after adding the sitemap rewrite rules?

        1. Dave H. Customer

          Replied - sorry about delay - looks like you have 2 missing }'s

  3. Eric Schroeder

    Yep restarted service and zippo!

    Here is the conf:

    # This config file uses nginx fastcgi-cache
    fastcgi_cache_path /var/www/cache/evolutionit levels=1:2 keys_zone=evolutionit:100m inactive=60m;

    server {
    listen 80;
    server_name evolutionitservice.com;

    root /var/www/evolutionit;

    index index.php index.htm index.html;

    access_log /var/log/nginx/evolutionit_access.log;
    error_log /var/log/nginx/evolutionit_error.log;
    include snippets/gzip.conf
    #include snippets/yoast-sitemaps.xml;
    include snippets/acme-challenge.conf;

    # Rewrites for Yoast SEO XML Sitemap EJS
    rewrite ^/sitemap_index.xml$ /index.php?sitemap=1 last;
    rewrite ^/([^/]+?)-sitemap([0-9]+)?.xml$ /index.php?sitemap=$1&sitemap_n=$2 last;

    location ~ ([^/]*)sitemap(.*).x(m|s)l$ {
    ## this rewrites sitemap.xml to /sitemap_index.xml
    rewrite ^/sitemap.xml$ /sitemap_index.xml permanent;
    ## this makes the XML sitemaps work
    rewrite ^/([a-z]+)?-?sitemap.xsl$ /index.php?xsl=$1 last;
    rewrite ^/sitemap_index.xml$ /index.php?sitemap=1 last;
    rewrite ^/([^/]+?)-sitemap([0-9]+)?.xml$ /index.php?sitemap=$1&sitemap_n=$2 last;
    ## The following lines are optional for the premium extensions
    ## News SEO
    rewrite ^/news-sitemap.xml$ /index.php?sitemap=wpseo_news last;
    ## Local SEO
    rewrite ^/locations.kml$ /index.php?sitemap=wpseo_local_kml last;
    rewrite ^/geo-sitemap.xml$ /index.php?sitemap=wpseo_local last;
    ## Video SEO
    rewrite ^/video-sitemap.xsl$ /index.php?xsl=video last;
    }

    # Exclusions
    include snippets/exclusions.conf;

    # Security
    include snippets/security.conf;

    # Static Content
    include snippets/static-files.conf;

    # Fastcgi cache rules
    include snippets/fastcgi-cache.conf;

    include snippets/limits.conf;

    include snippets/nginx-cloudflare.conf;

    location / {
    try_files $uri $uri/ /index.php?$args;
    }

    location ~ \.php$ {
    try_files $uri =404;
    include snippets/fastcgi-params.conf;

    fastcgi_pass unix:/run/php/php7.3-fpm.sock;

    # Skip cache based on rules in snippets/fastcgi-cache.conf.
    fastcgi_cache_bypass $skip_cache;
    fastcgi_no_cache $skip_cache;

    # Define memory zone for caching. Should match key_zone in fastcgi_cache_path above.
    fastcgi_cache evolutionit;

    # Define caching time.
    fastcgi_cache_valid 60m;
    #increase timeouts
    fastcgi_read_timeout 6000;
    fastcgi_connect_timeout 6000;
    fastcgi_send_timeout 6000;
    proxy_read_timeout 6000;
    proxy_connect_timeout 6000;
    proxy_send_timeout 6000;
    send_timeout 6000;

    #these lines should be the ones to allow Cloudflare Flexible SSL to be used so the server does not need to decrypt SSL
    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header X-Forwarded-Server $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto https;
    proxy_set_header X-Real-IP $remote_addr;

    server {
    listen 443 ssl default_server;
    server_name evolutionitservice.com;

    root /var/www/evolutionit;

    index index.php index.htm index.html;
    #Include Yoast Sitemap Config
    # include snippets/yoast-sitemaps.xml;

    # Rewrites for Yoast SEO XML Sitemap EJS
    rewrite ^/sitemap_index.xml$ /index.php?sitemap=1 last;
    rewrite ^/([^/]+?)-sitemap([0-9]+)?.xml$ /index.php?sitemap=$1&sitemap_n=$2 last;

    location ~ ([^/]*)sitemap(.*).x(m|s)l$ {
    ## this rewrites sitemap.xml to /sitemap_index.xml
    rewrite ^/sitemap.xml$ /sitemap_index.xml permanent;
    ## this makes the XML sitemaps work
    rewrite ^/([a-z]+)?-?sitemap.xsl$ /index.php?xsl=$1 last;
    rewrite ^/sitemap_index.xml$ /index.php?sitemap=1 last;
    rewrite ^/([^/]+?)-sitemap([0-9]+)?.xml$ /index.php?sitemap=$1&sitemap_n=$2 last;
    ## The following lines are optional for the premium extensions
    ## News SEO
    rewrite ^/news-sitemap.xml$ /index.php?sitemap=wpseo_news last;
    ## Local SEO
    rewrite ^/locations.kml$ /index.php?sitemap=wpseo_local_kml last;
    rewrite ^/geo-sitemap.xml$ /index.php?sitemap=wpseo_local last;
    ## Video SEO
    rewrite ^/video-sitemap.xsl$ /index.php?xsl=video last;
    }
    access_log /var/log/nginx/evolutionit_ssl_access.log;
    error_log /var/log/nginx/evolutionit_ssl_error.log;

    #once you have SSL certificates using LetsEncrypt you can alter the paths in the two lines below to reflect your domain and uncomment the lines by removing the leading # symbol
    #ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
    #ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;

    # Exclusions
    include snippets/exclusions.conf;

    # Security
    include snippets/security.conf;

    # Static Content
    include snippets/static-files.conf;

    # Fastcgi cache rules
    include snippets/fastcgi-cache.conf;

    include snippets/limits.conf;

    include snippets/nginx-cloudflare.conf;

    location / {
    try_files $uri $uri/ /index.php?$args;
    }

    location ~ \.php$ {
    try_files $uri =404;
    include snippets/fastcgi-params.conf;

    fastcgi_pass unix:/run/php/php7.3-fpm.sock;

    # Skip cache based on rules in snippets/fastcgi-cache.conf.
    fastcgi_cache_bypass $skip_cache;
    fastcgi_no_cache $skip_cache;

    # Define memory zone for caching. Should match key_zone in fastcgi_cache_path above.
    fastcgi_cache evolutionit;

    # Define caching time.
    fastcgi_cache_valid 60m;
    #increase timeouts
    fastcgi_read_timeout 6000;
    fastcgi_connect_timeout 6000;
    fastcgi_send_timeout 6000;
    proxy_read_timeout 6000;
    proxy_connect_timeout 6000;
    proxy_send_timeout 6000;
    send_timeout 6000;

    #these lines should be the ones to allow Cloudflare Flexible SSL to be used so the server does not need to decrypt SSL if you wish
    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header X-Forwarded-Server $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto https;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-NginX-Proxy true;

    1. Dave H. Customer

      It looks like your first server {} block for port 80 does not have the closing brace. '}' is missing.

▲
▼
1/1