How to host a sitemap.xml file when using Nginx to proxy requests to Ghost, and your site static content is being served by Nginx

(Just as a note, an XML sitemap is a complete list of all the pages on your site, in a format which can be easily consumed by search engines)

At the time of writing this post, automatic XML sitemap generation is a core feature within Ghost. It gives you the functionality of generating sitemaps so you don't have to generate the XML yourself.

We've set up the site following this approach by Ghost for Beginners.

Instead of responding on root path, dynamic Ghost content (A.K.A. the blog) would be reachable at This can be easyly accomplished following two steps:

1) modifiying config.js (Ghost configuration file) to tell Ghost where it should live.
2) making configuration change to Nginx conf file, to set /blog as the location and proxy traffic to Ghost port (2368).

But site is not only about blog, it is also about static. To accomplish this, we have followed the third approach described here by Ghost for Beginners too.

This way static content is being served by Nginx: architecture

How about sitemap.xml?

You can see an example of the original (Ghost automatically generated) sitemap for below: (published pages)
    (points to) ==> (root blog path) (published posts) (publishers) (existing tags)

But it doesn't include the static content, due to the fact that Ghost doesn't know it, remember, static content it's been served by Nginx, out of the Ghost control. In a first attempt we did try to fix it placing our custom sitemap.xml file in the root folder of the static content:
    (points to) (and other static content)

At this point, if we try requesting the root ( sitemap.xml file, we should see something like this:
    (points to) (and other static content) <==
    (points to) <==

But when we submitted this file to Google, it gave us a bad surprise: a nesting error. The reason is that there are two entries that point to (both of them marked with <==)

How have we fixed it?

Now there are just two steps to take:

1) We saved the content of /blog/sitemap.xml (the Ghost generated sitemap.xml for the blog) onto a file. This file was place on the root of the site static content as:


and we edited it to delete the first line:
(points to)

2) Then what we need to do is have be pointing to this file (file located on the server at /var/www/ In our Nginx conf, we added this near the end:

location /blog/sitemap.xml {
    return 301 /sitemap-blog.xml;

Finally try requesting the sitemap.xml file. You should see something like the following:
    (points to) (and other static content) (/sitemap-blog.xml)

Now this sitemap is validated by Google without any problem.

This configuration generates the sitemap every time a request is made to /sitemap.xml, so you don't have to generate the XML yourself.
The key is that the sitemap entry (published posts)
remains. Due to this reason you still have a dynamic sitemap.xml (some -changing- parts of it).

That's all, folks! It’s quick, it’s dirty, and maybe it’s not the most ‘correct’ way to do it, but it’s also the most maintainable method I’ve found (until someone send us a better one).

As I said, I’m fairly new to the Ghost app, so if you’ve got a cleaner, simpler way to deliver a dinamic sitemap.xml file when using Nginx to proxy requests to Ghost, and your site static content is being served by Nginx, feel free to contact us :)

Alberto Morales Morales

Software craftsman. Passion for developing quality code that can be proud of. Happily married.

Madrid, Spain.