Flask Open Graph Meta Tags: Add OG Tags to Your Python App
Set og:title, og:image, og:description and Twitter card tags in Flask using Jinja2 templates, dynamic routes, and server-side OG image generation.
How OG tags work in Flask
Flask renders HTML server-side via Jinja2 templates. Open Graph tags go in the <head> section of your base layout. You pass OG values as template variables from your route handler — no special library required.
Step 1: Base layout with OG tag block
{# templates/base.html #}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>{{ og_title | default(site_name) }}</title>
{# Open Graph #}
<meta property="og:type" content="{{ og_type | default('website') }}" />
<meta property="og:title" content="{{ og_title | default(site_name) }}" />
<meta property="og:description" content="{{ og_description | default('') }}" />
<meta property="og:image" content="{{ og_image }}" />
<meta property="og:url" content="{{ og_url | default(request.url) }}" />
{# Twitter Card #}
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="{{ og_title | default(site_name) }}" />
<meta name="twitter:description" content="{{ og_description | default('') }}" />
<meta name="twitter:image" content="{{ og_image }}" />
</head>
<body>{% block content %}{% endblock %}</body>
</html>Step 2: Pass OG values from your route
# app.py
from flask import Flask, render_template, request
app = Flask(__name__)
@app.route('/post/<slug>')
def post(slug):
post = get_post_from_db(slug)
return render_template('post.html',
og_title=post.title,
og_description=post.excerpt,
og_image=f'https://yourdomain.com/og/{slug}.png',
og_url=request.url,
og_type='article',
)
@app.route('/og/<slug>.png')
def og_image(slug):
# Generate dynamic OG image with Pillow
from PIL import Image, ImageDraw, ImageFont
import io
post = get_post_from_db(slug)
img = Image.new('RGB', (1200, 630), color=(10, 10, 10))
draw = ImageDraw.Draw(img)
font = ImageFont.truetype('fonts/Inter-Bold.ttf', 56)
draw.text((60, 240), post.title, fill='white', font=font)
buf = io.BytesIO()
img.save(buf, format='PNG')
buf.seek(0)
from flask import send_file
return send_file(buf, mimetype='image/png',
max_age=3600)Using Flask-Meld or blueprints for larger apps
In larger Flask apps using blueprints, define a g.og object in a before-request hook and read it in your base template:
# In a blueprint before_request or app-level context processor
@app.context_processor
def inject_og_defaults():
return dict(
og_title='My App',
og_description='Default site description',
og_image='https://yourdomain.com/og/default.png',
og_url=request.url,
)
# Then in a specific route, just override the keys you need:
return render_template('post.html',
og_title=post.title,
og_description=post.excerpt,
og_image=f'https://yourdomain.com/og/{post.slug}.png',
)Common Flask OG mistakes
- Relative og:image URLs: Always use absolute URLs.
url_for('static', filename='og.png', _external=True)is the Flask-idiomatic way. - Missing og:type: Without it, scrapers may misclassify your content. Always set it explicitly.
- Jinja2 auto-escaping: Flask auto-escapes HTML in templates — if your titles contain
&they'll be double-escaped. Use the|efilter only when needed, not on values that are already escaped.
Verify your Flask OG tags
After deploying, paste your URL into OGFixer to preview exactly how Twitter, Slack, Discord, and LinkedIn will render your links.