Skip to content

Instantly share code, notes, and snippets.

@Julian-Nash
Last active July 22, 2025 14:58
Show Gist options
  • Save Julian-Nash/aa3041b47183176ca9ff81c8382b655a to your computer and use it in GitHub Desktop.
Save Julian-Nash/aa3041b47183176ca9ff81c8382b655a to your computer and use it in GitHub Desktop.
Flask dynamic sitemap generator
@app.route("/sitemap")
@app.route("/sitemap/")
@app.route("/sitemap.xml")
def sitemap():
"""
Route to dynamically generate a sitemap of your website/application.
lastmod and priority tags omitted on static pages.
lastmod included on dynamic content such as blog posts.
"""
from flask import make_response, request, render_template
import datetime
from urllib.parse import urlparse
host_components = urlparse(request.host_url)
host_base = host_components.scheme + "://" + host_components.netloc
# Static routes with static content
static_urls = list()
for rule in app.url_map.iter_rules():
if not str(rule).startswith("/admin") and not str(rule).startswith("/user"):
if "GET" in rule.methods and len(rule.arguments) == 0:
url = {
"loc": f"{host_base}{str(rule)}"
}
static_urls.append(url)
# Dynamic routes with dynamic content
dynamic_urls = list()
blog_posts = Post.objects(published=True)
for post in blog_posts:
url = {
"loc": f"{host_base}/blog/{post.category.name}/{post.url}",
"lastmod": post.date_published.strftime("%Y-%m-%dT%H:%M:%SZ")
}
dynamic_urls.append(url)
xml_sitemap = render_template("public/sitemap.xml", static_urls=static_urls, dynamic_urls=dynamic_urls, host_base=host_base)
response = make_response(xml_sitemap)
response.headers["Content-Type"] = "application/xml"
return response
<?xml version="1.0" encoding="UTF-8"?>
<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">
{% for url in static_urls %}
<url>
<loc>{{ url["loc"] }}</loc>
</url>
{% endfor %}
{% for url in dynamic_urls %}
<url>
<loc>{{ url["loc"] }}</loc>
<lastmod>{{ url["lastmod"] }}</lastmod>
</url>
{% endfor %}
</urlset>
@alexshchegretsov
Copy link

thank you!

@abdounasser202
Copy link

Thanks

@jakbin
Copy link

jakbin commented Dec 7, 2021

thanks

@TobiaScivoletto
Copy link

thank you :)

Copy link

ghost commented Sep 18, 2022

great!

@ant1fact
Copy link

Appreciate it

@mclarkex
Copy link

really helpful, thanks

@Pradip-p
Copy link

Thank you !!!

@therohitdas
Copy link

thanks !

@ccall48
Copy link

ccall48 commented Mar 31, 2025

thanks, this seems to be best solution so far.
I just made a slight change to hide private endpoints, 'startswith' can also take a tuple of values which makes adding multiple endpoints quite easy...

private_endpoints = ("/admin", "/user", "/foo", "/bar")

...

# Static routes with static content
static_urls = list()
for rule in app.url_map.iter_rules():
    if not str(rule).startswith(private_endpoints):
        if "GET" in rule.methods and len(rule.arguments) == 0:
            url = {
                "loc": f"{host_base}{str(rule)}"
            }
            static_urls.append(url)

@alexrkaufman
Copy link

Hi! I am trying to use this but I dont understand how to use or modify line 29. Where is the Post object coming from and how can I generalize this to other blueprints pr dynamic sections of a flask site?

    blog_posts = Post.objects(published=True)

@jakbin
Copy link

jakbin commented Jul 22, 2025

Hi! I am trying to use this but I dont understand how to use or modify line 29. Where is the Post object coming from and how can I generalize this to other blueprints pr dynamic sections of a flask site?

    blog_posts = Post.objects(published=True)

blog_posts = Post.objects(published=True) is just fetch dynamically posts from database. You can put your logic to fetch dynamically content. Ask from chatgpt for better help.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment