Images OG dans Ruby on Rails
Générez des images OG dans Ruby on Rails avec l'API dynamique par abonnement de FastOG — avec un abonnement à l'API dynamique (créez votre clé API dans le tableau de bord). Prévisualisez dans le testeur d'images OG gratuit, puis générez des images Open Graph dynamiques depuis votre application Rails avec des URL signées HMAC.
Démarrage rapide
1. Créer le service de signature
# app/services/fastog_signer.rb
class QuickogSigner
def self.generate_signed_og_url(params, options = {})
base_url = options[:base_url] || ENV.fetch("FASTOG_BASE_URL", "https://fastog.com/api/v1/og")
api_key = options[:api_key] || ENV.fetch("FASTOG_API_KEY")
hmac_secret = options[:hmac_secret] || ENV.fetch("FASTOG_HMAC_SECRET")
raise "Missing FASTOG_API_KEY or FASTOG_HMAC_SECRET" unless api_key && hmac_secret
all = params.merge(key: api_key)
pairs = all.flat_map do |k, v|
Array(v).sort.map { |vv| "#{enc(k)}=#{enc(vv)}" }
end
canonical = pairs.sort.join("&")
signature = OpenSSL::HMAC.hexdigest("SHA256", hmac_secret, "GET/api/v1/og\n#{canonical}")
"#{base_url}?#{URI.encode_www_form(all.merge(s: signature))}"
end
def self.enc(s)
s.to_s.gsub(/[^A-Za-z0-9\-._~]/) { |c| c.bytes.map { |b| "%%%02X" % b }.join }
end
private_class_method :enc
end
Pourquoi un enc personnalisé ? Il effectue un encodage en pourcentage avec des hexadécimaux en majuscules, ne laissant que -._~ non réservé — ainsi les espaces deviennent %20, jamais +, ce qui correspond au rawurlencode du serveur. Si vous utilisez CGI.escape ou URI.encode_www_form_component à la place, les deux produisent + pour les espaces : normalisez avec .gsub('+', '%20') au préalable.
2. Configuration
# config/application.rb
config.fastog = ActiveSupport::OrderedOptions.new
config.fastog.api_key = ENV.fetch("FASTOG_API_KEY", nil)
config.fastog.hmac_secret = ENV.fetch("FASTOG_HMAC_SECRET", nil)
config.fastog.base_url = ENV.fetch("FASTOG_BASE_URL", "https://fastog.com/api/v1/og")
# .env
FASTOG_API_KEY=sk_your_full_api_key_here
FASTOG_HMAC_SECRET=your_64_char_hex_secret_here
Contrôleur
# app/controllers/blog_controller.rb
class BlogController < ApplicationController
def show
@post = Post.find_by!(slug: params[:slug])
@og_image_url = QuickogSigner.generate_signed_og_url(
template: "blog",
title: @post.title,
subtitle: @post.excerpt,
author: @post.author.name
)
end
end
Vue
<!-- app/views/blog/show.html.erb -->
<% content_for :head do %>
<meta property="og:image" content="<%= @og_image_url %>" />
<meta property="og:title" content="<%= @post.title %>" />
<meta property="og:description" content="<%= @post.excerpt %>" />
<% end %>
<article>
<h1><%= @post.title %></h1>
<p><%= @post.excerpt %></p>
</article>
Mise en page
<!-- app/views/layouts/application.html.erb -->
<head>
<%= yield :head %>
</head>
Helper
# app/helpers/fastog_helper.rb
module QuickogHelper
def og_image_url(template:, **params)
QuickogSigner.generate_signed_og_url({ template: template }.merge(params))
end
end
<!-- In view -->
<meta property="og:image" content="<%= og_image_url(template: 'blog', title: @post.title) %>" />
Exemple de commerce électronique
# app/controllers/products_controller.rb
class ProductsController < ApplicationController
def show
@product = Product.find(params[:id])
@og_image_url = QuickogSigner.generate_signed_og_url(
template: "ecommerce",
title: @product.name,
price: @product.formatted_price,
image: @product.image_url
)
end
end
Exemple d’article de blog
# app/controllers/users_controller.rb
class UsersController < ApplicationController
def show
@user = User.find_by!(username: params[:username])
@og_image_url = QuickogSigner.generate_signed_og_url(
template: "blog",
title: @user.name,
subtitle: @user.bio,
author: @user.name
)
end
end
Point de terminaison API
# config/routes.rb
get "api/og", to: "api/og#generate"
# app/controllers/api/og_controller.rb
module Api
class OgController < ApplicationController
skip_before_action :verify_authenticity_token
def generate
title = params[:title]
template = params[:template] || "default"
unless title
return render json: { error: "Missing title" }, status: :bad_request
end
og_image_url = QuickogSigner.generate_signed_og_url(
template: template,
title: title
)
render json: { ogImageUrl: og_image_url }
end
end
end
Turbo/Hotwire
<!-- app/views/blog/_post.html.erb -->
<%= turbo_frame_tag "post_#{@post.id}" do %>
<meta property="og:image" content="<%= og_image_url(template: 'blog', title: @post.title) %>" />
<h2><%= link_to @post.title, blog_path(@post) %></h2>
<% end %>
Pièges courants
Signature d’un mauvais message
❌ Erreur : "GET/api/v1/og#{canonical}" — le séparateur de saut de ligne est manquant.
✅ Correct : "GET/api/v1/og\n#{canonical}" — méthode + chemin, un saut de ligne, puis la requête canonique.
Omission de key dans la signature
❌ Erreur : signer uniquement vos paramètres de contenu et ajouter key à l’URL ensuite.
✅ Correct : key est fusionné dans les paramètres avant la construction de la requête canonique, et apparaît dans l’URL finale.
401 missing_signature / invalid_signature
missing_signature (401) signifie que key ou s est absent de l’URL. invalid_signature (401) signifie que la requête canonique ne correspond pas — vérifiez le saut de ligne dans le message signé, le même hmac_secret des deux côtés, et que enc encode en pourcentage les espaces comme %20 (jamais +).
Exposition du secret HMAC
N’exposez jamais FASTOG_HMAC_SECRET dans du JavaScript frontal ou du code côté client.