Gambar OG di Ruby on Rails
Buat gambar OG di Ruby on Rails menggunakan API Dinamis berbasis langganan dari FastOG — dengan langganan API Dinamis (buat kunci API Anda di dasbor). Pratinjau di Penguji Gambar OG Gratis, lalu render gambar Open Graph dinamis dari aplikasi Rails Anda dengan URL yang ditandatangani HMAC.
Mulai Cepat
1. Buat layanan penanda tangan
# 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
Mengapa enc khusus? Ia melakukan penyandian persen dengan heksadesimal huruf besar, hanya menyisakan -._~ yang tidak dicadangkan — sehingga spasi menjadi %20, bukan +, sesuai dengan rawurlencode server. Jika Anda menggantinya dengan CGI.escape atau URI.encode_www_form_component, keduanya menghasilkan + untuk spasi: normalisasi dengan .gsub('+', '%20') terlebih dahulu.
2. Konfigurasi
# 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
Controller
# 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
Tampilan
<!-- 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>
Tata Letak
<!-- 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) %>" />
Contoh E-commerce
# 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
Contoh Posting 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
Titik Akhir 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 %>
Kesalahan Umum
Menandatangani Pesan yang Salah
❌ Salah: "GET/api/v1/og#{canonical}" — melewatkan pemisah baris baru.
✅ Benar: "GET/api/v1/og\n#{canonical}" — metode + jalur, baris baru, lalu query kanonik.
Mengabaikan key dari Tanda Tangan
❌ Salah: menandatangani hanya parameter konten Anda dan menambahkan key ke URL setelahnya.
✅ Benar: key digabungkan ke dalam parameter sebelum query kanonik dibuat, dan muncul di URL akhir.
401 missing_signature / invalid_signature
missing_signature (401) berarti key atau s tidak ada di URL. invalid_signature (401) berarti kanonik tidak cocok — pastikan baris baru dalam pesan yang ditandatangani, hmac_secret yang sama di kedua sisi, dan bahwa enc melakukan percent-encode spasi sebagai %20 (bukan +).
Membocorkan Rahasia HMAC
Jangan pernah mengekspos FASTOG_HMAC_SECRET dalam JavaScript frontend atau kode sisi klien.